diff --git a/.github/ISSUE_TEMPLATE/bug-report-cn.yml b/.github/ISSUE_TEMPLATE/bug-report-cn.yml new file mode 100644 index 00000000..85c8e8c9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report-cn.yml @@ -0,0 +1,65 @@ +name: 🐛 TEgg Bug 反馈 +description: 如发现 TEgg 框架中的 Bug,请及时在此汇报。 +labels: [bug] +body: + - type: textarea + attributes: + label: | + 在此输入你需要反馈的 Bug 具体信息(Bug in Detail): + placeholder: | + 1. 我做了什么。 + + 2. 我的预期值。 + + 3. 我实际得到的结果。 + + 4. 可以的话,请提供一些截图、视频作为附件以复现症状。 + validations: + required: true + - type: textarea + attributes: + label: 可复现问题的仓库地址(Reproduction Repo) + description: | + 1. 请使用 `npm init egg --type=simple bug` 创建最小可复现问题的代码。 + + 2. 在 GitHub 中上传该代码项目,并在此处粘贴地址(你也可以直接将你的仓库压缩为 zip 文件直接以附件形式提交)。 + placeholder: | + https://github.com/YOUR_REPOSITORY_URL + validations: + required: true + - type: input + attributes: + label: Node 版本号: + description: | + 你的当前复现问题的 Node 版本号: + placeholder: | + 使用 “node -v” 命令,在控制台得到版本号(例如:v18.14.0)。 + validations: + required: true + - type: input + attributes: + label: TEgg 版本号: + description: | + 你的当前复现问题 TEgg 版本号: + placeholder: | + 请直接在“package.json”中查阅(例如:0.0.1)。 + validations: + required: true + - type: input + attributes: + label: "相关插件名称与版本号:" + description: | + 插件名称以及版本号: + placeholder: | + 请直接在“package.json”中查阅(例如:egg-mysql,3.1.1)。 + validations: + required: true + - type: input + attributes: + label: "操作平台与版本号:" + description: | + 你的操作平台与版本号: + placeholder: | + Windows 10 专业版(21H2) + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 00000000..56a55f4e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,65 @@ +name: 🐛 Bug Report For TEgg (in English) +description: Report an issue if something isn't working as expected 🤔. +labels: [bug] +body: + - type: textarea + attributes: + label: | + Your detail info about the Bug: + placeholder: | + 1. What I did. + + 2. What I expected to happen. + + 3. What I actually got. + + 4. If possible, images/videos as attachments are welcomed to show the bug. + validations: + required: true + - type: textarea + attributes: + label: Reproduction Repo + description: | + 1. Please use `npm init egg --type=simple bug` to create your smallest repo. + + 2. Submit it in the GitHub and paste your URL here (you can also attach your zip file directly). + placeholder: | + https://github.com/YOUR_REPOSITORY_URL or your zip file + validations: + required: true + - type: input + attributes: + label: Node Version + description: | + What's your Node's version? + placeholder: | + Use "node -v" in your console to get it (e.g: v18.14.0). + validations: + required: true + - type: input + attributes: + label: TEgg Version + description: | + What's your TEgg version? + placeholder: | + See it directly in your "package.json" file (e.g: 0.0.1) + validations: + required: true + - type: input + attributes: + label: Plugin Name and its version + description: | + What's your plugin's name and version? + placeholder: | + See them directly in your "package.json" file (e.g: egg-mysql, 3.1.1) + validations: + required: true + - type: input + attributes: + label: Platform and its version + description: | + What's your platform and its version? + placeholder: | + Windows 10 Professional(21H2) + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 4569b670..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: 'Bug report' -about: 'Report a bug to help us improve' -title: '' -labels: '' -assignees: '' - ---- - -## What happens? -A clear and concise description of what the bug is. - -## Mini Showcase Repository(REQUIRED) -> Provide a mini GitHub repository which can reproduce the issue. -> Use `npm init egg --type=simple bug` then upload to your GitHub - - - -## How To Reproduce - -**Steps to reproduce the behavior:** -1. -2. - -**Expected behavior** -1. -2. - -## Context -- **Node Version**: -- **Egg Version**: -- **Plugin Name**: -- **Plugin Version**: -- **Platform**: diff --git a/.github/ISSUE_TEMPLATE/bug_report_cn.md b/.github/ISSUE_TEMPLATE/bug_report_cn.md deleted file mode 100644 index 52a9c132..00000000 --- a/.github/ISSUE_TEMPLATE/bug_report_cn.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: '缺陷问题反馈' -about: '如何正确的提出一个 Issue :https://github.com/eggjs/egg/issues/3310' -title: '' -labels: '' -assignees: '' - ---- - - - -## What happens? - - -## 最小可复现仓库 -> 请使用 `npm init egg --type=simple bug` 创建,并上传到你的 GitHub 仓库 - - - -## 复现步骤,错误日志以及相关配置 - - - - - -## 相关环境信息 -- **操作系统**: -- **Node 版本**: -- **Egg 版本**: diff --git a/.github/ISSUE_TEMPLATE/feature-request-cn.yml b/.github/ISSUE_TEMPLATE/feature-request-cn.yml new file mode 100644 index 00000000..156edda3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request-cn.yml @@ -0,0 +1,21 @@ +name: 💡 我有一个新点子 +description: 我对 TEgg 框架有一个新的想法(或许我想来实现他)…… +labels: [feature request] +body: + - type: markdown + attributes: + value: | + 对于 TEgg 框架,你有一个新的想法? + 不过在提交你的新点子之前,麻烦请检阅一下是否之前的帖子中有类似重复的内容。 + - type: textarea + attributes: + label: 请详细告知你的新点子: + placeholder: | + 1. 您期望能够实现什么功能。 + + 2. 您的理由(如:我一直被什么问题困扰……)。 + 如果方便的话,请提供截屏或者视频等详细信息。 + + 3. 我能够做一些什么(最好是能提供一些伪代码帮助实现)。 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml new file mode 100644 index 00000000..2312247a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -0,0 +1,23 @@ +name: 💡 Feature Request For TEgg (in English) +description: I have a suggestion (and may want to implement it)! +labels: [feature request] +body: + - type: markdown + attributes: + value: | + You have an idea how to improve the TEgg? + + Before submitting, please have a look at the existing issues if there's already + something related to your suggestion. + - type: textarea + attributes: + label: "Enter your suggestions in details:" + placeholder: | + 1. What I expected to happen? + + 2. Your reason (e.g: I'm always frustrated with...). + If possible, images or videos are welcome. + + 3. What I plan to do (Optional but better in pseudo codes). + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index c2573aa0..00000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -name: 'Feature request' -about: 'Suggest an idea for this project' -title: '[Feature Request] say something' -labels: '' -assignees: '' - ---- - -## Background -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -## Proposal -Describe the solution you'd like, better to provide some pseudo code. - -## Additional context -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/rfc-cn.yml b/.github/ISSUE_TEMPLATE/rfc-cn.yml new file mode 100644 index 00000000..fe3e96e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc-cn.yml @@ -0,0 +1,27 @@ +name: 🚀 RFC 提案 +description: 我对 TEgg 框架技术架构功能层面上有重大新增、改进等。 +labels: [RFC proposal] +body: + - type: markdown + attributes: + value: | + 对于 TEgg 框架功能你是否有重大的新增或改进之类的想法? + + 不过在提交你的新想法或方案之前,麻烦请检阅一下是否之前的帖子中有类似重复的内容。 + - type: textarea + attributes: + label: 请详细告知你的新解决思路: + placeholder: | + 1. 描述你希望解决的问题的现状,附上相关的 issue 地址。 + 如果方便的话,请提供截屏或者视频等详细信息。 + + 2. 我能够做一些什么(譬如具体相关的的 API,描述思路,最好是能提供一些伪代码帮助实现)。 + validations: + required: true + - type: checkboxes + attributes: + label: "跟进类型:" + description: 此议案跟进类型情况: + options: + - label: 这是某个任务 + - label: 这是一个具体的 PR 的地址(URL) diff --git a/.github/ISSUE_TEMPLATE/rfc.yml b/.github/ISSUE_TEMPLATE/rfc.yml new file mode 100644 index 00000000..47c2d0ff --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc.yml @@ -0,0 +1,31 @@ +name: 🚀 RFC Proposals (in English) +description: I've got a major improvement (or idea) on the technical architecture of the TEgg framework. +labels: [RFC proposal] +body: + - type: markdown + attributes: + value: | + Any better new/changable functions for the core technical architecture of the TEgg framework? + + But please make sure there's no duplicated issues related to your idea before submitting. + - type: textarea + attributes: + label: "Please describe your idea in detail:" + placeholder: | + 1. Describe the current situation of the problem you want to solve, + and attach the related issue address. + + If it is possible, please provide screenshots or videos in detail. + + 2. What can I do (related APIs, Your ideas, better to provide some pseudo code to help implementations). + validations: + required: true + - type: checkboxes + attributes: + label: Follow-up type + description: The type of the RFC proposals. + options: + - label: Some Task + - label: PR URL(s) + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/rfc_cn.md b/.github/ISSUE_TEMPLATE/rfc_cn.md deleted file mode 100644 index 5bc2e8c7..00000000 --- a/.github/ISSUE_TEMPLATE/rfc_cn.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: 'RFC Proposals' -about: 'Provide a solution for this project' -title: '[RFC] say something' -labels: 'type: proposals' -assignees: '' - ---- - - - -## 背景 - -> 描述你希望解决的问题的现状,附上相关的 issue 地址 - -## 思路 - -> 描述大概的解决思路,可以包含 API 设计和伪代码等 - -## 跟进 - -- [ ] some task -- [ ] PR URL diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index b79162ab..fe6a556c 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -4,8 +4,6 @@ on: branches: [ master ] pull_request: branches: [ master ] - schedule: - - cron: '0 2 * * *' jobs: Runner-ubuntu: runs-on: ubuntu-latest @@ -22,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [ 14, 16, 18 ] + node-version: [ 14, 16, 18, 20 ] steps: - name: Checkout Git Source uses: actions/checkout@master @@ -33,13 +31,15 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install Npm - run: npm i -g npm@8 + run: npm i -g npm@9 - name: Install Dependencies run: npm i - name: Continuous integration - run: npm run ci + run: | + npm run ci + npm run tsc:pub - name: Code Coverage uses: codecov/codecov-action@v1 @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [ 14, 16, 18 ] + node-version: [ 14, 16, 18, 20 ] steps: - name: Checkout Git Source uses: actions/checkout@master @@ -68,7 +68,7 @@ jobs: run: /usr/local/opt/mysql@5.7/bin/mysql.server start - name: Install Npm - run: npm i -g npm@8 + run: npm i -g npm@9 - name: Install Dependencies run: npm i @@ -86,7 +86,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [ 14, 16, 18 ] + node-version: [ 14, 16, 18, 20 ] steps: - name: Checkout Git Source uses: actions/checkout@master diff --git a/.gitignore b/.gitignore index 322dfc85..0eacba65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/node_modules +node_modules coverage *.log npm-debug.log @@ -23,10 +23,7 @@ dist **/*.js **/*.d.ts core/**/dist -core/**/node_modules plugin/**/dist -plugin/**/node_modules -standalone/**/node_modules/ plugin/oneapi/test/fixtures/modules/*/oneapi plugin/dal/test/fixtures/modules/*/dal/ @@ -34,10 +31,12 @@ plugin/dal/test/fixtures/modules/*/dal/ !core/common-util/test/fixtures/**/node_modules !core/common-util/test/fixtures/**/node_modules/**/*.js !plugin/*/test/fixtures/**/*.js -!**/.autod.conf.js !plugin/*/typings/*.d.ts !plugin/*/test/fixtures/apps/*/config/*.js !plugin/*/test/fixtures/apps/**/typings/*.d.ts !core/eventbus-decorator/src/type.d.ts !plugin/orm/test/fixtures/prepare.js !benchmark/**/*.js +plugin/tegg/test/fixtures/apps/**/*.js +!standalone/standalone/test/fixtures/**/node_modules +!standalone/standalone/test/fixtures/**/node_modules/**/*.js diff --git a/.mocharc-integration.yml b/.mocharc-integration.yml deleted file mode 100644 index 17532e9e..00000000 --- a/.mocharc-integration.yml +++ /dev/null @@ -1,12 +0,0 @@ -timeout: "120000" -spec: - - integration/*/test/**/*.test.ts -recursive: true -extension: - - ts -require: - - intelli-espower-loader - - source-map-support/register - - espower-typescript/guess -full-trace: true -exit: true diff --git a/.mocharc.yml b/.mocharc.yml index c631c24a..4479918b 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -1,14 +1,11 @@ timeout: "120000" spec: - - core/*/test/**/*.test.ts - - plugin/*/test/**/*.test.ts - - standalone/*/test/**/*.test.ts + - test/**/*.test.ts recursive: true extension: - ts require: - - intelli-espower-loader + - ts-node/register - source-map-support/register - - espower-typescript/guess full-trace: true exit: true diff --git a/.nycrc.yml b/.nycrc.yml index d873f0e8..11c12f8c 100644 --- a/.nycrc.yml +++ b/.nycrc.yml @@ -10,17 +10,16 @@ reporter: - json-summary - json - lcov -lines: 80 -statements: 80 +lines: 0 +statements: 0 exclude: - - core/*/.autod.conf.js - core/*/typings - core/*/dist - - plugin/*/.autod.conf.js - plugin/*/typings - plugin/*/dist - coverage - core/*/test/**/* - plugin/*/test/**/* - - integration/*/test/**/* - standalone/*/test/**/* + - benchmark/**/* + - core/test-util/**/* diff --git a/CHANGELOG.md b/CHANGELOG.md index a500a7fa..e57c541d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,534 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + + +### Bug Fixes + +* typo `acL` to `acl` ([#156](https://github.com/eggjs/tegg/issues/156)) ([a775d34](https://github.com/eggjs/tegg/commit/a775d34d38c481c5f9e90504224553d31ad728d3)) + + +### Features + +* add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + + +### Features + +* support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + + +### Features + +* add LoadUnitMultiInstanceProtoHook for tegg plugin ([#150](https://github.com/eggjs/tegg/issues/150)) ([b938580](https://github.com/eggjs/tegg/commit/b9385803383dceedfc26bd990e5d752cd33f0f67)) + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + + +### Bug Fixes + +* fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + + +### Features + +* add aop runtime/dynamic inject runtime for standalone ([#149](https://github.com/eggjs/tegg/issues/149)) ([6091fc6](https://github.com/eggjs/tegg/commit/6091fc6be885976d72a6920d37ec685927b63d5d)) +* add helper to get EggObject from class ([#148](https://github.com/eggjs/tegg/issues/148)) ([77eaf38](https://github.com/eggjs/tegg/commit/77eaf38383ad974b30d13f4c30c489fb7fa7274d)) + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + + +### Features + +* export EggModuleLoader in standalone ([#146](https://github.com/eggjs/tegg/issues/146)) ([9d1da9a](https://github.com/eggjs/tegg/commit/9d1da9a87dbd486930adc50cd43020c2fb478230)) +* implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + + +### Bug Fixes + +* fix aop plugin files ([#140](https://github.com/eggjs/tegg/issues/140)) ([f47eef6](https://github.com/eggjs/tegg/commit/f47eef634efd442ac5a8f68567e36c940247e48b)) + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + + +### Bug Fixes + +* init all context advice if root proto miss ([#139](https://github.com/eggjs/tegg/issues/139)) ([0602ea8](https://github.com/eggjs/tegg/commit/0602ea81578bf717ee4b4c490ace8c1c133478c5)) + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + + +### Bug Fixes + +* ensure ContextInitiator be called after ctx ready ([#138](https://github.com/eggjs/tegg/issues/138)) ([79e16da](https://github.com/eggjs/tegg/commit/79e16dae913b6114ac8d13bde8de60164d57dab3)) + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + + +### Features + +* impl ModuleConfigs for standalone ([#136](https://github.com/eggjs/tegg/issues/136)) ([7227492](https://github.com/eggjs/tegg/commit/7227492295b9c84e3660bfc006ca96e7a9652a25)) + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + + +### Features + +* 事务注解增加数据源选项 ([#135](https://github.com/eggjs/tegg/issues/135)) ([c33b3b5](https://github.com/eggjs/tegg/commit/c33b3b5ec9d32a8c6675d986013042f0cb8e4370)) + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + + +### Bug Fixes + +* after call mockModuleContext, hasMockModuleContext should be true ([#134](https://github.com/eggjs/tegg/issues/134)) ([88b3caa](https://github.com/eggjs/tegg/commit/88b3caadd24f08221b8098c42733e26376338cae)) + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + + +### Bug Fixes + +* export StandaloneInnerObject ([#131](https://github.com/eggjs/tegg/issues/131)) ([e4b87e0](https://github.com/eggjs/tegg/commit/e4b87e0a48e3232adaf43bad75f44d0ae775c984)) + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + + +### Features + +* export transaction decorator from tegg ([8be0521](https://github.com/eggjs/tegg/commit/8be05212b62fe7f111688efaec935be64d623918)) +* impl transaction decorator ([#124](https://github.com/eggjs/tegg/issues/124)) ([4896615](https://github.com/eggjs/tegg/commit/4896615af951bbff940cda7abc116df40ed486e5)) + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + + +### Bug Fixes + +* use posix join for package path ([#127](https://github.com/eggjs/tegg/issues/127)) ([53672f4](https://github.com/eggjs/tegg/commit/53672f404edb72c7330e125f72dd356cde0607ad)) + + +### Features + +* standalone Runner run support ctx ([#126](https://github.com/eggjs/tegg/issues/126)) ([0788c7d](https://github.com/eggjs/tegg/commit/0788c7dfb57f96c55e94cc6692c0b6e9ac1ee03c)) + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + + +### Features + +* implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + + +### Bug Fixes + +* don't check eventbus plugin name ([#113](https://github.com/eggjs/tegg/issues/113)) ([2a94a57](https://github.com/eggjs/tegg/commit/2a94a57c58e4fd971400966c15597aace4bb1ecc)) + + +### Features + +* The exposed module reads the options. ([#112](https://github.com/eggjs/tegg/issues/112)) ([a52b44b](https://github.com/eggjs/tegg/commit/a52b44b753463bfdef6fbbc39f920be8eccf1567)) + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + + +### Bug Fixes + +* fix contextEggObjectGetProperty conflict ([#105](https://github.com/eggjs/tegg/issues/105)) ([c570315](https://github.com/eggjs/tegg/commit/c570315ece6ef7443ecf3df2b45aa8c934a5aa38)) + + + + + +## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) + + +### Bug Fixes + +* should not cache ctx object ([#103](https://github.com/eggjs/tegg/issues/103)) ([be54083](https://github.com/eggjs/tegg/commit/be5408375261d98b60fbc97e18de9232581a9547)) + + + + + +## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) + + +### Bug Fixes + +* get app/ctx properties when load unit beforeCreate ([#102](https://github.com/eggjs/tegg/issues/102)) ([76ef679](https://github.com/eggjs/tegg/commit/76ef679d745deb235db9dcc3fa34984b511bd5c6)) + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + + +### Bug Fixes + +* egg qualifier should register after all file loaded ([#100](https://github.com/eggjs/tegg/issues/100)) ([5033b51](https://github.com/eggjs/tegg/commit/5033b51796b8a3329bd79884a8d8f18226193a1b)) + + +### Features + +* add backgroundTask.timeout config ([#101](https://github.com/eggjs/tegg/issues/101)) ([0b1eee0](https://github.com/eggjs/tegg/commit/0b1eee00d6feb9c6d4509023dffe85c0ada749c2)) + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + + +### Bug Fixes + +* eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) +* not create ctx logger proto ([#97](https://github.com/eggjs/tegg/issues/97)) ([100886b](https://github.com/eggjs/tegg/commit/100886ba90bdc7cccd07fa2f390defb5b0c53e22)) + + + + + +## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) + + +### Bug Fixes + +* remove useless init singleton proto ([#96](https://github.com/eggjs/tegg/issues/96)) ([097ac58](https://github.com/eggjs/tegg/commit/097ac58c675d43088c8785a12cf224b5d6adea17)) + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + + +### Bug Fixes + +* loader should not deps metadata ([#94](https://github.com/eggjs/tegg/issues/94)) ([ff57de4](https://github.com/eggjs/tegg/commit/ff57de4f3e0d0dc33d77d05a887242fcb4c32024)) + + +### Features + +* append call stack for runInBackground ([#91](https://github.com/eggjs/tegg/issues/91)) ([ec7bc2c](https://github.com/eggjs/tegg/commit/ec7bc2c60ffb49b4a51feec82e391b1f6a88549a)) +* remove context egg object factory ([#93](https://github.com/eggjs/tegg/issues/93)) ([e14bdb2](https://github.com/eggjs/tegg/commit/e14bdb257eaebc0b0a4c37c6073a5c3237718718)) +* use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + + +### Bug Fixes + +* BackgroundTaskHelper should support recursively call ([#90](https://github.com/eggjs/tegg/issues/90)) ([368ac03](https://github.com/eggjs/tegg/commit/368ac0343d0d4e96b3768e7fd169b721551d0e4b)) + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + + +### Features + +* use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) + + + + + +## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) + + +### Bug Fixes + +* should not notify backgroundTaskHelper if teggContext not exists ([#88](https://github.com/eggjs/tegg/issues/88)) ([4cab68b](https://github.com/eggjs/tegg/commit/4cab68bfc08a3786bde9a67cd8687f152829d9a0)) + + + + + +## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) + + +### Bug Fixes + +* wait egg background task done before destroy tegg ctx ([#87](https://github.com/eggjs/tegg/issues/87)) ([deea4d8](https://github.com/eggjs/tegg/commit/deea4d8d75c43347c6ee09e0e97f5fa80dd68dd9)) + + + + + +## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) + + +### Bug Fixes + +* beginModuleScope should be reentrant ([#86](https://github.com/eggjs/tegg/issues/86)) ([648aeaf](https://github.com/eggjs/tegg/commit/648aeaf1f20ff5bc217bf6f16fac9d9181eb8447)) + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + + +### Bug Fixes + +* inject property should be configurable ([#85](https://github.com/eggjs/tegg/issues/85)) ([c13ab55](https://github.com/eggjs/tegg/commit/c13ab55d7b483a5c4a6e4293a6095aa98d070a8b)) + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + + +### Bug Fixes + +* router type ([#83](https://github.com/eggjs/tegg/issues/83)) ([b32d9b8](https://github.com/eggjs/tegg/commit/b32d9b8e94552d27dc0249c9f38e7223b24beff0)) + + +### Features + +* add app.eggContextHandler ([#84](https://github.com/eggjs/tegg/issues/84)) ([2772624](https://github.com/eggjs/tegg/commit/277262418143956b2e75bd1db5f2e7dd9b75eb8b)) +* export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + + +### Bug Fixes + +* cork/uncork should can be called multi times in same ctx ([#78](https://github.com/eggjs/tegg/issues/78)) ([269cda6](https://github.com/eggjs/tegg/commit/269cda6327122111c230e6f69abb525ce4ab5be1)) + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package tegg + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + + +### Bug Fixes + +* fix nest inject ctx obj to singleton obj ([#74](https://github.com/eggjs/tegg/issues/74)) ([e4b6252](https://github.com/eggjs/tegg/commit/e4b6252aa79925e16185e568bf7b220f367253ab)) + + + + + +# [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) + + +### Features + +* impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* add 'globby' as dependencies ([#71](https://github.com/eggjs/tegg/issues/71)) ([76d85d9](https://github.com/eggjs/tegg/commit/76d85d9948527028f926ae0ff5a61111eb1cbd04)) +* eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) +* fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) +* fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) +* fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) +* fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) +* fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) +* fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) +* fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) +* inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) +* none exist node_modules path ([#59](https://github.com/eggjs/tegg/issues/59)) ([77cb068](https://github.com/eggjs/tegg/commit/77cb0687ba8e5d9f20a6df0548de9d55a8771c21)) +* optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) +* skip file not exits ([#62](https://github.com/eggjs/tegg/issues/62)) ([10e56d4](https://github.com/eggjs/tegg/commit/10e56d418f359efa5d8909541768082cf068d2a4)) +* use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) +* use require.resolve instead path.join to resolve dependencies path ([#63](https://github.com/eggjs/tegg/issues/63)) ([d7f3beb](https://github.com/eggjs/tegg/commit/d7f3beb27a22b95bb54589c5988a68ce2484c089)) + + +### Features + +* add new module scan mode ([#58](https://github.com/eggjs/tegg/issues/58)) ([3be6c20](https://github.com/eggjs/tegg/commit/3be6c2047a0241a482aafd0aaa072f51f861b6ea)) +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) +* impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) +* middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) +* multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) +* standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) +* support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9ecdbd2](https://github.com/eggjs/tegg/commit/9ecdbd2fe434445c698cd2140ae97f76b6bb6ddf)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + + +### Features + +* delete controller root hook ([bbb68f4](https://github.com/eggjs/tegg/commit/bbb68f43a1a9fcfd86c05581b10c56eeb77d4053)) + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) +* fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) +* fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) +* fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) +* fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) +* fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) +* fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) +* fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) +* none exist node_modules path ([#59](https://github.com/eggjs/tegg/issues/59)) ([77cb068](https://github.com/eggjs/tegg/commit/77cb0687ba8e5d9f20a6df0548de9d55a8771c21)) +* optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) +* skip file not exits ([#62](https://github.com/eggjs/tegg/issues/62)) ([10e56d4](https://github.com/eggjs/tegg/commit/10e56d418f359efa5d8909541768082cf068d2a4)) +* use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) +* use require.resolve instead path.join to resolve dependencies path ([#63](https://github.com/eggjs/tegg/issues/63)) ([d7f3beb](https://github.com/eggjs/tegg/commit/d7f3beb27a22b95bb54589c5988a68ce2484c089)) + + +### Features + +* add new module scan mode ([#58](https://github.com/eggjs/tegg/issues/58)) ([3be6c20](https://github.com/eggjs/tegg/commit/3be6c2047a0241a482aafd0aaa072f51f861b6ea)) +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) +* impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) +* middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) +* multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) +* standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) +* support leoric hooks ([#41](https://github.com/eggjs/tegg/issues/41)) ([9ecdbd2](https://github.com/eggjs/tegg/commit/9ecdbd2fe434445c698cd2140ae97f76b6bb6ddf)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) diff --git a/README.md b/README.md index a684c4c2..5c0604d9 100644 --- a/README.md +++ b/README.md @@ -19,21 +19,33 @@ exports.tegg = { }; ``` +```js +// config/config.default.js +{ + tegg: { + // 读取模块支持自定义配置,可用于扩展或过滤不需要的模块文件 + readModuleOptions: { + extraFilePattern: ['!**/dist', '!**/release'], + }, + }; +} +``` + ## Usage ### 原型 + module 中的对象基本信息,提供了 - 实例化方式:每个请求实例化/全局单例/每次注入都实例化 - 访问级别:module 外是否可访问 - - #### ContextProto -每次请求都会实例化一个 ContextProto,并且只会实例化一次 +每次请求都会实例化一个 ContextProto,并且只会实例化一次 ##### 定义 + ```typescript @ContextProto(params: { // 原型的实例化名称 @@ -52,8 +64,11 @@ module 中的对象基本信息,提供了 accessLevel?: AccessLevel; }) ``` + ##### 示例 + ###### 简单示例 + ```typescript import { ContextProto } from '@eggjs/tegg'; @@ -65,7 +80,9 @@ export class HelloService { } ``` + ###### 复杂示例 + ```typescript import { ContextProto, AccessLevel } from '@eggjs/tegg'; @@ -80,11 +97,13 @@ export default class HelloService { } } ``` + #### SingletonProto -整个应用声明周期只会实例化一个 SingletonProto +整个应用声明周期只会实例化一个 SingletonProto ##### 定义 + ```typescript @SingletonProto(params: { // 原型的实例化名称 @@ -103,8 +122,11 @@ export default class HelloService { accessLevel?: AccessLevel; }) ``` + ##### 示例 + ###### 简单示例 + ```typescript import { SingletonProto } from '@eggjs/tegg'; @@ -116,7 +138,9 @@ export class HelloService { } ``` + ###### 复杂示例 + ```typescript import { SingletonProto, AccessLevel } from '@eggjs/tegg'; @@ -131,11 +155,179 @@ export class HelloService { } } ``` + +#### MultiInstanceProto + +支持一个类有多个实例。比如说 logger 可能会初始化多个,用来输出到不通文件,db 可能会初始化多个,用来连接不同的数据库。 +使用这个注解可以方便的对接外部资源。 + +##### 定义 +```ts +// 静态定义 +@MultiInstanceProto(params: { + // 对象的生命周期 + // CONTEXT: 每个上下文都有一个实例 + // SINGLETON: 整个应用生命周期只有一个实例 + initType?: ObjectInitTypeLike; + + // 对象是在 module 内可访问还是全局可访问 + // PRIVATE: 仅 module 内可访问 + // PUBLIC: 全局可访问 + // 默认值为 PRIVATE + accessLevel?: AccessLevel; + + // 高阶参数,指定类型的实现原型 + protoImplType?: string; + + // 对象元信息 + objects: ObjectInfo[]; +}) + +// 动态定义 +@MultiInstanceProto(params: { + // 对象的生命周期 + // CONTEXT: 每个上下文都有一个实例 + // SINGLETON: 整个应用生命周期只有一个实例 + initType?: ObjectInitTypeLike; + + // 对象是在 module 内可访问还是全局可访问 + // PRIVATE: 仅 module 内可访问 + // PUBLIC: 全局可访问 + // 默认值为 PRIVATE + accessLevel?: AccessLevel; + + // 高阶参数,指定类型的实现原型 + protoImplType?: string; + + // 动态调用,获取对象元信息 + // 仅会调用一次,调用后结果会被缓存 + getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[]; +}) +``` + +##### 示例 + +首先定义一个自定义 Qualifier 注解 +```ts +import { + QualifierUtil, + EggProtoImplClass, +} from '@eggjs/tegg'; + +export const LOG_PATH_ATTRIBUTE = Symbol.for('LOG_PATH_ATTRIBUTE'); + +export function LogPath(name: string) { + return function(target: any, propertyKey: PropertyKey) { + QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, LOG_PATH_ATTRIBUTE, name); + }; +} + +``` + +定一个具体实现 + +```typescript +import { + MultiInstanceProto, + MultiInstancePrototypeGetObjectsContext, + LifecycleInit, + LifecycleDestroy, + QualifierUtil, + EggProtoImplClass, +} from '@eggjs/tegg'; +import { EggObject, ModuleConfigUtil, EggObjectLifeCycleContext } from '@eggjs/tegg/helper'; +import fs from 'node:fs'; +import { Writable } from 'node:stream'; +import path from 'node:path'; +import { EOL } from 'node:os'; + +export const LOG_PATH_ATTRIBUTE = Symbol.for('LOG_PATH_ATTRIBUTE'); + +@MultiInstanceProto({ + // 从 module.yml 中动态获取配置来决定需要初始化几个对象 + getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { + const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath); + return (config as any).features.logger.map(name => { + return { + name: 'dynamicLogger', + qualifiers: [{ + attribute: LOG_PATH_ATTRIBUTE, + value: name, + }], + } + }); + }, +}) +export class DynamicLogger { + stream: Writable; + loggerName: string; + + @LifecycleInit() + async init(ctx: EggObjectLifeCycleContext, obj: EggObject) { + // 获取需要实例化对象的 Qualifieri + const loggerName = obj.proto.getQualifier(LOG_PATH_ATTRIBUTE); + this.loggerName = loggerName as string; + this.stream = fs.createWriteStream(path.join(ctx.loadUnit.unitPath, `${loggerName}.log`)); + } + + @LifecycleDestroy() + async destroy() { + return new Promise((resolve, reject) => { + this.stream.end(err => { + if (err) { + return reject(err); + } + return resolve(); + }); + }); + } + + info(msg: string) { + return new Promise((resolve, reject) => { + this.stream.write(msg + EOL,err => { + if (err) { + return reject(err); + } + return resolve(); + }); + }); + } + +} +``` + +使用 DynamicLogger. +```ts +@SingletonProto() +export class Foo { + @Inject({ + name: 'dynamicLogger', + }) + // 通过自定义注解来指定 Qualifier + @LogPath('foo') + fooDynamicLogger: DynamicLogger; + + @Inject({ + name: 'dynamicLogger', + }) + @LogPath('bar') + barDynamicLogger: DynamicLogger; + + async hello(): Promise { + await this.fooDynamicLogger.info('hello, foo'); + await this.barDynamicLogger.info('hello, bar'); + } +} +``` + + + #### 生命周期 hook -由于对象的生命周期交给了容器来托管,代码中无法感知什么时候对象初始化,什么时候依赖被注入了。所以提供了生命周期 hook 来实现这些通知的功能。 +由于对象的生命周期交给了容器来托管,代码中无法感知什么时候对象初始化,什么时候依赖被注入了。所以提供了生命周期 hook 来实现这些通知的功能。 ##### 定义 + ```typescript /** * lifecycle hook interface for egg object @@ -172,7 +364,9 @@ interface EggObjectLifecycle { destroy?(): Promise; } ``` + ##### 示例 + ```typescript import { EggObjectLifecycle } from '@eggjs/tegg'; @@ -208,12 +402,68 @@ export class Foo implements EggObjectLifecycle { } ``` +##### 生命周期方法装饰器 + +上面展示的 hook 是通过方法命名约定来实现生命周期 hook,我们还提供了更加可读性更强的装饰器模式。 + +```ts +import { + LifecyclePostConstruct, + LifecyclePreInject, + LifecyclePostInject, + LifecycleInit, + LifecyclePreDestroy, + LifecycleDestroy, +} from '@eggjs/tegg'; + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, + name: 'helloInterface', +}) +export class HelloService { + @LifecyclePostConstruct() + protected async _postConstruct() { + console.log('对象构造完成'); + } + + @LifecyclePreInject() + protected async _preInject() { + console.log('依赖将要注入'); + } + + @LifecyclePostInject() + protected async _postInject() { + console.log('依赖注入完成'); + } + + @LifecycleInit() + protected async _init() { + console.log('执行一些异步的初始化过程'); + } + + @LifecyclePreDestroy() + protected async _preDestroy() { + console.log('对象将要释放了'); + } + + @LifecycleDestroy() + protected async _destroy() { + console.log('执行一些释放资源的操作'); + } + + async hello(user: User) { + const echoResponse = await this.echoAdapter.echo({ name: user.name }); + return `hello, ${echoResponse.name}`; + } +} +``` ### 注入 -Proto 中可以依赖其他的 Proto,或者 egg 中的对象。 +Proto 中可以依赖其他的 Proto,或者 egg 中的对象。 #### 定义 + ```typescript @Inject(param?: { // 注入对象的名称,在某些情况下一个原型可能有多个实例 @@ -226,8 +476,11 @@ Proto 中可以依赖其他的 Proto,或者 egg 中的对象。 proto?: string; }) ``` + #### 示例 + ##### 简单示例 + ```typescript import { EggLogger } from 'egg'; import { Inject } from '@eggjs/tegg'; @@ -244,7 +497,9 @@ export class HelloService { } } ``` + ##### 复杂示例 + ```typescript import { EggLogger } from 'egg'; import { Inject } from '@eggjs/tegg'; @@ -264,6 +519,7 @@ export class HelloService { } } ``` + #### 限制 - ContextProto 可以注入任何 Proto 但是 SingletonProto 不能注入 ContextProto @@ -272,22 +528,24 @@ export class HelloService { - 一个 module 内不能有实例化方式和名称同时相同的原型 - **不可以注入 ctx/app,用什么注入什么** - - #### 兼容 egg -egg 中有 extend 方式,可以将对象扩展到 Context/Application 上,这些对象均可注入。Context 上的对象类比为 ContextProto,Application 上的对象类比为 SingletonProto。 +egg 中有 extend 方式,可以将对象扩展到 Context/Application 上,这些对象均可注入。Context 上的对象类比为 ContextProto,Application 上的对象类比为 SingletonProto。 #### 进阶 + ### module 内原型名称冲突 -一个 module 内,有两个原型,原型名相同,实例化不同,这时直接 Inject 是不行的,module 无法理解具体需要哪个对象。这时就需要告知 module 需要注入的对象实例化方式是哪种。 +一个 module 内,有两个原型,原型名相同,实例化不同,这时直接 Inject 是不行的,module 无法理解具体需要哪个对象。这时就需要告知 module 需要注入的对象实例化方式是哪种。 ###### 定义 + ```typescript @InitTypeQualifier(initType: ObjectInitType) ``` + ###### 示例 + ```typescript import { EggLogger } from 'egg'; import { Inject, InitTypeQualifier, ObjectInitType } from '@eggjs/tegg'; @@ -295,20 +553,24 @@ import { Inject, InitTypeQualifier, ObjectInitType } from '@eggjs/tegg'; @ContextProto() export class HelloService { @Inject() - // 明确指定示例化方式为 CONTEXT 的 logger + // 明确指定实例化方式为 CONTEXT 的 logger @InitTypeQualifier(ObjectInitType.CONTEXT) logger: EggLogger; } ``` + ##### module 间原型名称冲突 -可能多个 module 都实现了名称为 `HelloAdapter` 的原型, 且 `accessLevel = AccessLevel.PUBLIC`,需要明确的告知 module 需要注入的原型来自哪个 module. +可能多个 module 都实现了名称为 `HelloAdapter` 的原型, 且 `accessLevel = AccessLevel.PUBLIC`,需要明确的告知 module 需要注入的原型来自哪个 module. ###### 定义 + ```typescript @ModuleQualifier(moduleName: string) ``` + ###### 示例 + ```typescript import { EggLogger } from 'egg'; import { Inject, InitTypeQualifier, ObjectInitType } from '@eggjs/tegg'; @@ -322,27 +584,48 @@ export class HelloService { } ``` +### egg 内 ctx/app 命名冲突 + +egg 内可能出现 ctx 和 app 上有同名对象的存在,我们可以通过使用 `EggQualifier` 来明确指定注入的对象来自 ctx 还是 app。不指定时,默认注入 app 上的对象。 + +###### 定义 + +```typescript +@EggQualifier(eggType: EggType) +``` + +###### 示例 + +```typescript +import { EggLogger } from 'egg'; +import { Inject, EggQualifier, EggType } from '@eggjs/tegg'; + +@ContextProto() +export class HelloService { + @Inject() + // 明确指定注入 ctx 上的 foo 而不是 app 上的 foo + @EggQualifier(EggType.CONTEXT) + foo: Foo; +} +``` + ### 单测 #### 单测 Context -在单测中需要获取 egg Context 时,可以使用以下 API。 +在单测中需要获取 egg Context 时,可以使用以下 API。 ```typescript export interface Application { /** - * 创建 module 上下文 + * 创建 module 上下文 scope */ - mockModuleContext(data?: any): Promise ; - /** - * 销毁 module 上下文 - */ - destroyModuleContext(context: Context): Promise ; + mockModuleContextScope(this: MockApplication, fn: (ctx: Context) => Promise, data?: any): Promise; } ``` - #### 获取对象实例 + 在单测中需要获取 module 中的对象实例时,可以使用以下 API。 ```typescript @@ -367,10 +650,9 @@ export interface Context { 目前 module 尚未实现所有 egg 的特性,如果需要使用 egg 的功能,可以通过一些方式来兼容。 - ##### Schedule -目前 Schedule 尚未实现注解,仍然需要使用 egg 的目录和继承方式,在这种场景下如果需要使用 module 的实现,需要使用 `ctx.beginModuleScope`。举个例子: +目前 Schedule 尚未实现注解,仍然需要使用 egg 的目录和继承方式,在这种场景下如果需要使用 module 的实现,需要使用 `ctx.beginModuleScope`。举个例子: ```typescript // notify/EC_FOO.js @@ -395,10 +677,9 @@ module.exports = Subscription; ``` - #### 注入 egg 对象 -module 会自动去遍历 egg 的 Application 和 Context 对象,获取其所有的属性,所有的属性都可以进行无缝的注入。举个例子,如何注入现在的 egg proxy: +module 会自动去遍历 egg 的 Application 和 Context 对象,获取其所有的属性,所有的属性都可以进行无缝的注入。举个例子,如何注入现在的 egg proxy: ```typescript import { IProxy } from 'egg' @@ -414,15 +695,14 @@ class FooService { } ``` - ##### 注入 logger -专为 logger 做了优化,可以直接注入 custom logger。 +专为 logger 做了优化,可以直接注入 custom logger。 举个例子: - 有一个自定义的 fooLogger + ```javascript // config.default.js exports.customLogger = { @@ -432,8 +712,8 @@ exports.customLogger = { }; ``` - 代码中可以直接注入: + ```typescript import { EggLogger } from 'egg'; @@ -443,12 +723,12 @@ class FooService { } ``` - #### 注入 egg 方法 由于 module 注入时,只可以注入对象,不能注入方法,如果需要使用现有 egg 的方法,就需要对方法进行一定的封装。 举个例子:假设 context 上有一个方法是 `getHeader`,在 module 中需要使用这个方法需要进行封装。 + ```typescript // extend/context.ts @@ -460,8 +740,8 @@ export default { ``` - 先将方法封装成一个对象。 + ```typescript // HeaderHelper.ts @@ -477,6 +757,7 @@ class HeaderHelper { ``` 再将对象放到 Context 扩展上即可。 + ```typescript // extend/context.ts @@ -498,6 +779,7 @@ export default { 在 module 中,每个对象实例都有自己的生命周期,开发者可以对每个对象进行细致的控制。只要为对象实现 module 定义好的接口即可。所有生命周期 hook 均为可选方法,不需要的可以不实现。 #### 接口定义 + ```typescript interface EggObjectLifecycle { /** @@ -532,8 +814,8 @@ interface EggObjectLifecycle { } ``` - #### 实现 + ```typescript import { EggObjectLifecycle } from '@eggjs/tegg'; @@ -551,16 +833,17 @@ class FooService implement EggObjectLifecycle { ``` ### 异步任务 -module 在请求结束后会把请求相关的对象释放,所以在请求中使用 `process.nextTick`、 `setTimeout`、 `setInterval`这类接口并不安全,可能导致一些错误。因此需要使用框架提供的接口,以便框架了解当前请求有哪些异步任务在执行,等异步任务执行完成后再释放对象。 +module 在请求结束后会把请求相关的对象释放,所以在请求中使用 `process.nextTick`、 `setTimeout`、 `setInterval`这类接口并不安全,可能导致一些错误。因此需要使用框架提供的接口,以便框架了解当前请求有哪些异步任务在执行,等异步任务执行完成后再释放对象。 #### 安装 + ```shell npm i --save @eggjs/tegg-background-task ``` - #### 使用 + ```typescript import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; @@ -577,18 +860,21 @@ export default class BackgroundService { } ``` - #### 超时时间 + 框架不会无限的等待异步任务执行,在默认 5s 之后如果异步任务还没有完成则会放弃等待开始执行释放过程。如果需要等待更长的时间,建议有两种方式: - **推荐方式:将异步任务转发给单例对象(SingletonProto)来执行,单例对象永远不会释放** - 调整超时时间,对 `backgroundTaskHelper.timeout` 进行赋值即可 +- 如果将超时时间设置为 `Infinity`,框架将不会超时 +- 可以在 config 文件中指定 `backgroundTask.timeout` 来全局覆盖默认的超时时间 ### 动态注入 #### 使用 定义一个抽象类和一个类型枚举。 + ```ts export enum HelloType { FOO = 'FOO', @@ -601,6 +887,7 @@ export abstract class AbstractHello { ``` 定义一个自定义枚举。 + ```ts import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; import { ContextHelloType } from '../FooType'; @@ -615,6 +902,7 @@ export const Hello: ImplDecorator = ``` 实现抽象类。 + ```ts import { ContextProto } from '@eggjs/tegg'; import { ContextHello } from '../decorator/Hello'; @@ -632,6 +920,7 @@ export class BarHello extends AbstractHello { ``` 动态获取实现。 + ```ts import { EggObjectFactory } from '@eggjs/tegg'; @@ -647,20 +936,22 @@ export class HelloService { } ``` -### 配置项目 module +### 配置项目 module 一般情况下,无需在项目中手动声明包含了哪些 module,tegg 会自动在项目目录下进行扫描。但是会存在一些特殊的情况,tegg 无法扫描到,比如说 module 是通过 npm 包的方式来发布。因此 tegg 支持通过 `config/module.json` 的方式来声明包含了哪些 module。 支持通过 `path` 引用 `app/modules/foo` 目录下的 module。 + ```json [ {"path": "../app/modules/foo"} ] ``` -支持通过 `pkg` 引用使用 npm 发布的 module。 +支持通过 `package` 引用使用 npm 发布的 module。 + ```json [ - {"pkg": "foo"} + {"package": "foo"} ] ``` diff --git a/benchmark/http/package.json b/benchmark/http/package.json index c068319f..ea685b4e 100644 --- a/benchmark/http/package.json +++ b/benchmark/http/package.json @@ -1,20 +1,19 @@ { "name": "tegg_benchmark", "egg": { - "typescript": true }, "scripts": { "dev": "egg-bin dev" }, "dependencies": { - "@eggjs/tegg": "^1.1.1", - "@eggjs/tegg-config": "^1.1.1", - "@eggjs/tegg-controller-plugin": "^1.1.1", - "@eggjs/tegg-plugin": "^1.1.1", + "@eggjs/tegg": "^3.7.0", + "@eggjs/tegg-config": "^3.7.0", + "@eggjs/tegg-controller-plugin": "^3.7.0", + "@eggjs/tegg-plugin": "^3.7.0", "@eggjs/tsconfig": "^1.1.0", - "egg": "^2.36.0" + "egg": "^3.9.1" }, "devDependencies": { - "egg-bin": "^5.1.1" + "egg-bin": "6" } } diff --git a/core/aop-decorator/CHANGELOG.md b/core/aop-decorator/CHANGELOG.md index 83d90988..467b6ec4 100644 --- a/core/aop-decorator/CHANGELOG.md +++ b/core/aop-decorator/CHANGELOG.md @@ -3,6 +3,218 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + + +### Features + +* implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/aop-decorator + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* fix file path for advice decorator ([#64](https://github.com/eggjs/tegg/issues/64)) ([d6aa091](https://github.com/eggjs/tegg/commit/d6aa091851b5d1ca63e7e56e081df4d15ab3284e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/aop-decorator diff --git a/core/aop-decorator/index.ts b/core/aop-decorator/index.ts index a5994899..5a6bfb22 100644 --- a/core/aop-decorator/index.ts +++ b/core/aop-decorator/index.ts @@ -1,6 +1,6 @@ export * from './src/decorator/Advice'; export * from './src/decorator/Pointcut'; -export * from './src/decorator/Cosscut'; +export * from './src/decorator/Crosscut'; export * from './src/model/Aspect'; export * from './src/model/PointcutInfo'; export * from './src/util/AdviceInfoUtil'; diff --git a/core/aop-decorator/package.json b/core/aop-decorator/package.json index 6d5d2d0f..a0a5738f 100644 --- a/core/aop-decorator/package.json +++ b/core/aop-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/aop-decorator", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg aop decorator", "keywords": [ "tegg", @@ -16,16 +16,16 @@ "directory": "core/aop-decorator" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-metadata": "^1.4.0" + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0" }, "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "publishConfig": { "access": "public" @@ -41,5 +41,13 @@ ], "bugs": { "url": "https://github.com/eggjs/tegg/issues" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/aop-decorator/src/decorator/Advice.ts b/core/aop-decorator/src/decorator/Advice.ts index 21c7d3ed..3c79d5dd 100644 --- a/core/aop-decorator/src/decorator/Advice.ts +++ b/core/aop-decorator/src/decorator/Advice.ts @@ -3,14 +3,16 @@ import { EggProtoImplClass, ObjectInitType, Prototype, - PrototypeParams, + PrototypeParams, PrototypeUtil, } from '@eggjs/core-decorator'; import { AdviceInfoUtil } from '../util/AdviceInfoUtil'; +import { StackUtil } from '@eggjs/tegg-common-util'; -export interface AdviceContext { +export interface AdviceContext { that: T; method: PropertyKey; args: any[]; + adviceParams?: K; } /** @@ -20,33 +22,33 @@ export interface AdviceContext { * 1. afterReturn/afterThrow * 1. afterFinally */ -export interface IAdvice { +export interface IAdvice { /** * call before function * @param ctx */ - beforeCall?(ctx: AdviceContext): Promise; + beforeCall?(ctx: AdviceContext): Promise; /** * call after function succeed */ - afterReturn?(ctx: AdviceContext, result: any): Promise; + afterReturn?(ctx: AdviceContext, result: any): Promise; /** * call after function throw error */ - afterThrow?(ctx: AdviceContext, error: Error): Promise; + afterThrow?(ctx: AdviceContext, error: Error): Promise; /** * always call after function done */ - afterFinally?(ctx: AdviceContext): Promise; + afterFinally?(ctx: AdviceContext): Promise; /** * execute the function * the only one can modify the function return value */ - around?(ctx: AdviceContext, next: () => Promise): Promise; + around?(ctx: AdviceContext, next: () => Promise): Promise; } const defaultAdviceParam = { @@ -62,5 +64,6 @@ export function Advice(param?: PrototypeParams) { ...param, }); func(constructor); + PrototypeUtil.setFilePath(constructor, StackUtil.getCalleeFromStack(false, 5)); }; } diff --git a/core/aop-decorator/src/decorator/Cosscut.ts b/core/aop-decorator/src/decorator/Crosscut.ts similarity index 92% rename from core/aop-decorator/src/decorator/Cosscut.ts rename to core/aop-decorator/src/decorator/Crosscut.ts index 8740f5fe..ca2f339c 100644 --- a/core/aop-decorator/src/decorator/Cosscut.ts +++ b/core/aop-decorator/src/decorator/Crosscut.ts @@ -12,6 +12,7 @@ import { export interface CrosscutOptions { // 默认值 100 order?: number; + adviceParams?: any; } const defaultCrossOptions = { @@ -48,6 +49,7 @@ export function Crosscut(param: CrosscutParam, options?: CrosscutOptions) { adviceInfo: { clazz: constructor, order: options?.order ?? defaultCrossOptions.order, + adviceParams: options?.adviceParams, }, }; } else if (param.type === PointcutType.NAME) { @@ -56,6 +58,7 @@ export function Crosscut(param: CrosscutParam, options?: CrosscutOptions) { adviceInfo: { clazz: constructor, order: options?.order ?? defaultCrossOptions.order, + adviceParams: options?.adviceParams, }, }; } else { @@ -64,6 +67,7 @@ export function Crosscut(param: CrosscutParam, options?: CrosscutOptions) { adviceInfo: { clazz: constructor, order: options?.order ?? defaultCrossOptions.order, + adviceParams: options?.adviceParams, }, }; } diff --git a/core/aop-decorator/src/decorator/Pointcut.ts b/core/aop-decorator/src/decorator/Pointcut.ts index cbe15734..819e616f 100644 --- a/core/aop-decorator/src/decorator/Pointcut.ts +++ b/core/aop-decorator/src/decorator/Pointcut.ts @@ -7,6 +7,7 @@ import { AdviceInfoUtil } from '../util/AdviceInfoUtil'; export interface PointcutOptions { // default is 1000 order?: number; + adviceParams?: any; } const defaultPointcutOptions = { @@ -21,6 +22,7 @@ export function Pointcut(adviceClazz: EggProtoImplClass, options?: Poin PointcutAdviceInfoUtil.addPointcutAdviceInfo({ clazz: adviceClazz, order: options?.order ?? defaultPointcutOptions.order, + adviceParams: options?.adviceParams, }, targetClazz, methodName); }; } diff --git a/core/aop-decorator/src/model/Aspect.ts b/core/aop-decorator/src/model/Aspect.ts index 565d1567..e7bb2c0f 100644 --- a/core/aop-decorator/src/model/Aspect.ts +++ b/core/aop-decorator/src/model/Aspect.ts @@ -4,11 +4,13 @@ import { IAdvice } from '../decorator/Advice'; export interface AdviceInfo { clazz: EggProtoImplClass; order: number; + adviceParams: any; } export interface AspectAdvice { name: string; clazz: EggProtoImplClass; + adviceParams: any; } export class Aspect { @@ -44,6 +46,7 @@ export class AspectBuilder { return { clazz: t.clazz, name: this.adviceName(t.clazz, i), + adviceParams: t.adviceParams, }; }); return new Aspect(this.clazz, this.method, aspectAdviceList); diff --git a/core/aop-decorator/test/AspectMetaBuilder.test.ts b/core/aop-decorator/test/AspectMetaBuilder.test.ts index b17b5358..556051a3 100644 --- a/core/aop-decorator/test/AspectMetaBuilder.test.ts +++ b/core/aop-decorator/test/AspectMetaBuilder.test.ts @@ -21,6 +21,7 @@ import { PointcutAdviceOverwriteChildExample, PointcutAdviceOverwriteParentExample, } from './fixtures/InheritExample'; +import { PrototypeUtil } from '@eggjs/core-decorator'; describe('test/AspectMetaBuild.test.ts', () => { const crosscutAdviceFactory = new CrosscutAdviceFactory(); @@ -44,8 +45,8 @@ describe('test/AspectMetaBuild.test.ts', () => { const advices = aspect.adviceList; assert.deepStrictEqual(advices, [ - { name: 'PointcutExample#hello#PointcutAdviceBeforeCallExample#0', clazz: PointcutAdviceBeforeCallExample }, - { name: 'PointcutExample#hello#PointcutAdviceAfterReturnExample#1', clazz: PointcutAdviceAfterReturnExample }, + { name: 'PointcutExample#hello#PointcutAdviceBeforeCallExample#0', clazz: PointcutAdviceBeforeCallExample, adviceParams: undefined }, + { name: 'PointcutExample#hello#PointcutAdviceAfterReturnExample#1', clazz: PointcutAdviceAfterReturnExample, adviceParams: undefined }, ]); }); }); @@ -62,9 +63,9 @@ describe('test/AspectMetaBuild.test.ts', () => { assert(aspect.method === 'hello'); const advices = aspect.adviceList; assert.deepStrictEqual(advices, [ - { name: 'CrosscutExample#hello#CrosscutClassAdviceExample#0', clazz: CrosscutClassAdviceExample }, - { name: 'CrosscutExample#hello#CrosscutNameAdviceExample#1', clazz: CrosscutNameAdviceExample }, - { name: 'CrosscutExample#hello#CrosscutCustomAdviceExample#2', clazz: CrosscutCustomAdviceExample }, + { name: 'CrosscutExample#hello#CrosscutClassAdviceExample#0', clazz: CrosscutClassAdviceExample, adviceParams: undefined }, + { name: 'CrosscutExample#hello#CrosscutNameAdviceExample#1', clazz: CrosscutNameAdviceExample, adviceParams: undefined }, + { name: 'CrosscutExample#hello#CrosscutCustomAdviceExample#2', clazz: CrosscutCustomAdviceExample, adviceParams: undefined }, ]); }); }); @@ -82,11 +83,11 @@ describe('test/AspectMetaBuild.test.ts', () => { const overwriteAdvices = overwriteAspect.adviceList; assert.deepStrictEqual(overwriteAdvices, [ - { name: 'ChildExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample }, - { name: 'ChildExample#overwriteMethod#CrosscutOverwriteChildExample#1', clazz: CrosscutOverwriteChildExample }, + { name: 'ChildExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample, adviceParams: undefined }, + { name: 'ChildExample#overwriteMethod#CrosscutOverwriteChildExample#1', clazz: CrosscutOverwriteChildExample, adviceParams: undefined }, // FIXME: parent/child should has correct order - { name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteChildExample#2', clazz: PointcutAdviceOverwriteChildExample }, - { name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteParentExample#3', clazz: PointcutAdviceOverwriteParentExample }, + { name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteChildExample#2', clazz: PointcutAdviceOverwriteChildExample, adviceParams: undefined }, + { name: 'ChildExample#overwriteMethod#PointcutAdviceOverwriteParentExample#3', clazz: PointcutAdviceOverwriteParentExample, adviceParams: undefined }, ]); const noOverwriteAspect = aspects.find(t => t.method === 'noOverwriteMethod'); @@ -94,8 +95,8 @@ describe('test/AspectMetaBuild.test.ts', () => { assert(noOverwriteAspect.clazz === ChildExample); const noOverwriteAdvices = noOverwriteAspect.adviceList; assert.deepStrictEqual(noOverwriteAdvices, [ - { name: 'ChildExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample }, - { name: 'ChildExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample }, + { name: 'ChildExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample, adviceParams: undefined }, + { name: 'ChildExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample, adviceParams: undefined }, ]); }); @@ -111,8 +112,8 @@ describe('test/AspectMetaBuild.test.ts', () => { assert(overwriteAspect.clazz === ParentExample); const overwriteAdvices = overwriteAspect.adviceList; assert.deepStrictEqual(overwriteAdvices, [ - { name: 'ParentExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample }, - { name: 'ParentExample#overwriteMethod#PointcutAdviceOverwriteParentExample#1', clazz: PointcutAdviceOverwriteParentExample }, + { name: 'ParentExample#overwriteMethod#CrosscutOverwriteParentExample#0', clazz: CrosscutOverwriteParentExample, adviceParams: undefined }, + { name: 'ParentExample#overwriteMethod#PointcutAdviceOverwriteParentExample#1', clazz: PointcutAdviceOverwriteParentExample, adviceParams: undefined }, ]); const noOverwriteAspect = aspects.find(t => t.method === 'noOverwriteMethod'); @@ -120,8 +121,8 @@ describe('test/AspectMetaBuild.test.ts', () => { assert(noOverwriteAspect.clazz === ParentExample); const noOverwriteAdvices = noOverwriteAspect.adviceList; assert.deepStrictEqual(noOverwriteAdvices, [ - { name: 'ParentExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample }, - { name: 'ParentExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample }, + { name: 'ParentExample#noOverwriteMethod#CrosscutNoOverwriteParentExample#0', clazz: CrosscutNoOverwriteParentExample, adviceParams: undefined }, + { name: 'ParentExample#noOverwriteMethod#PointcutAdviceNoOverwriteParentExample#1', clazz: PointcutAdviceNoOverwriteParentExample, adviceParams: undefined }, ]); }); }); @@ -133,4 +134,9 @@ describe('test/AspectMetaBuild.test.ts', () => { const aspects = builder.build(); assert(aspects.length === 1); }); + + it('should has right file path', () => { + const filePath = PrototypeUtil.getFilePath(CrosscutClassAdviceExample); + assert(filePath === require.resolve('./fixtures/CrosscutExample')); + }); }); diff --git a/core/aop-decorator/test/fixtures/InheritExample.ts b/core/aop-decorator/test/fixtures/InheritExample.ts index d5958995..c77ce977 100644 --- a/core/aop-decorator/test/fixtures/InheritExample.ts +++ b/core/aop-decorator/test/fixtures/InheritExample.ts @@ -1,7 +1,7 @@ import { ContextProto } from '@eggjs/core-decorator'; import { Advice, AdviceContext, IAdvice } from '../../src/decorator/Advice'; import { Pointcut } from '../../src/decorator/Pointcut'; -import { Crosscut } from '../../src/decorator/Cosscut'; +import { Crosscut } from '../../src/decorator/Crosscut'; import { PointcutType } from '../../src/model/PointcutInfo'; @Advice() diff --git a/core/aop-decorator/tsconfig.json b/core/aop-decorator/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/aop-decorator/tsconfig.json +++ b/core/aop-decorator/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/aop-runtime/CHANGELOG.md b/core/aop-runtime/CHANGELOG.md index 7bc6cf50..a7bd6700 100644 --- a/core/aop-runtime/CHANGELOG.md +++ b/core/aop-runtime/CHANGELOG.md @@ -3,6 +3,257 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + + +### Bug Fixes + +* init all context advice if root proto miss ([#139](https://github.com/eggjs/tegg/issues/139)) ([0602ea8](https://github.com/eggjs/tegg/commit/0602ea81578bf717ee4b4c490ace8c1c133478c5)) + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + + +### Features + +* implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) +* fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-aop-runtime + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) +* fix mock prototype in aop not work ([#66](https://github.com/eggjs/tegg/issues/66)) ([16640eb](https://github.com/eggjs/tegg/commit/16640eb751405532b2a1241b17624ce3ac2d1c7a)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-runtime@1.4.0...@eggjs/tegg-aop-runtime@1.4.1) (2022-09-04) diff --git a/core/aop-runtime/package.json b/core/aop-runtime/package.json index 3f18a5be..d4b4b7b5 100644 --- a/core/aop-runtime/package.json +++ b/core/aop-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-aop-runtime", - "version": "1.5.0", + "version": "3.23.0", "description": "tegg aop", "main": "dist/index.js", "eggModule": { @@ -19,11 +19,11 @@ "aop" ], "scripts": { - "clean": "tsc -b --clean", + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", + "clean": "tsc -b --clean && rm -rf dist", "tsc": "npm run clean && tsc -p ./tsconfig.json", - "tsc-pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc-pub", - "autod": "autod" + "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -43,16 +43,23 @@ "access": "public" }, "dependencies": { - "@eggjs/aop-decorator": "^1.4.0", - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0", + "@eggjs/aop-decorator": "^3.23.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", "koa-compose": "^4.1.0" }, "devDependencies": { - "@eggjs/module-test-util": "^1.4.0", - "@eggjs/tegg-loader": "^1.4.0" + "@eggjs/module-test-util": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mm": "^3.2.1", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/aop-runtime/src/AspectExecutor.ts b/core/aop-runtime/src/AspectExecutor.ts index f8dfb905..e2f81efc 100644 --- a/core/aop-runtime/src/AspectExecutor.ts +++ b/core/aop-runtime/src/AspectExecutor.ts @@ -2,21 +2,25 @@ import { AdviceContext, AspectAdvice, IAdvice } from '@eggjs/aop-decorator'; import compose from 'koa-compose'; import type { Middleware } from 'koa-compose'; +interface InternalAdviceContext { + that: T; + method: PropertyKey; + args: any[]; +} + export class AspectExecutor { obj: Object; method: PropertyKey; aspectAdviceList: readonly AspectAdvice[]; - originMethod: Function; constructor(obj: object, method: PropertyKey, aspectAdviceList: readonly AspectAdvice[]) { this.obj = obj; this.method = method; this.aspectAdviceList = aspectAdviceList; - this.originMethod = obj[method]; } async execute(...args: any[]) { - const ctx: AdviceContext = { + const ctx: InternalAdviceContext = { that: this.obj, method: this.method, args, @@ -34,52 +38,59 @@ export class AspectExecutor { } } - async beforeCall(ctx: AdviceContext) { + async beforeCall(ctx: InternalAdviceContext) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.beforeCall) { - await advice.beforeCall(ctx); + await advice.beforeCall({ ...ctx, adviceParams: aspectAdvice.adviceParams }); } } } - async afterReturn(ctx: AdviceContext, result: any) { + async afterReturn(ctx: InternalAdviceContext, result: any) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.afterReturn) { - await advice.afterReturn(ctx, result); + await advice.afterReturn({ ...ctx, adviceParams: aspectAdvice.adviceParams }, result); } } } - async afterThrow(ctx: AdviceContext, error: Error) { + async afterThrow(ctx: InternalAdviceContext, error: Error) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.afterThrow) { - await advice.afterThrow(ctx, error); + await advice.afterThrow({ ...ctx, adviceParams: aspectAdvice.adviceParams }, error); } } } - async afterFinally(ctx: AdviceContext) { + async afterFinally(ctx: InternalAdviceContext) { for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; if (advice.afterFinally) { - await advice.afterFinally(ctx); + await advice.afterFinally({ ...ctx, adviceParams: aspectAdvice.adviceParams }); } } } - async doExecute(ctx: AdviceContext) { + async doExecute(ctx: InternalAdviceContext) { const lastCall = () => { - return Reflect.apply(this.originMethod, ctx.that, ctx.args); + const originMethod = Object.getPrototypeOf(this.obj)[this.method]; + return Reflect.apply(originMethod, ctx.that, ctx.args); }; - const functions: Array> = []; + const functions: Array> = []; for (const aspectAdvice of this.aspectAdviceList) { const advice: IAdvice = ctx.that[aspectAdvice.name]; const fn = advice.around; if (fn) { - functions.push(fn.bind(advice)); + functions.push(async (ctx: InternalAdviceContext, next: () => Promise) => { + const fnCtx: AdviceContext = { + ...ctx, + adviceParams: aspectAdvice.adviceParams, + }; + return await fn.call(advice, fnCtx, next); + }); } } functions.push(lastCall); diff --git a/core/aop-runtime/src/LoadUnitAopHook.ts b/core/aop-runtime/src/LoadUnitAopHook.ts index 963d9468..2e68ab7a 100644 --- a/core/aop-runtime/src/LoadUnitAopHook.ts +++ b/core/aop-runtime/src/LoadUnitAopHook.ts @@ -24,8 +24,8 @@ export class LoadUnitAopHook implements LifecycleHook { }); it('should work', async () => { - const ctx = new EggTestContext(); - const hello = await CoreTestHelper.getObject(Hello, ctx); - const callTrace = await CoreTestHelper.getObject(CallTrace); - const msg = await hello.hello('aop'); - const traceMsg = callTrace.msgs; - assert(msg === 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))))'); - assert.deepStrictEqual(traceMsg, [ - { - className: 'CrosscutAdvice', - methodName: 'beforeCall', - id: 233, - name: 'aop', - }, - { - className: 'PointcutAdvice', - methodName: 'beforeCall', - id: 233, - name: 'aop', - }, - { - className: 'CrosscutAdvice', - methodName: 'afterReturn', - id: 233, - name: 'withPointAroundParam(withCrosscutAroundParam(aop))', - result: 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))))', - }, - { - className: 'PointcutAdvice', - methodName: 'afterReturn', - id: 233, - name: 'withPointAroundParam(withCrosscutAroundParam(aop))', - result: 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))))', - }, - { - className: 'CrosscutAdvice', - methodName: 'afterFinally', - id: 233, - name: 'withPointAroundParam(withCrosscutAroundParam(aop))', - }, - { + await EggTestContext.mockContext(async () => { + const hello = await CoreTestHelper.getObject(Hello); + const callTrace = await CoreTestHelper.getObject(CallTrace); + const msg = await hello.hello('aop'); + const traceMsg = callTrace.msgs; + assert.deepStrictEqual(msg, `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`); + assert.deepStrictEqual(traceMsg, [ + { + className: 'CrosscutAdvice', + methodName: 'beforeCall', + id: 233, + name: 'aop', + adviceParams: crosscutAdviceParams, + }, + { + className: 'PointcutAdvice', + methodName: 'beforeCall', + id: 233, + name: 'aop', + adviceParams: pointcutAdviceParams, + }, + { + className: 'CrosscutAdvice', + methodName: 'afterReturn', + id: 233, + name: 'withPointAroundParam(withCrosscutAroundParam(aop))', + result: `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`, + adviceParams: crosscutAdviceParams, + }, + { + className: 'PointcutAdvice', + methodName: 'afterReturn', + id: 233, + name: 'withPointAroundParam(withCrosscutAroundParam(aop))', + result: `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`, + adviceParams: pointcutAdviceParams, + }, + { + className: 'CrosscutAdvice', + methodName: 'afterFinally', + id: 233, + name: 'withPointAroundParam(withCrosscutAroundParam(aop))', + adviceParams: crosscutAdviceParams, + }, + { + className: 'PointcutAdvice', + methodName: 'afterFinally', + id: 233, + name: 'withPointAroundParam(withCrosscutAroundParam(aop))', + adviceParams: pointcutAdviceParams, + }, + ]); + + await assert.rejects(async () => { + await hello.helloWithException('foo'); + }, new Error('ops, exception for withPointAroundParam(foo)')); + assert.deepStrictEqual(callTrace.msgs[callTrace.msgs.length - 2], { className: 'PointcutAdvice', - methodName: 'afterFinally', + methodName: 'afterThrow', id: 233, - name: 'withPointAroundParam(withCrosscutAroundParam(aop))', - }, - ]); + name: 'withPointAroundParam(foo)', + result: 'ops, exception for withPointAroundParam(foo)', + adviceParams: pointcutAdviceParams, + }); + }); + }); + + it('mock should work', async () => { + await EggTestContext.mockContext(async () => { + const hello = await CoreTestHelper.getObject(Hello); + let helloMocked = false; + mm(Hello.prototype, 'hello', async () => { + helloMocked = true; + }); + await hello.hello('aop'); + assert(helloMocked); + }); }); }); diff --git a/core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts b/core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts index 6055e77d..4de99037 100644 --- a/core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts +++ b/core/aop-runtime/test/fixtures/modules/hello_succeed/Hello.ts @@ -1,5 +1,6 @@ import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; import { Advice, AdviceContext, Crosscut, IAdvice, Pointcut, PointcutType } from '@eggjs/aop-decorator'; +import assert from 'assert'; export interface CallTraceMsg { className: string; @@ -7,6 +8,7 @@ export interface CallTraceMsg { id: number; name: string; result?: string; + adviceParams?: any; } @SingletonProto() @@ -18,43 +20,72 @@ export class CallTrace { } } +export const pointcutAdviceParams = { + point: Math.random().toString(), + cut: Math.random().toString(), +}; + @Advice() export class PointcutAdvice implements IAdvice { @Inject() callTrace: CallTrace; async beforeCall(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'beforeCall', id: ctx.that.id, name: ctx.args[0], + adviceParams: ctx.adviceParams, }); } async afterReturn(ctx: AdviceContext, result: any): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterReturn', id: ctx.that.id, name: ctx.args[0], result, + adviceParams: ctx.adviceParams, }); } + async afterThrow(ctx: AdviceContext, error: Error): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); + this.callTrace.addMsg({ + className: PointcutAdvice.name, + methodName: 'afterThrow', + id: ctx.that.id, + name: ctx.args[0], + result: error.message, + adviceParams: ctx.adviceParams, + }); + } + async afterFinally(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); this.callTrace.addMsg({ className: PointcutAdvice.name, methodName: 'afterFinally', id: ctx.that.id, name: ctx.args[0], + adviceParams: ctx.adviceParams, }); } async around(ctx: AdviceContext, next: () => Promise): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); ctx.args[0] = `withPointAroundParam(${ctx.args[0]})`; const result = await next(); - return `withPointAroundResult(${result})`; + return `withPointAroundResult(${result}${JSON.stringify(pointcutAdviceParams)})`; } } @@ -62,53 +93,75 @@ export class PointcutAdvice implements IAdvice { export class Hello { id = 233; - @Pointcut(PointcutAdvice) + @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) async hello(name: string) { return `hello ${name}`; } + + @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) + async helloWithException(name: string) { + throw new Error(`ops, exception for ${name}`); + } + } +export const crosscutAdviceParams = { + cross: Math.random().toString(), + cut: Math.random().toString(), +}; + @Crosscut({ type: PointcutType.CLASS, clazz: Hello, methodName: 'hello', -}) +}, { adviceParams: crosscutAdviceParams }) @Advice() -export class CrosscutAdvice implements IAdvice { +export class CrosscutAdvice implements IAdvice { @Inject() callTrace: CallTrace; - async beforeCall(ctx: AdviceContext): Promise { + async beforeCall(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'beforeCall', id: ctx.that.id, name: ctx.args[0], + adviceParams: ctx.adviceParams, }); } async afterReturn(ctx: AdviceContext, result: any): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'afterReturn', id: ctx.that.id, name: ctx.args[0], result, + adviceParams: ctx.adviceParams, }); } async afterFinally(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); this.callTrace.addMsg({ className: CrosscutAdvice.name, methodName: 'afterFinally', id: ctx.that.id, name: ctx.args[0], + adviceParams: ctx.adviceParams, }); } async around(ctx: AdviceContext, next: () => Promise): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); ctx.args[0] = `withCrosscutAroundParam(${ctx.args[0]})`; const result = await next(); - return `withCrossAroundResult(${result})`; + return `withCrossAroundResult(${result}${JSON.stringify(ctx.adviceParams)})`; } } diff --git a/core/aop-runtime/tsconfig.json b/core/aop-runtime/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/aop-runtime/tsconfig.json +++ b/core/aop-runtime/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/background-task/CHANGELOG.md b/core/background-task/CHANGELOG.md index 5408e222..5c82ee97 100644 --- a/core/background-task/CHANGELOG.md +++ b/core/background-task/CHANGELOG.md @@ -3,6 +3,258 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + + +### Features + +* add backgroundTask.timeout config ([#101](https://github.com/eggjs/tegg/issues/101)) ([0b1eee0](https://github.com/eggjs/tegg/commit/0b1eee00d6feb9c6d4509023dffe85c0ada749c2)) + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + + +### Bug Fixes + +* BackgroundTaskHelper should support recursively call ([#90](https://github.com/eggjs/tegg/issues/90)) ([368ac03](https://github.com/eggjs/tegg/commit/368ac0343d0d4e96b3768e7fd169b721551d0e4b)) + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-background-task + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-background-task diff --git a/core/background-task/package.json b/core/background-task/package.json index d2c913e0..0ca3fd05 100644 --- a/core/background-task/package.json +++ b/core/background-task/package.json @@ -1,7 +1,7 @@ { "name": "@eggjs/tegg-background-task", "description": "background util for tegg", - "version": "1.4.0", + "version": "3.23.0", "keywords": [ "egg", "typescript", @@ -16,11 +16,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -37,12 +37,19 @@ "author": "killagu ", "license": "MIT", "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-lifecycle": "^1.0.0" + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0" }, "devDependencies": { - "egg": "^2.26.0", - "mz-modules": "^2.1.0" + "@eggjs/tegg-common-util": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/core/background-task/src/BackgroundTaskHelper.ts b/core/background-task/src/BackgroundTaskHelper.ts index a3b767b2..aa52d7e6 100644 --- a/core/background-task/src/BackgroundTaskHelper.ts +++ b/core/background-task/src/BackgroundTaskHelper.ts @@ -1,6 +1,8 @@ +import assert from 'assert'; import { AccessLevel, ContextProto, Inject } from '@eggjs/core-decorator'; -import type { EggLogger } from 'egg'; +import type { EggLogger, EggAppConfig } from 'egg'; import { EggObjectLifecycle } from '@eggjs/tegg-lifecycle'; +import { ContextHandler, EggContextLifecycleUtil } from '@eggjs/tegg-runtime'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, @@ -12,8 +14,24 @@ export class BackgroundTaskHelper implements EggObjectLifecycle { // default timeout for async task timeout = 5000; + @Inject() + config: EggAppConfig; + private backgroundTasks: Array> = []; + async init() { + const ctx = ContextHandler.getContext(); + assert(ctx, 'background task helper must be init in context'); + EggContextLifecycleUtil.registerObjectLifecycle(ctx, { + preDestroy: async () => { + await this.doPreDestroy(); + }, + }); + if (this.config.backgroundTask?.timeout) { + this.timeout = this.config.backgroundTask.timeout; + } + } + run(fn: () => Promise) { const backgroundTask = new Promise(resolve => { try { @@ -37,21 +55,28 @@ export class BackgroundTaskHelper implements EggObjectLifecycle { this.backgroundTasks.push(backgroundTask); } - async preDestroy(): Promise { + async doPreDestroy(): Promise { // quick quit if (!this.backgroundTasks.length) return; + const backgroundTasks = this.backgroundTasks.slice(); + if (this.timeout <= 0 || this.timeout === Infinity) { + await Promise.all(backgroundTasks); + } else { + const { promise: timeout, resolve } = this.sleep(); - const { promise: timeout, resolve } = this.sleep(); - - await Promise.race([ - // not block the pre destroy process too long - timeout, - // ensure all background task are done before destroy the context - Promise.all(this.backgroundTasks), - ]); - - // always resolve the sleep promise - resolve(); + await Promise.race([ + // not block the pre destroy process too long + timeout, + // ensure all background task are done before destroy the context + Promise.all(backgroundTasks), + ]); + // always resolve the sleep promise + resolve(); + } + if (this.backgroundTasks.length !== backgroundTasks.length) { + this.backgroundTasks = this.backgroundTasks.slice(backgroundTasks.length); + return this.doPreDestroy(); + } } private sleep() { diff --git a/core/background-task/test/BackgroundTaskHelper.test.ts b/core/background-task/test/BackgroundTaskHelper.test.ts index 02e88b04..906f748c 100644 --- a/core/background-task/test/BackgroundTaskHelper.test.ts +++ b/core/background-task/test/BackgroundTaskHelper.test.ts @@ -1,5 +1,5 @@ import { BackgroundTaskHelper } from '../src/BackgroundTaskHelper'; -import sleep from 'mz-modules/sleep'; +import { TimerUtil } from '@eggjs/tegg-common-util'; import assert from 'assert'; describe('test/BackgroundTaskHelper.test.ts', () => { @@ -14,11 +14,11 @@ describe('test/BackgroundTaskHelper.test.ts', () => { it('should done', async () => { let run = false; helper.run(async () => { - await sleep(10); + await TimerUtil.sleep(10); run = true; }); - await helper.preDestroy(); + await helper.doPreDestroy(); assert(run); }); }); @@ -28,11 +28,11 @@ describe('test/BackgroundTaskHelper.test.ts', () => { let run = false; helper.timeout = 10; helper.run(async () => { - await sleep(100); + await TimerUtil.sleep(100); run = true; }); - await helper.preDestroy(); + await helper.doPreDestroy(); assert(run === false); }); }); @@ -40,11 +40,11 @@ describe('test/BackgroundTaskHelper.test.ts', () => { describe('fn reject', () => { it('should done', async () => { helper.run(async () => { - await sleep(10); + await TimerUtil.sleep(10); throw new Error('mock error'); }); - await helper.preDestroy(); + await helper.doPreDestroy(); }); }); @@ -54,7 +54,24 @@ describe('test/BackgroundTaskHelper.test.ts', () => { throw new Error('mock error'); }); - await helper.preDestroy(); + await helper.doPreDestroy(); + }); + }); + + describe('recursive fn', () => { + it('should done', async () => { + let runDone = 0; + helper.run(async () => { + await TimerUtil.sleep(10); + runDone++; + helper.run(async () => { + await TimerUtil.sleep(10); + runDone++; + }); + }); + + await helper.doPreDestroy(); + assert(runDone === 2); }); }); }); diff --git a/core/background-task/tsconfig.json b/core/background-task/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/background-task/tsconfig.json +++ b/core/background-task/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/common-util/.autod.conf.js b/core/common-util/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/common-util/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/common-util/CHANGELOG.md b/core/common-util/CHANGELOG.md index 4864a8dd..833cc93d 100644 --- a/core/common-util/CHANGELOG.md +++ b/core/common-util/CHANGELOG.md @@ -3,6 +3,181 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + + +### Features + +* support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + + +### Features + +* implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + + +### Bug Fixes + +* use posix join for package path ([#127](https://github.com/eggjs/tegg/issues/127)) ([53672f4](https://github.com/eggjs/tegg/commit/53672f404edb72c7330e125f72dd356cde0607ad)) + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + + +### Features + +* The exposed module reads the options. ([#112](https://github.com/eggjs/tegg/issues/112)) ([a52b44b](https://github.com/eggjs/tegg/commit/a52b44b753463bfdef6fbbc39f920be8eccf1567)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-common-util + + + + + # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-common-util diff --git a/core/common-util/index.ts b/core/common-util/index.ts index 12f53367..cecc82d1 100644 --- a/core/common-util/index.ts +++ b/core/common-util/index.ts @@ -6,3 +6,5 @@ export * from './src/FSUtil'; export * from './src/StackUtil'; export * from './src/ProxyUtil'; export * from './src/ModuleConfig'; +export * from './src/TimerUtil'; +export * from './src/RuntimeConfig'; diff --git a/core/common-util/package.json b/core/common-util/package.json index 7b2e99fd..8ee6bd79 100644 --- a/core/common-util/package.json +++ b/core/common-util/package.json @@ -1,7 +1,7 @@ { "name": "@eggjs/tegg-common-util", "description": "common util for tegg", - "version": "1.2.0", + "version": "3.23.0", "keywords": [ "egg", "typescript", @@ -15,11 +15,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -36,10 +36,19 @@ "node": ">=14.0.0" }, "dependencies": { - "globby": "^10.0.2", + "extend2": "^1.0.0", + "globby": "^11.1.0", "js-yaml": "^3.14.0" }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/common-util/src/ModuleConfig.ts b/core/common-util/src/ModuleConfig.ts index 3098ca63..eba68e7c 100644 --- a/core/common-util/src/ModuleConfig.ts +++ b/core/common-util/src/ModuleConfig.ts @@ -4,8 +4,10 @@ import fs, { promises as fsPromise } from 'fs'; import path from 'path'; import globby from 'globby'; import { FSUtil } from './FSUtil'; +import extend from 'extend2'; export interface ModuleReference { + name: string; path: string; } @@ -35,8 +37,9 @@ export interface ModuleConfig { export interface ReadModuleReferenceOptions { // module dir deep for globby when use auto scan module // default is 10 - deep?: number, + deep?: number; cwd?: string; + extraFilePattern?: string[]; } const DEFAULT_READ_MODULE_REF_OPTS = { @@ -44,11 +47,17 @@ const DEFAULT_READ_MODULE_REF_OPTS = { }; export class ModuleConfigUtil { - public static moduleYamlPath(modulePath: string): string { + public static moduleYamlPath(modulePath: string, env?: string): string { + if (env) { + return path.join(modulePath, `module.${env}.yml`); + } return path.join(modulePath, 'module.yml'); } - public static moduleJsonPath(modulePath: string): string { + public static moduleJsonPath(modulePath: string, env?: string): string { + if (env) { + return path.join(modulePath, `module.${env}.json`); + } return path.join(modulePath, 'module.json'); } @@ -71,14 +80,19 @@ export class ModuleConfigUtil { let moduleReference: ModuleReference; if (ModuleReferenceConfigHelp.isNpmModuleReference(moduleReferenceConfig)) { const options = cwd ? { paths: [ cwd ] } : {}; - const pkgJson = path.join(moduleReferenceConfig.package, 'package.json'); + // path.posix for windows keep path as foo/package.json + const pkgJson = path.posix.join(moduleReferenceConfig.package, 'package.json'); const file = require.resolve(pkgJson, options); + const modulePath = path.dirname(file); moduleReference = { - path: path.dirname(file), + path: modulePath, + name: ModuleConfigUtil.readModuleNameSync(modulePath), }; } else if (ModuleReferenceConfigHelp.isInlineModuleReference(moduleReferenceConfig)) { + const modulePath = path.join(configDir, moduleReferenceConfig.path); moduleReference = { - path: path.join(configDir, moduleReferenceConfig.path), + path: modulePath, + name: ModuleConfigUtil.readModuleNameSync(modulePath), }; } else { throw new Error('unknown type of module reference config: ' + JSON.stringify(moduleReferenceConfig)); @@ -99,6 +113,7 @@ export class ModuleConfigUtil { '!**/+(.*)/**', // not load coverage '!**/coverage', + ...(realOptions.extraFilePattern || []), ], { cwd: baseDir, deep: realOptions.deep, @@ -106,7 +121,13 @@ export class ModuleConfigUtil { const moduleDirSet = new Set(); for (const packagePath of packagePaths) { const absolutePkgPath = path.join(baseDir, packagePath); - const realPkgPath = fs.realpathSync(absolutePkgPath); + let realPkgPath; + try { + realPkgPath = fs.realpathSync(absolutePkgPath); + } catch (_) { + continue; + } + const moduleDir = path.dirname(realPkgPath); // skip the symbolic link @@ -115,13 +136,15 @@ export class ModuleConfigUtil { } moduleDirSet.add(moduleDir); + let name: string; try { - this.readModuleNameSync(moduleDir); + name = this.readModuleNameSync(moduleDir); } catch (_) { continue; } ref.push({ path: moduleDir, + name, }); } const moduleReferences = this.readModuleFromNodeModules(baseDir); @@ -134,24 +157,38 @@ export class ModuleConfigUtil { }); ref.push({ path: moduleReference.path, + name: moduleReference.name, }); } return ref; } - private static readModuleFromNodeModules(baseDir: string) { + public static readModuleFromNodeModules(baseDir: string) { const ref: ModuleReference[] = []; - const pkgContent = fs.readFileSync(path.join(baseDir, 'package.json'), 'utf8'); + let pkgContent: string; + try { + pkgContent = fs.readFileSync(path.join(baseDir, 'package.json'), 'utf8'); + } catch (_) { + return []; + } const pkg = JSON.parse(pkgContent); for (const dependencyKey of Object.keys(pkg.dependencies || {})) { - const absolutePkgPath = path.join(baseDir, '/node_modules', dependencyKey); + let packageJsonPath: string; + try { + // https://nodejs.org/api/packages.html#package-entry-points + // ignore cases where the package entry is exports but package.json is not exported + packageJsonPath = require.resolve(`${dependencyKey}/package.json`, { paths: [ baseDir ] }); + } catch (_) { + continue; + } + const absolutePkgPath = path.dirname(packageJsonPath); const realPkgPath = fs.realpathSync(absolutePkgPath); try { - if (this.readModuleNameSync(realPkgPath)) { - ref.push({ - path: realPkgPath, - }); - } + const name = this.readModuleNameSync(realPkgPath); + ref.push({ + path: realPkgPath, + name, + }); } catch (_) { continue; } @@ -167,33 +204,44 @@ export class ModuleConfigUtil { return path.join(baseDir, 'config', moduleDir); } + private static getModuleName(pkg: any) { + assert(pkg.eggModule && pkg.eggModule.name, 'eggModule.name not found in package.json'); + return pkg.eggModule.name; + } + public static async readModuleName(baseDir: string, moduleDir: string): Promise { moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); const pkgContent = await fsPromise.readFile(path.join(moduleDir, 'package.json'), 'utf8'); const pkg = JSON.parse(pkgContent); - assert(pkg.eggModule && pkg.eggModule.name, 'eggModule.name not found in package.json'); - return pkg.eggModule.name; + return ModuleConfigUtil.getModuleName(pkg); } public static readModuleNameSync(moduleDir: string, baseDir?: string): string { moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); const pkgContent = fs.readFileSync(path.join(moduleDir, 'package.json'), 'utf8'); const pkg = JSON.parse(pkgContent); - assert(pkg.eggModule && pkg.eggModule.name, 'eggModule.name not found in package.json'); - return pkg.eggModule.name; + return ModuleConfigUtil.getModuleName(pkg); } - public static async loadModuleConfig(moduleDir: string, baseDir?: string): Promise { + public static async loadModuleConfig(moduleDir: string, baseDir?: string, env?: string): Promise { moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); - const yamlConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir); - if (yamlConfig) { - return yamlConfig; + let defaultConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir); + if (!defaultConfig) { + defaultConfig = await ModuleConfigUtil.loadModuleJson(moduleDir); + } + let envConfig: ModuleConfig | undefined; + if (env) { + envConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir, env); + if (!envConfig) { + envConfig = await ModuleConfigUtil.loadModuleJson(moduleDir, env); + } } - return await ModuleConfigUtil.loadModuleJson(moduleDir); + extend(true, defaultConfig, envConfig); + return defaultConfig; } - private static async loadModuleJson(moduleDir: string): Promise { - const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir); + private static async loadModuleJson(moduleDir: string, env?: string): Promise { + const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir, env); const moduleJsonPathExists = await FSUtil.fileExists(moduleJsonPath); if (!moduleJsonPathExists) { return; @@ -203,8 +251,8 @@ export class ModuleConfigUtil { return moduleJson.config; } - private static async loadModuleYaml(moduleDir: string): Promise { - const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir); + private static async loadModuleYaml(moduleDir: string, env?: string): Promise { + const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir, env); const moduleYamlPathExists = await FSUtil.fileExists(moduleYamlPath); if (!moduleYamlPathExists) { return; @@ -213,17 +261,25 @@ export class ModuleConfigUtil { return yaml.safeLoad(moduleYamlContent) as ModuleConfigUtil; } - public static loadModuleConfigSync(moduleDir: string, baseDir?: string): ModuleConfig | undefined { + public static loadModuleConfigSync(moduleDir: string, baseDir?: string, env?: string): ModuleConfig | undefined { moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); - const yamlConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir); - if (yamlConfig) { - return yamlConfig; + let defaultConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir); + if (!defaultConfig) { + defaultConfig = ModuleConfigUtil.loadModuleJsonSync(moduleDir); + } + let envConfig: ModuleConfig | undefined; + if (env) { + envConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir, env); + if (!envConfig) { + envConfig = ModuleConfigUtil.loadModuleJsonSync(moduleDir, env); + } } - return ModuleConfigUtil.loadModuleJsonSync(moduleDir); + extend(true, defaultConfig, envConfig); + return defaultConfig; } - private static loadModuleJsonSync(moduleDir: string): ModuleConfig | undefined { - const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir); + private static loadModuleJsonSync(moduleDir: string, env?: string): ModuleConfig | undefined { + const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir, env); const moduleJsonPathExists = fs.existsSync(moduleJsonPath); if (!moduleJsonPathExists) { return; @@ -233,8 +289,8 @@ export class ModuleConfigUtil { return moduleJson.config; } - private static loadModuleYamlSync(moduleDir: string): ModuleConfig | undefined { - const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir); + private static loadModuleYamlSync(moduleDir: string, env?: string): ModuleConfig | undefined { + const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir, env); const moduleYamlPathExists = fs.existsSync(moduleYamlPath); if (!moduleYamlPathExists) { return; diff --git a/core/common-util/src/RuntimeConfig.ts b/core/common-util/src/RuntimeConfig.ts new file mode 100644 index 00000000..ac1dfb7d --- /dev/null +++ b/core/common-util/src/RuntimeConfig.ts @@ -0,0 +1,18 @@ +export type EnvType = 'local' | 'unittest' | 'prod' | string; + +export interface RuntimeConfig { + /** + * Application name + */ + name: string; + + /** + * Application environment + */ + env: EnvType; + + /** + * Application directory + */ + baseDir: string; +} diff --git a/core/common-util/src/TimerUtil.ts b/core/common-util/src/TimerUtil.ts new file mode 100644 index 00000000..65f68690 --- /dev/null +++ b/core/common-util/src/TimerUtil.ts @@ -0,0 +1,5 @@ +export class TimerUtil { + static async sleep(ms: number) { + await new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/core/common-util/test/ModuleConfig.test.ts b/core/common-util/test/ModuleConfig.test.ts index 6a338ae3..e407bf08 100644 --- a/core/common-util/test/ModuleConfig.test.ts +++ b/core/common-util/test/ModuleConfig.test.ts @@ -8,6 +8,11 @@ describe('test/ModuleConfig.test.ts', () => { const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/foo-yaml')); assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1' } }); }); + + it('should load env', () => { + const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config'), undefined, 'dev'); + assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1', port: 11306 } }); + }); }); describe('load module reference', () => { @@ -16,10 +21,10 @@ describe('test/ModuleConfig.test.ts', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-no-module-json'); const ref = ModuleConfigUtil.readModuleReference(fixturesPath); assert.deepStrictEqual(ref, [ - { path: path.join(fixturesPath, 'app/module-a') }, - { path: path.join(fixturesPath, 'app/module-b') }, - { path: path.join(fixturesPath, 'app/module-b/test/fixtures/module-e') }, - { path: path.join(fixturesPath, 'node_modules/module-c') }, + { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, + { path: path.join(fixturesPath, 'app/module-b'), name: 'moduleB' }, + { path: path.join(fixturesPath, 'app/module-b/test/fixtures/module-e'), name: 'moduleE' }, + { path: path.join(fixturesPath, 'node_modules/module-c'), name: 'moduleC' }, ]); }); @@ -33,7 +38,7 @@ describe('test/ModuleConfig.test.ts', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-symlink'); const ref = ModuleConfigUtil.readModuleReference(fixturesPath); assert.deepStrictEqual(ref, [ - { path: path.join(fixturesPath, 'app/module-a') }, + { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, ]); }); }); @@ -44,8 +49,8 @@ describe('test/ModuleConfig.test.ts', () => { const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-module-json'); const ref = ModuleConfigUtil.readModuleReference(fixturesPath); assert.deepStrictEqual(ref, [ - { path: path.join(fixturesPath, 'app/module-a') }, - { path: path.join(fixturesPath, 'app/module-b') }, + { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, + { path: path.join(fixturesPath, 'app/module-b'), name: 'moduleB' }, ]); }); }); @@ -57,9 +62,59 @@ describe('test/ModuleConfig.test.ts', () => { cwd: fixturesPath, }); assert.deepStrictEqual(ref, [ - { path: path.join(fixturesPath, 'node_modules/module-a') }, + { path: path.join(fixturesPath, 'node_modules/module-a'), name: 'moduleA' }, + ]); + }); + }); + + describe('filter module', () => { + it('should work', () => { + const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-modules'); + const readModuleOptions = { + cwd: fixturesPath, + extraFilePattern: [ '!**/dist' ], + }; + const ref = ModuleConfigUtil.readModuleReference(fixturesPath, readModuleOptions); + assert.deepStrictEqual(ref, [ + { path: path.join(fixturesPath, 'app/module-a'), name: 'moduleA' }, ]); }); }); }); + + describe('read package dependencies', () => { + + it('should success if package.json not exist', async () => { + const dir = path.resolve(__dirname, './fixtures/monorepo/foo'); + const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); + assert.deepStrictEqual(ret, []); + }); + + it('should success whether dependencies entry has exported package.json', async () => { + const dir = path.resolve(__dirname, './fixtures/monorepo/packages/d'); + const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); + assert.deepStrictEqual(ret, [{ + path: path.resolve(__dirname, './fixtures/monorepo/packages/d/node_modules/e'), + name: 'e', + }]); + }); + + it('should read dependencies from self node_modules', async () => { + const dir = path.resolve(__dirname, './fixtures/monorepo/packages/a'); + const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); + assert.deepStrictEqual(ret, [{ + path: path.resolve(__dirname, './fixtures/monorepo/packages/a/node_modules/c'), + name: 'c', + }]); + }); + + it('should read dependencies from parent node_modules', async () => { + const dir = path.resolve(__dirname, './fixtures/monorepo/packages/b'); + const ret = ModuleConfigUtil.readModuleFromNodeModules(dir); + assert.deepStrictEqual(ret, [{ + path: path.resolve(__dirname, './fixtures/monorepo/packages/a'), + name: 'a', + }]); + }); + }); }); diff --git a/core/common-util/test/TimerUtil.test.ts b/core/common-util/test/TimerUtil.test.ts new file mode 100644 index 00000000..f512657d --- /dev/null +++ b/core/common-util/test/TimerUtil.test.ts @@ -0,0 +1,11 @@ +import assert from 'assert'; +import { TimerUtil } from '..'; + +describe('test/TimerUtil.test.ts', () => { + it('should sleep work', async () => { + const start = Date.now(); + await TimerUtil.sleep(3); + const use = Date.now() - start; + assert(use > 1, `use time ${use}ms`); + }); +}); diff --git a/core/common-util/test/fixtures/apps/app-with-modules/app/module-a/package.json b/core/common-util/test/fixtures/apps/app-with-modules/app/module-a/package.json new file mode 100644 index 00000000..b9be02c6 --- /dev/null +++ b/core/common-util/test/fixtures/apps/app-with-modules/app/module-a/package.json @@ -0,0 +1,6 @@ +{ + "name": "module-a", + "eggModule": { + "name": "moduleA" + } +} diff --git a/core/common-util/test/fixtures/apps/app-with-modules/package.json b/core/common-util/test/fixtures/apps/app-with-modules/package.json new file mode 100644 index 00000000..bde99de9 --- /dev/null +++ b/core/common-util/test/fixtures/apps/app-with-modules/package.json @@ -0,0 +1,3 @@ +{ + "name": "foo" +} diff --git a/core/common-util/test/fixtures/modules/dev-module-config/module.dev.yml b/core/common-util/test/fixtures/modules/dev-module-config/module.dev.yml new file mode 100644 index 00000000..0f79ce2e --- /dev/null +++ b/core/common-util/test/fixtures/modules/dev-module-config/module.dev.yml @@ -0,0 +1,2 @@ +mysql: + port: 11306 diff --git a/core/common-util/test/fixtures/modules/dev-module-config/module.yml b/core/common-util/test/fixtures/modules/dev-module-config/module.yml new file mode 100644 index 00000000..9e55e708 --- /dev/null +++ b/core/common-util/test/fixtures/modules/dev-module-config/module.yml @@ -0,0 +1,2 @@ +mysql: + host: 127.0.0.1 diff --git a/core/common-util/test/fixtures/monorepo/foo/.gitkeep b/core/common-util/test/fixtures/monorepo/foo/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/core/common-util/test/fixtures/monorepo/node_modules/a b/core/common-util/test/fixtures/monorepo/node_modules/a new file mode 120000 index 00000000..670d7186 --- /dev/null +++ b/core/common-util/test/fixtures/monorepo/node_modules/a @@ -0,0 +1 @@ +../packages/a \ No newline at end of file diff --git a/core/common-util/test/fixtures/monorepo/packages/a/node_modules/c/package.json b/core/common-util/test/fixtures/monorepo/packages/a/node_modules/c/package.json new file mode 100644 index 00000000..889cdf3c --- /dev/null +++ b/core/common-util/test/fixtures/monorepo/packages/a/node_modules/c/package.json @@ -0,0 +1,8 @@ +{ + "eggModule": { + "name": "c" + }, + "dependencies": {}, + "name": "c", + "author": "" +} \ No newline at end of file diff --git a/core/common-util/test/fixtures/monorepo/packages/a/package.json b/core/common-util/test/fixtures/monorepo/packages/a/package.json new file mode 100644 index 00000000..3d93da3f --- /dev/null +++ b/core/common-util/test/fixtures/monorepo/packages/a/package.json @@ -0,0 +1,10 @@ +{ + "eggModule": { + "name": "a" + }, + "dependencies": { + "c": "*" + }, + "name": "b", + "author": "" +} \ No newline at end of file diff --git a/core/common-util/test/fixtures/monorepo/packages/b/package.json b/core/common-util/test/fixtures/monorepo/packages/b/package.json new file mode 100644 index 00000000..840de94d --- /dev/null +++ b/core/common-util/test/fixtures/monorepo/packages/b/package.json @@ -0,0 +1,10 @@ +{ + "eggModule": { + "name": "b" + }, + "dependencies": { + "a": "*" + }, + "name": "b", + "author": "" +} diff --git a/core/common-util/test/fixtures/monorepo/packages/d/node_modules/e/package.json b/core/common-util/test/fixtures/monorepo/packages/d/node_modules/e/package.json new file mode 100644 index 00000000..ccf90a50 --- /dev/null +++ b/core/common-util/test/fixtures/monorepo/packages/d/node_modules/e/package.json @@ -0,0 +1,10 @@ +{ + "eggModule": { + "name": "e" + }, + "name": "e", + "exports": { + "./package.json": "./package.json" + }, + "author": "" +} \ No newline at end of file diff --git a/core/common-util/test/fixtures/monorepo/packages/d/node_modules/f/package.json b/core/common-util/test/fixtures/monorepo/packages/d/node_modules/f/package.json new file mode 100644 index 00000000..51d24c2e --- /dev/null +++ b/core/common-util/test/fixtures/monorepo/packages/d/node_modules/f/package.json @@ -0,0 +1,10 @@ +{ + "eggModule": { + "name": "f" + }, + "name": "f", + "exports": { + "./index.js": "./index.js" + }, + "author": "" +} \ No newline at end of file diff --git a/core/common-util/test/fixtures/monorepo/packages/d/package.json b/core/common-util/test/fixtures/monorepo/packages/d/package.json new file mode 100644 index 00000000..22a2cbf2 --- /dev/null +++ b/core/common-util/test/fixtures/monorepo/packages/d/package.json @@ -0,0 +1,11 @@ +{ + "eggModule": { + "name": "d" + }, + "dependencies": { + "e": "*", + "f": "*" + }, + "name": "d", + "author": "" +} \ No newline at end of file diff --git a/core/common-util/tsconfig.json b/core/common-util/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/common-util/tsconfig.json +++ b/core/common-util/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/controller-decorator/.autod.conf.js b/core/controller-decorator/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/controller-decorator/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/controller-decorator/CHANGELOG.md b/core/controller-decorator/CHANGELOG.md index b75bbbfd..41a3722c 100644 --- a/core/controller-decorator/CHANGELOG.md +++ b/core/controller-decorator/CHANGELOG.md @@ -3,6 +3,216 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + + +### Bug Fixes + +* typo `acL` to `acl` ([#156](https://github.com/eggjs/tegg/issues/156)) ([a775d34](https://github.com/eggjs/tegg/commit/a775d34d38c481c5f9e90504224553d31ad728d3)) + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) +* middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) +* multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/controller-decorator + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) +* middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) +* multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) + + + + + # [1.5.0](https://github.com/eggjs/tegg/compare/@eggjs/controller-decorator@1.4.0...@eggjs/controller-decorator@1.5.0) (2022-08-16) diff --git a/core/controller-decorator/package.json b/core/controller-decorator/package.json index 2b42aafb..6b2ec383 100644 --- a/core/controller-decorator/package.json +++ b/core/controller-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/controller-decorator", - "version": "1.6.0", + "version": "3.23.0", "description": "tegg controller decorator", "keywords": [ "egg", @@ -16,11 +16,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -37,14 +37,20 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-metadata": "^1.4.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", "path-to-regexp": "^1.8.0", "reflect-metadata": "^0.1.13" }, "devDependencies": { - "egg": "^2.26.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", "undici": "^5.26.5" }, "publishConfig": { diff --git a/core/controller-decorator/src/decorator/http/HTTPController.ts b/core/controller-decorator/src/decorator/http/HTTPController.ts index 545af22e..6a70815a 100644 --- a/core/controller-decorator/src/decorator/http/HTTPController.ts +++ b/core/controller-decorator/src/decorator/http/HTTPController.ts @@ -1,4 +1,4 @@ -import { AccessLevel, ContextProto, EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; +import { AccessLevel, EggProtoImplClass, PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import HTTPInfoUtil from '../../util/HTTPInfoUtil'; import ControllerInfoUtil from '../../util/ControllerInfoUtil'; @@ -14,13 +14,13 @@ export function HTTPController(param?: HTTPControllerParams) { return function(constructor: EggProtoImplClass) { ControllerInfoUtil.setControllerType(constructor, ControllerType.HTTP); if (param?.controllerName) { - ControllerInfoUtil.setControllerName(constructor, param?.controllerName); + ControllerInfoUtil.setControllerName(constructor, param.controllerName); } if (param?.path) { HTTPInfoUtil.setHTTPPath(param.path, constructor); } // TODO elegant? - const func = ContextProto({ + const func = SingletonProto({ accessLevel: AccessLevel.PUBLIC, name: param?.protoName, }); diff --git a/core/controller-decorator/src/decorator/http/Host.ts b/core/controller-decorator/src/decorator/http/Host.ts index 58ac4c65..033e330c 100644 --- a/core/controller-decorator/src/decorator/http/Host.ts +++ b/core/controller-decorator/src/decorator/http/Host.ts @@ -2,26 +2,31 @@ import ControllerInfoUtil from '../../util/ControllerInfoUtil'; import { EggProtoImplClass } from '@eggjs/core-decorator'; import MethodInfoUtil from '../../util/MethodInfoUtil'; import assert from 'assert'; +import { HostType } from '../../model'; + +export function Host(host: HostType) { + + function parseHost(): string[] { + return Array.isArray(host) ? host : [ host ]; + } -export function Host(host: string) { function classHost(constructor: EggProtoImplClass) { - ControllerInfoUtil.addControllerHost(host, constructor); + ControllerInfoUtil.addControllerHosts(parseHost(), constructor); } - function methodHOst(target: any, propertyKey: PropertyKey) { + function methodHost(target: any, propertyKey: PropertyKey) { assert(typeof propertyKey === 'string', `[controller/${target.name}] expect method name be typeof string, but now is ${String(propertyKey)}`); const controllerClazz = target.constructor as EggProtoImplClass; const methodName = propertyKey as string; - - MethodInfoUtil.setMethodHost(host, controllerClazz, methodName); + MethodInfoUtil.setMethodHosts(parseHost(), controllerClazz, methodName); } return function(target: any, propertyKey?: PropertyKey) { if (propertyKey === undefined) { classHost(target); } else { - methodHOst(target, propertyKey); + methodHost(target, propertyKey); } }; } diff --git a/core/controller-decorator/src/impl/http/HTTPControllerMetaBuilder.ts b/core/controller-decorator/src/impl/http/HTTPControllerMetaBuilder.ts index 51acaf20..0494586c 100644 --- a/core/controller-decorator/src/impl/http/HTTPControllerMetaBuilder.ts +++ b/core/controller-decorator/src/impl/http/HTTPControllerMetaBuilder.ts @@ -43,9 +43,9 @@ export class HTTPControllerMetaBuilder { const protoName = property!.name as string; const needAcl = ControllerInfoUtil.hasControllerAcl(this.clazz); const aclCode = ControllerInfoUtil.getControllerAcl(this.clazz); - const host = ControllerInfoUtil.getControllerHost(this.clazz); + const hosts = ControllerInfoUtil.getControllerHosts(this.clazz); const metadata = new HTTPControllerMeta( - clazzName, protoName, controllerName, httpPath, httpMiddlewares, methods, needAcl, aclCode, host); + clazzName, protoName, controllerName, httpPath, httpMiddlewares, methods, needAcl, aclCode, hosts); ControllerMetadataUtil.setControllerMetadata(this.clazz, metadata); for (const method of metadata.methods) { const realPath = metadata.getMethodRealPath(method); @@ -63,4 +63,3 @@ export class HTTPControllerMetaBuilder { } ControllerMetaBuilderFactory.registerControllerMetaBuilder(ControllerType.HTTP, HTTPControllerMetaBuilder.create); - diff --git a/core/controller-decorator/src/impl/http/HTTPControllerMethodMetaBuilder.ts b/core/controller-decorator/src/impl/http/HTTPControllerMethodMetaBuilder.ts index 0ee8715b..06ec0a6c 100644 --- a/core/controller-decorator/src/impl/http/HTTPControllerMethodMetaBuilder.ts +++ b/core/controller-decorator/src/impl/http/HTTPControllerMethodMetaBuilder.ts @@ -105,13 +105,13 @@ export class HTTPControllerMethodMetaBuilder { const middlewares = MethodInfoUtil.getMethodMiddlewares(this.clazz, this.methodName); const needAcl = MethodInfoUtil.hasMethodAcl(this.clazz, this.methodName); const aclCode = MethodInfoUtil.getMethodAcl(this.clazz, this.methodName); - const host = MethodInfoUtil.getMethodHost(this.clazz, this.methodName); + const hosts = MethodInfoUtil.getMethodHosts(this.clazz, this.methodName); const realPath = parentPath ? path.posix.join(parentPath, httpPath) : httpPath; const paramTypeMap = this.buildParamType(realPath); const priority = this.getPriority(); return new HTTPMethodMeta( - this.methodName, httpPath!, httpMethod!, middlewares, contextIndex, paramTypeMap, priority, needAcl, aclCode, host); + this.methodName, httpPath!, httpMethod!, middlewares, contextIndex, paramTypeMap, priority, needAcl, aclCode, hosts); } } diff --git a/core/controller-decorator/src/model/HTTPControllerMeta.ts b/core/controller-decorator/src/model/HTTPControllerMeta.ts index a0786a4f..bea96ff2 100644 --- a/core/controller-decorator/src/model/HTTPControllerMeta.ts +++ b/core/controller-decorator/src/model/HTTPControllerMeta.ts @@ -14,7 +14,7 @@ export class HTTPControllerMeta implements ControllerMetadata { public readonly methods: readonly HTTPMethodMeta[]; public readonly needAcl: boolean; public readonly aclCode?: string; - public readonly host?: string; + public readonly hosts?: string[]; constructor( className: string, @@ -25,7 +25,7 @@ export class HTTPControllerMeta implements ControllerMetadata { methods: HTTPMethodMeta[], needAcl: boolean, aclCode: string | undefined, - host: string | undefined, + hosts: string[] | undefined, ) { this.protoName = protoName; this.controllerName = controllerName; @@ -35,7 +35,7 @@ export class HTTPControllerMeta implements ControllerMetadata { this.methods = methods; this.needAcl = needAcl; this.aclCode = aclCode; - this.host = host; + this.hosts = hosts; } getMethodRealPath(method: HTTPMethodMeta) { @@ -45,11 +45,11 @@ export class HTTPControllerMeta implements ControllerMetadata { return method.path; } - getMethodHost(method: HTTPMethodMeta): string | undefined { - if (this.host) { - return this.host; + getMethodHosts(method: HTTPMethodMeta): string[] | undefined { + if (this.hosts) { + return this.hosts; } - return method.host; + return method.hosts; } getMethodName(method: HTTPMethodMeta) { @@ -67,7 +67,7 @@ export class HTTPControllerMeta implements ControllerMetadata { } hasMethodAcl(method: HTTPMethodMeta): boolean { - return method.needAcL || this.needAcl; + return method.needAcl || this.needAcl; } getMethodAcl(method: HTTPMethodMeta): string | undefined { diff --git a/core/controller-decorator/src/model/HTTPMethodMeta.ts b/core/controller-decorator/src/model/HTTPMethodMeta.ts index ab0c091a..726746f5 100644 --- a/core/controller-decorator/src/model/HTTPMethodMeta.ts +++ b/core/controller-decorator/src/model/HTTPMethodMeta.ts @@ -78,9 +78,9 @@ export class HTTPMethodMeta implements MethodMeta { public readonly contextParamIndex: number | undefined; public readonly paramMap: Map; public readonly priority: number; - public readonly needAcL: boolean; + public readonly needAcl: boolean; public readonly aclCode: string | undefined; - public readonly host: string | undefined; + public readonly hosts: string[] | undefined; constructor( name: string, @@ -92,7 +92,7 @@ export class HTTPMethodMeta implements MethodMeta { priority: number, needAcl: boolean, aclCode: string | undefined, - host: string | undefined, + hosts: string[] | undefined, ) { this.name = name; this.path = path; @@ -101,9 +101,9 @@ export class HTTPMethodMeta implements MethodMeta { this.contextParamIndex = contextParamIndex; this.paramMap = paramTypeMap; this.priority = priority; - this.needAcL = needAcl; + this.needAcl = needAcl; this.aclCode = aclCode; - this.host = host; + this.hosts = hosts; } } diff --git a/core/controller-decorator/src/model/types.ts b/core/controller-decorator/src/model/types.ts index 26baa65f..761305a6 100644 --- a/core/controller-decorator/src/model/types.ts +++ b/core/controller-decorator/src/model/types.ts @@ -8,14 +8,18 @@ export enum ControllerType { HTTP = 'HTTP', SOFA_RPC = 'SOFA_RPC', MGW_RPC = 'MGW_RPC', + MESSAGE = 'MESSAGE', } +export type HostType = string | string []; + export type ControllerTypeLike = ControllerType | string; export enum MethodType { HTTP = 'HTTP', SOFA_RPC = 'SOFA_RPC', MGW_RPC = 'MGW_RPC', + MESSAGE = 'MESSAGE', } export type MethodTypeLike = ControllerType | string; diff --git a/core/controller-decorator/src/util/ControllerInfoUtil.ts b/core/controller-decorator/src/util/ControllerInfoUtil.ts index f947557d..7ff80747 100644 --- a/core/controller-decorator/src/util/ControllerInfoUtil.ts +++ b/core/controller-decorator/src/util/ControllerInfoUtil.ts @@ -45,11 +45,11 @@ export default class ControllerInfoUtil { return MetadataUtil.getMetaData(CONTROLLER_ACL, clazz); } - static addControllerHost(host: string, clazz: EggProtoImplClass) { - MetadataUtil.defineMetaData(CONTROLLER_HOST, host, clazz); + static addControllerHosts(hosts: string[], clazz: EggProtoImplClass) { + MetadataUtil.defineMetaData(CONTROLLER_HOST, hosts, clazz); } - static getControllerHost(clazz: EggProtoImplClass): string | undefined { + static getControllerHosts(clazz: EggProtoImplClass): string[] | undefined { return MetadataUtil.getMetaData(CONTROLLER_HOST, clazz); } } diff --git a/core/controller-decorator/src/util/MethodInfoUtil.ts b/core/controller-decorator/src/util/MethodInfoUtil.ts index 251204b2..9fd67e55 100644 --- a/core/controller-decorator/src/util/MethodInfoUtil.ts +++ b/core/controller-decorator/src/util/MethodInfoUtil.ts @@ -1,6 +1,6 @@ import { EggProtoImplClass, MetadataUtil } from '@eggjs/core-decorator'; -import { ControllerTypeLike, MiddlewareFunc } from '../model'; import { MapUtil } from '@eggjs/tegg-common-util'; +import { ControllerTypeLike, MiddlewareFunc } from '../model'; const METHOD_CONTROLLER_TYPE_MAP = Symbol.for('EggPrototype#controller#mthods'); const METHOD_CONTROLLER_HOST = Symbol.for('EggPrototype#controller#mthods#host'); @@ -8,7 +8,7 @@ const METHOD_CONTEXT_INDEX = Symbol.for('EggPrototype#controller#method#context' const METHOD_MIDDLEWARES = Symbol.for('EggPrototype#method#middlewares'); const METHOD_ACL = Symbol.for('EggPrototype#method#acl'); -type METHOD_MAP = Map; +type METHOD_MAP = Map; type MethodContextIndexMap = Map; type MethodMiddlewareMap = Map; type MethodAclMap = Map; @@ -21,7 +21,7 @@ export default class MethodInfoUtil { static getMethodControllerType(clazz: EggProtoImplClass, methodName: string): ControllerTypeLike | undefined { const methodControllerMap: METHOD_MAP | undefined = MetadataUtil.getMetaData(METHOD_CONTROLLER_TYPE_MAP, clazz); - return methodControllerMap?.get(methodName); + return methodControllerMap?.get(methodName) as ControllerTypeLike | undefined; } static setMethodContextIndexInArgs(index: number, clazz: EggProtoImplClass, methodName: string) { @@ -60,13 +60,13 @@ export default class MethodInfoUtil { return methodAclMap?.get(methodName); } - static setMethodHost(host: string, clazz: EggProtoImplClass, methodName: string) { + static setMethodHosts(hosts: string[], clazz: EggProtoImplClass, methodName: string) { const methodControllerMap: METHOD_MAP = MetadataUtil.initOwnMapMetaData(METHOD_CONTROLLER_HOST, clazz, new Map()); - methodControllerMap.set(methodName, host); + methodControllerMap.set(methodName, hosts); } - static getMethodHost(clazz: EggProtoImplClass, methodName: string): string | undefined { + static getMethodHosts(clazz: EggProtoImplClass, methodName: string): string[] | undefined { const methodControllerMap: METHOD_MAP | undefined = MetadataUtil.getMetaData(METHOD_CONTROLLER_HOST, clazz); - return methodControllerMap?.get(methodName); + return methodControllerMap?.get(methodName) as string[] | undefined; } } diff --git a/core/controller-decorator/test/Acl.test.ts b/core/controller-decorator/test/Acl.test.ts index 400fc455..2d7babf8 100644 --- a/core/controller-decorator/test/Acl.test.ts +++ b/core/controller-decorator/test/Acl.test.ts @@ -1,7 +1,7 @@ import assert from 'assert'; import { AclController } from './fixtures/AclController'; import { ControllerMetaBuilderFactory } from '../src/builder/ControllerMetaBuilderFactory'; -import { ControllerType } from '../src/model'; +import { ControllerType, HTTPControllerMeta } from '../src/model'; describe('test/Context.test.ts', () => { it('should work', () => { diff --git a/core/controller-decorator/test/http/Host.test.ts b/core/controller-decorator/test/http/Host.test.ts index 966f41a2..e7871504 100644 --- a/core/controller-decorator/test/http/Host.test.ts +++ b/core/controller-decorator/test/http/Host.test.ts @@ -5,12 +5,12 @@ import MethodInfoUtil from '../../src/util/MethodInfoUtil'; describe('test/Host.test.ts', () => { it('controller Host work', () => { - const controllerHost = ControllerInfoUtil.getControllerHost(HostController); - assert(controllerHost === 'foo.eggjs.com'); + const controllerHost = ControllerInfoUtil.getControllerHosts(HostController); + assert(controllerHost![0] === 'foo.eggjs.com'); }); it('method Host work', () => { - const methodHost = MethodInfoUtil.getMethodHost(HostController, 'bar'); - assert(methodHost === 'bar.eggjs.com'); + const methodHost = MethodInfoUtil.getMethodHosts(HostController, 'bar'); + assert(methodHost![0] === 'bar.eggjs.com'); }); }); diff --git a/core/controller-decorator/tsconfig.json b/core/controller-decorator/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/controller-decorator/tsconfig.json +++ b/core/controller-decorator/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/core-decorator/.autod.conf.js b/core/core-decorator/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/core-decorator/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/core-decorator/CHANGELOG.md b/core/core-decorator/CHANGELOG.md index b4f94d76..b0109b37 100644 --- a/core/core-decorator/CHANGELOG.md +++ b/core/core-decorator/CHANGELOG.md @@ -3,6 +3,219 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + + +### Features + +* add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + + +### Bug Fixes + +* fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + + +### Features + +* use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/core-decorator + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) diff --git a/core/core-decorator/README.md b/core/core-decorator/README.md index dc21269b..318d2de3 100644 --- a/core/core-decorator/README.md +++ b/core/core-decorator/README.md @@ -1,5 +1,5 @@ -# `@eggjs/standalon-decorator` +# `@eggjs/core-decorator` ## Usage -Please read [@eggjs/tegg-standalone](../../standalone/standalone) +Please read [@eggjs/tegg-plugin](../../plugin/tegg) diff --git a/core/core-decorator/index.ts b/core/core-decorator/index.ts index 7ba504e6..f54c2c46 100644 --- a/core/core-decorator/index.ts +++ b/core/core-decorator/index.ts @@ -4,13 +4,17 @@ export * from './src/decorator/InitTypeQualifier'; export * from './src/decorator/ModuleQualifier'; export * from './src/decorator/ContextProto'; export * from './src/decorator/SingletonProto'; +export * from './src/decorator/EggQualifier'; +export * from './src/decorator/MultiInstanceProto'; export * from './src/enum/AccessLevel'; export * from './src/enum/ObjectInitType'; +export * from './src/enum/EggType'; export * from './src/model/EggPrototypeInfo'; export * from './src/model/InjectObjectInfo'; export * from './src/model/QualifierInfo'; +export * from './src/model/EggMultiInstancePrototypeInfo'; export * from './src/util/MetadataUtil'; export * from './src/util/PrototypeUtil'; diff --git a/core/core-decorator/package.json b/core/core-decorator/package.json index fc4e1e76..08cdd952 100644 --- a/core/core-decorator/package.json +++ b/core/core-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/core-decorator", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg core decorator", "keywords": [ "egg", @@ -15,11 +15,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -36,10 +36,18 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/tegg-common-util": "^1.2.0", + "@eggjs/tegg-common-util": "^3.23.0", "reflect-metadata": "^0.1.13" }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/core-decorator/src/decorator/EggQualifier.ts b/core/core-decorator/src/decorator/EggQualifier.ts new file mode 100644 index 00000000..6814cd53 --- /dev/null +++ b/core/core-decorator/src/decorator/EggQualifier.ts @@ -0,0 +1,11 @@ +import { QualifierUtil } from '../util/QualifierUtil'; +import { EggProtoImplClass } from '../model/EggPrototypeInfo'; +import { EggType } from '../enum/EggType'; + +export const EggQualifierAttribute = Symbol.for('Qualifier.Egg'); + +export function EggQualifier(eggType: EggType) { + return function(target: any, propertyKey: PropertyKey) { + QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, EggQualifierAttribute, eggType); + }; +} diff --git a/core/core-decorator/src/decorator/MultiInstanceProto.ts b/core/core-decorator/src/decorator/MultiInstanceProto.ts new file mode 100644 index 00000000..fc638af0 --- /dev/null +++ b/core/core-decorator/src/decorator/MultiInstanceProto.ts @@ -0,0 +1,74 @@ +import { ObjectInitType, ObjectInitTypeLike } from '../enum/ObjectInitType'; +import { AccessLevel } from '../enum/AccessLevel'; +import { DEFAULT_PROTO_IMPL_TYPE } from './Prototype'; +import { + EggMultiInstanceCallbackPrototypeInfo, + EggMultiInstancePrototypeInfo, + MultiInstancePrototypeGetObjectsContext, + ObjectInfo, +} from '../model/EggMultiInstancePrototypeInfo'; +import { EggProtoImplClass } from '../model/EggPrototypeInfo'; +import { PrototypeUtil } from '../util/PrototypeUtil'; +import { StackUtil } from '@eggjs/tegg-common-util'; + +const DEFAULT_PARAMS = { + initType: ObjectInitType.SINGLETON, + accessLevel: AccessLevel.PRIVATE, + protoImplType: DEFAULT_PROTO_IMPL_TYPE, +}; + +export interface BaseMultiInstancePrototypeCallbackParams { + /** + * obj init type + */ + initType?: ObjectInitTypeLike; + /** + * access level + */ + accessLevel?: AccessLevel; + /** + * EggPrototype implement type + */ + protoImplType?: string; +} + +export interface MultiInstancePrototypeCallbackParams extends BaseMultiInstancePrototypeCallbackParams { + getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[]; +} + +export interface MultiInstancePrototypeStaticParams extends BaseMultiInstancePrototypeCallbackParams { + /** + * object info list + */ + objects: ObjectInfo[]; +} + +export type MultiInstancePrototypeParams = MultiInstancePrototypeCallbackParams | MultiInstancePrototypeStaticParams; + +export function MultiInstanceProto(param: MultiInstancePrototypeParams) { + return function(clazz: EggProtoImplClass) { + PrototypeUtil.setIsEggMultiInstancePrototype(clazz); + if ((param as MultiInstancePrototypeStaticParams).objects) { + const property: EggMultiInstancePrototypeInfo = { + ...DEFAULT_PARAMS, + ...param as MultiInstancePrototypeStaticParams, + className: clazz.name, + }; + PrototypeUtil.setMultiInstanceStaticProperty(clazz, property); + } else if ((param as MultiInstancePrototypeCallbackParams).getObjects) { + const property: EggMultiInstanceCallbackPrototypeInfo = { + ...DEFAULT_PARAMS, + ...param as MultiInstancePrototypeCallbackParams, + className: clazz.name, + }; + PrototypeUtil.setMultiInstanceCallbackProperty(clazz, property); + } + + // './tegg/core/common-util/src/StackUtil.ts', + // './tegg/core/core-decorator/src/decorator/Prototype.ts', + // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', + // './tegg/core/core-decorator/node_modules/_reflect-metadata@0.1.13@reflect-metadata/Reflect.js', + // './tegg/core/core-decorator/test/fixtures/decators/CacheService.ts', + PrototypeUtil.setFilePath(clazz, StackUtil.getCalleeFromStack(false, 4)); + }; +} diff --git a/core/core-decorator/src/decorator/Prototype.ts b/core/core-decorator/src/decorator/Prototype.ts index 7da72323..54c52d0d 100644 --- a/core/core-decorator/src/decorator/Prototype.ts +++ b/core/core-decorator/src/decorator/Prototype.ts @@ -26,6 +26,7 @@ export function Prototype(param?: PrototypeParams) { const property: Partial = { ...DEFAULT_PARAMS, ...param, + className: clazz.name, }; if (!property.name) { property.name = NameUtil.getClassName(clazz); diff --git a/core/core-decorator/src/enum/EggType.ts b/core/core-decorator/src/enum/EggType.ts new file mode 100644 index 00000000..95097df2 --- /dev/null +++ b/core/core-decorator/src/enum/EggType.ts @@ -0,0 +1,4 @@ +export enum EggType { + APP = 'APP', + CONTEXT = 'CONTEXT', +} diff --git a/core/core-decorator/src/model/EggMultiInstancePrototypeInfo.ts b/core/core-decorator/src/model/EggMultiInstancePrototypeInfo.ts new file mode 100644 index 00000000..f329e3d0 --- /dev/null +++ b/core/core-decorator/src/model/EggMultiInstancePrototypeInfo.ts @@ -0,0 +1,62 @@ +import { ObjectInitTypeLike } from '../enum/ObjectInitType'; +import { AccessLevel } from '../enum/AccessLevel'; +import { EggPrototypeName } from './EggPrototypeInfo'; +import { QualifierInfo } from './QualifierInfo'; + +export interface ObjectInfo { + name: EggPrototypeName; + qualifiers: QualifierInfo[]; +} + +export interface MultiInstancePrototypeGetObjectsContext { + unitPath: string; +} + +export interface EggMultiInstancePrototypeInfo { + /** + * The class name of the object + */ + className?: string; + /** + * obj init type + */ + initType: ObjectInitTypeLike; + /** + * access level + */ + accessLevel: AccessLevel; + /** + * EggPrototype implement type + */ + protoImplType: string; + + /** + * object info list + */ + objects: ObjectInfo[]; +} + +export interface EggMultiInstanceCallbackPrototypeInfo { + /** + * The class name of the object + */ + className?: string; + /** + * obj init type + */ + initType: ObjectInitTypeLike; + /** + * access level + */ + accessLevel: AccessLevel; + /** + * EggPrototype implement type + */ + protoImplType: string; + + /** + * get object callback + * @param ctx + */ + getObjects(ctx: MultiInstancePrototypeGetObjectsContext): ObjectInfo[]; +} diff --git a/core/core-decorator/src/model/EggPrototypeInfo.ts b/core/core-decorator/src/model/EggPrototypeInfo.ts index e8a21ac7..f612897f 100644 --- a/core/core-decorator/src/model/EggPrototypeInfo.ts +++ b/core/core-decorator/src/model/EggPrototypeInfo.ts @@ -1,5 +1,6 @@ import { ObjectInitTypeLike } from '../enum/ObjectInitType'; import { AccessLevel } from '../enum/AccessLevel'; +import { QualifierInfo } from './QualifierInfo'; export type EggProtoImplClass = new(...args: any[]) => T; export type EggPrototypeName = PropertyKey; @@ -9,6 +10,10 @@ export interface EggPrototypeInfo { * egg object name */ name: EggPrototypeName; + /** + * The class name of the object + */ + className?: string; /** * obj init type */ @@ -21,4 +26,8 @@ export interface EggPrototypeInfo { * EggPrototype implement type */ protoImplType: string; + /** + * EggPrototype qualifiers + */ + qualifiers?: QualifierInfo[]; } diff --git a/core/core-decorator/src/model/QualifierInfo.ts b/core/core-decorator/src/model/QualifierInfo.ts index 711b8556..dde83400 100644 --- a/core/core-decorator/src/model/QualifierInfo.ts +++ b/core/core-decorator/src/model/QualifierInfo.ts @@ -14,22 +14,22 @@ export type QualifierValue = string | number; * hello(name: string): Promise; * } * - * @ContextProto({ name: 'helloService' }) - * @HelloService(HelloServiceType.Email) + * \@ContextProto({ name: 'helloService' }) + * \@HelloService(HelloServiceType.Email) * class EmailHelloService implement HelloService { * ... * } * - * @ContextProto({ name: 'helloService' }) - * @HelloService(HelloServiceType.Message) + * \@ContextProto({ name: 'helloService' }) + * \@HelloService(HelloServiceType.Message) * class MessageHelloService implement HelloService { * ... * } * - * @ContextProto() + * \@ContextProto() * class HelloFacade { - * @Inject - * @HelloService(HelloServiceType.Message) + * \@Inject + * \@HelloService(HelloServiceType.Message) * helloService: HelloService; * } */ diff --git a/core/core-decorator/src/util/PrototypeUtil.ts b/core/core-decorator/src/util/PrototypeUtil.ts index 064197e8..c6d02385 100644 --- a/core/core-decorator/src/util/PrototypeUtil.ts +++ b/core/core-decorator/src/util/PrototypeUtil.ts @@ -1,11 +1,18 @@ import { MetadataUtil } from './MetadataUtil'; -import { EggProtoImplClass, EggPrototypeInfo } from '../model/EggPrototypeInfo'; +import { EggProtoImplClass, EggPrototypeInfo, EggPrototypeName } from '../model/EggPrototypeInfo'; import { InjectObjectInfo } from '../model/InjectObjectInfo'; +import { + EggMultiInstanceCallbackPrototypeInfo, + EggMultiInstancePrototypeInfo, MultiInstancePrototypeGetObjectsContext, +} from '../model/EggMultiInstancePrototypeInfo'; export class PrototypeUtil { static readonly IS_EGG_OBJECT_PROTOTYPE = Symbol.for('EggPrototype#isEggPrototype'); + static readonly IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE = Symbol.for('EggPrototype#isEggMultiInstancePrototype'); static readonly FILE_PATH = Symbol.for('EggPrototype.filePath'); static readonly PROTOTYPE_PROPERTY = Symbol.for('EggPrototype.Property'); + static readonly MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY = Symbol.for('EggPrototype.MultiInstanceStaticProperty'); + static readonly MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY = Symbol.for('EggPrototype.MultiInstanceCallbackProperty'); static readonly INJECT_OBJECT_NAME_SET = Symbol.for('EggPrototype.injectObjectNames'); static readonly CLAZZ_PROTO = Symbol.for('EggPrototype.clazzProto'); @@ -25,6 +32,22 @@ export class PrototypeUtil { return MetadataUtil.getBooleanMetaData(this.IS_EGG_OBJECT_PROTOTYPE, clazz); } + /** + * Mark class is egg object multi instance prototype + * @param {Function} clazz - + */ + static setIsEggMultiInstancePrototype(clazz: EggProtoImplClass) { + MetadataUtil.defineMetaData(this.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, true, clazz); + } + + /** + * If class is egg object multi instance prototype, return true + * @param {Function} clazz - + */ + static isEggMultiInstancePrototype(clazz: EggProtoImplClass): boolean { + return MetadataUtil.getBooleanMetaData(this.IS_EGG_OBJECT_MULTI_INSTANCE_PROTOTYPE, clazz); + } + /** * set class file path * @param {Function} clazz - @@ -60,6 +83,63 @@ export class PrototypeUtil { return MetadataUtil.getMetaData(this.PROTOTYPE_PROPERTY, clazz); } + static getInitType(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): string | undefined { + const property = this.getProperty(clazz) ?? this.getMultiInstanceProperty(clazz, ctx); + return property?.initType; + } + + static getAccessLevel(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): string | undefined { + const property = this.getProperty(clazz) ?? this.getMultiInstanceProperty(clazz, ctx); + return property?.accessLevel; + } + + static getObjNames(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): EggPrototypeName[] { + const property = this.getProperty(clazz); + if (property) { + return [ property.name ]; + } + const multiInstanceProperty = this.getMultiInstanceProperty(clazz, ctx); + return multiInstanceProperty?.objects.map(t => t.name) || []; + } + + /** + * set class property + * @param {EggProtoImplClass} clazz - + * @param {EggPrototypeInfo} property - + */ + static setMultiInstanceStaticProperty(clazz: EggProtoImplClass, property: EggMultiInstancePrototypeInfo) { + MetadataUtil.defineMetaData(this.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, property, clazz); + } + + /** + * set class property + * @param {EggProtoImplClass} clazz - + * @param {EggPrototypeInfo} property - + */ + static setMultiInstanceCallbackProperty(clazz: EggProtoImplClass, property: EggMultiInstanceCallbackPrototypeInfo) { + MetadataUtil.defineMetaData(this.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, property, clazz); + } + + /** + * get class property + * @param {EggProtoImplClass} clazz - + * @param {MultiInstancePrototypeGetObjectsContext} ctx - + * @return {EggPrototypeInfo} - + */ + static getMultiInstanceProperty(clazz: EggProtoImplClass, ctx: MultiInstancePrototypeGetObjectsContext): EggMultiInstancePrototypeInfo | undefined { + const metadata = MetadataUtil.getMetaData(this.MULTI_INSTANCE_PROTOTYPE_STATIC_PROPERTY, clazz); + if (metadata) { + return metadata; + } + const callBackMetadata = MetadataUtil.getMetaData(this.MULTI_INSTANCE_PROTOTYPE_CALLBACK_PROPERTY, clazz); + if (callBackMetadata) { + return { + ...callBackMetadata, + objects: callBackMetadata.getObjects(ctx), + }; + } + } + static addInjectObject(clazz: EggProtoImplClass, injectObject: InjectObjectInfo) { const objs: InjectObjectInfo[] = MetadataUtil.initOwnArrayMetaData(this.INJECT_OBJECT_NAME_SET, clazz, []); objs.push(injectObject); diff --git a/core/core-decorator/test/decorators.test.ts b/core/core-decorator/test/decorators.test.ts index c3e7a42c..2168edfe 100644 --- a/core/core-decorator/test/decorators.test.ts +++ b/core/core-decorator/test/decorators.test.ts @@ -16,6 +16,8 @@ import { DEFAULT_PROTO_IMPL_TYPE, } from '..'; import QualifierCacheService from './fixtures/decators/QualifierCacheService'; +import { FOO_ATTRIBUTE, FooLogger } from './fixtures/decators/FooLogger'; +import { EggMultiInstancePrototypeInfo } from '../src/model/EggMultiInstancePrototypeInfo'; describe('test/decorator.test.ts', () => { describe('ContextProto', () => { @@ -26,6 +28,7 @@ describe('test/decorator.test.ts', () => { initType: ObjectInitType.CONTEXT, accessLevel: AccessLevel.PUBLIC, protoImplType: DEFAULT_PROTO_IMPL_TYPE, + className: 'ContextCache', }; assert.deepStrictEqual(PrototypeUtil.getProperty(ContextCache), expectObjectProperty); }); @@ -39,6 +42,7 @@ describe('test/decorator.test.ts', () => { initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: DEFAULT_PROTO_IMPL_TYPE, + className: 'SingletonCache', }; assert.deepStrictEqual(PrototypeUtil.getProperty(SingletonCache), expectObjectProperty); }); @@ -90,6 +94,34 @@ describe('test/decorator.test.ts', () => { }); }); + describe('MultiInstanceProto', () => { + it('should work', () => { + assert(PrototypeUtil.isEggMultiInstancePrototype(FooLogger)); + const expectObjectProperty: EggMultiInstancePrototypeInfo = { + initType: ObjectInitType.SINGLETON, + accessLevel: AccessLevel.PUBLIC, + protoImplType: 'foo', + objects: [{ + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo1', + }], + }, { + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo2', + }], + }], + className: 'FooLogger', + }; + assert.deepStrictEqual(PrototypeUtil.getMultiInstanceProperty(FooLogger, { + unitPath: 'foo', + }), expectObjectProperty); + }); + }); + it('should get the right file path', () => { assert(PrototypeUtil.getFilePath(CacheService) === CacheService.fileName); }); diff --git a/core/core-decorator/test/fixtures/decators/FooLogger.ts b/core/core-decorator/test/fixtures/decators/FooLogger.ts new file mode 100644 index 00000000..ba5b8aa2 --- /dev/null +++ b/core/core-decorator/test/fixtures/decators/FooLogger.ts @@ -0,0 +1,27 @@ +import { MultiInstanceProto } from '../../../src/decorator/MultiInstanceProto'; +import { AccessLevel } from '../../../src/enum/AccessLevel'; +import { ObjectInitType } from '../../../src/enum/ObjectInitType'; + +export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); + +@MultiInstanceProto({ + accessLevel: AccessLevel.PUBLIC, + initType: ObjectInitType.SINGLETON, + protoImplType: 'foo', + objects: [{ + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo1', + }], + }, { + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo2', + }], + }], +}) +export class FooLogger { + +} diff --git a/core/core-decorator/tsconfig.json b/core/core-decorator/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/core-decorator/tsconfig.json +++ b/core/core-decorator/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/dynamic-inject-runtime/CHANGELOG.md b/core/dynamic-inject-runtime/CHANGELOG.md index 33b82eaa..821592a9 100644 --- a/core/dynamic-inject-runtime/CHANGELOG.md +++ b/core/dynamic-inject-runtime/CHANGELOG.md @@ -3,6 +3,237 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + + +### Features + +* remove context egg object factory ([#93](https://github.com/eggjs/tegg/issues/93)) ([e14bdb2](https://github.com/eggjs/tegg/commit/e14bdb257eaebc0b0a4c37c6073a5c3237718718)) + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject-runtime diff --git a/core/dynamic-inject-runtime/index.ts b/core/dynamic-inject-runtime/index.ts index 8076c48a..f8f69293 100644 --- a/core/dynamic-inject-runtime/index.ts +++ b/core/dynamic-inject-runtime/index.ts @@ -1,5 +1,4 @@ -export * from './src/impl/EggContextObjectFactory'; -export * from './src/impl/EggSingletonObjectFactory'; +export * from './src/EggObjectFactory'; import './src/EggObjectFactoryPrototype'; import './src/EggObjectFactoryObject'; diff --git a/core/dynamic-inject-runtime/package.json b/core/dynamic-inject-runtime/package.json index 5a3e450f..fdeca907 100644 --- a/core/dynamic-inject-runtime/package.json +++ b/core/dynamic-inject-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-dynamic-inject-runtime", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg dyniamic inject", "main": "dist/index.js", "eggModule": { @@ -18,11 +18,11 @@ "tegg" ], "scripts": { - "clean": "tsc -b --clean", + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", + "clean": "tsc -b --clean && rm -rf dist", "tsc": "npm run clean && tsc -p ./tsconfig.json", - "tsc-pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc-pub", - "autod": "autod" + "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -42,15 +42,21 @@ "access": "public" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-dynamic-inject": "^1.4.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0" + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-dynamic-inject": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0" }, "devDependencies": { - "@eggjs/module-test-util": "^1.4.0", - "@eggjs/tegg-loader": "^1.4.0" + "@eggjs/module-test-util": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/dynamic-inject-runtime/src/AbstractEggObjectFactory.ts b/core/dynamic-inject-runtime/src/AbstractEggObjectFactory.ts deleted file mode 100644 index c8d563e8..00000000 --- a/core/dynamic-inject-runtime/src/AbstractEggObjectFactory.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { EggAbstractClazz, EggObjectFactory } from '@eggjs/tegg-dynamic-inject'; -import type { EggContainerFactory, EggContext } from '@eggjs/tegg-runtime'; -import { QualifierValue } from '@eggjs/core-decorator'; - -export abstract class AbstractEggObjectFactory implements EggObjectFactory { - eggContext?: EggContext; - eggContainerFactory: typeof EggContainerFactory; - - abstract getEggObject(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue): Promise; -} diff --git a/core/dynamic-inject-runtime/src/impl/EggSingletonObjectFactory.ts b/core/dynamic-inject-runtime/src/EggObjectFactory.ts similarity index 75% rename from core/dynamic-inject-runtime/src/impl/EggSingletonObjectFactory.ts rename to core/dynamic-inject-runtime/src/EggObjectFactory.ts index 30463945..136a7d7d 100644 --- a/core/dynamic-inject-runtime/src/impl/EggSingletonObjectFactory.ts +++ b/core/dynamic-inject-runtime/src/EggObjectFactory.ts @@ -1,15 +1,14 @@ import type { EggContainerFactory } from '@eggjs/tegg-runtime'; -import { EggAbstractClazz, QualifierImplUtil } from '@eggjs/tegg-dynamic-inject'; +import { EggAbstractClazz, EggObjectFactory as IEggObjectFactory, QualifierImplUtil } from '@eggjs/tegg-dynamic-inject'; import { AccessLevel, PrototypeUtil, QualifierValue, SingletonProto } from '@eggjs/core-decorator'; -import { AbstractEggObjectFactory } from '../AbstractEggObjectFactory'; -import { EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE } from '../EggObjectFactoryPrototype'; +import { EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE } from './EggObjectFactoryPrototype'; @SingletonProto({ protoImplType: EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE, name: 'eggObjectFactory', accessLevel: AccessLevel.PUBLIC, }) -export class EggSingletonObjectFactory extends AbstractEggObjectFactory { +export class EggObjectFactory implements IEggObjectFactory { eggContainerFactory: typeof EggContainerFactory; async getEggObject(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue) { diff --git a/core/dynamic-inject-runtime/src/EggObjectFactoryObject.ts b/core/dynamic-inject-runtime/src/EggObjectFactoryObject.ts index 89ec9f46..53535046 100644 --- a/core/dynamic-inject-runtime/src/EggObjectFactoryObject.ts +++ b/core/dynamic-inject-runtime/src/EggObjectFactoryObject.ts @@ -2,14 +2,13 @@ import { EggContainerFactory, EggContext, EggObject, - EggObjectLifeCycleContext, EggObjectFactory as TEggObjectFactory, } from '@eggjs/tegg-runtime'; import { EggObjectFactoryPrototype } from './EggObjectFactoryPrototype'; import { EggObjectName } from '@eggjs/core-decorator'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { EggPrototype } from '@eggjs/tegg-metadata'; -import { AbstractEggObjectFactory } from './AbstractEggObjectFactory'; +import { EggObjectFactory } from './EggObjectFactory'; const OBJ = Symbol('EggObjectFactoryObject#obj'); @@ -18,26 +17,24 @@ export class EggObjectFactoryObject implements EggObject { readonly name: EggObjectName; readonly ctx?: EggContext; readonly id: string; - private [OBJ]: AbstractEggObjectFactory; + private [OBJ]: EggObjectFactory; - constructor(name: EggObjectName, proto: EggObjectFactoryPrototype, ctx?: EggContext) { + constructor(name: EggObjectName, proto: EggObjectFactoryPrototype) { this.proto = proto; this.name = name; - this.ctx = ctx; this.id = IdenticalUtil.createObjectId(this.proto.id, this.ctx?.id); } get obj() { if (!this[OBJ]) { - this[OBJ] = this.proto.constructEggObject() as AbstractEggObjectFactory; + this[OBJ] = this.proto.constructEggObject() as EggObjectFactory; this[OBJ].eggContainerFactory = EggContainerFactory; - this[OBJ].eggContext = this.ctx; } return this[OBJ]; } - static async createObject(name: EggObjectName, proto: EggPrototype, _: EggObjectLifeCycleContext, ctx?: EggContext): Promise { - return new EggObjectFactoryObject(name, proto as EggObjectFactoryPrototype, ctx); + static async createObject(name: EggObjectName, proto: EggPrototype): Promise { + return new EggObjectFactoryObject(name, proto as EggObjectFactoryPrototype); } readonly isReady: true; diff --git a/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts b/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts index 73fd5a5f..8bf4efbc 100644 --- a/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts +++ b/core/dynamic-inject-runtime/src/EggObjectFactoryPrototype.ts @@ -5,12 +5,12 @@ import { InjectObjectProto, LoadUnit, } from '@eggjs/tegg-metadata'; import { - AccessLevel, EggProtoImplClass, EggPrototypeName, + AccessLevel, EggProtoImplClass, EggPrototypeInfo, EggPrototypeName, MetaDataKey, MetadataUtil, - ObjectInitTypeLike, PrototypeUtil, + ObjectInitTypeLike, QualifierInfo, - QualifierUtil, + QualifierUtil, QualifierValue, } from '@eggjs/core-decorator'; import { NameUtil } from '@eggjs/tegg-common-util'; import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle'; @@ -28,15 +28,18 @@ export class EggObjectFactoryPrototype implements EggPrototype { readonly name: EggPrototypeName; readonly qualifiers: QualifierInfo[]; - constructor(clazz: EggProtoImplClass, loadUnit: LoadUnit) { + constructor(clazz: EggProtoImplClass, loadUnit: LoadUnit, prototypeInfo: EggPrototypeInfo) { this.clazz = clazz; - this.qualifiers = QualifierUtil.getProtoQualifiers(clazz); + this.qualifiers = [ + ...QualifierUtil.getProtoQualifiers(clazz), + ...(prototypeInfo.qualifiers ?? []), + ]; this.id = IdenticalUtil.createProtoId(loadUnit.id, NameUtil.getClassName(this.clazz)); - const property = PrototypeUtil.getProperty(clazz)!; - this.initType = property.initType; - this.accessLevel = property.accessLevel; + this.initType = prototypeInfo.initType; + this.accessLevel = prototypeInfo.accessLevel; this.loadUnitId = loadUnit.id; - this.name = property.name || NameUtil.getClassName(this.clazz); + this.name = prototypeInfo.name || NameUtil.getClassName(this.clazz); + this.injectObjects = []; } constructEggObject(): EggObjectFactory { @@ -52,6 +55,10 @@ export class EggObjectFactoryPrototype implements EggPrototype { return selfQualifiers?.value === qualifier.value; } + getQualifier(attribute: string): QualifierValue | undefined { + return this.qualifiers.find(t => t.attribute === attribute)?.value; + } + verifyQualifiers(qualifiers: QualifierInfo[]): boolean { for (const qualifier of qualifiers) { if (!this.verifyQualifier(qualifier)) { @@ -62,7 +69,7 @@ export class EggObjectFactoryPrototype implements EggPrototype { } static create(ctx: EggPrototypeLifecycleContext) { - return new EggObjectFactoryPrototype(ctx.clazz as EggProtoImplClass, ctx.loadUnit); + return new EggObjectFactoryPrototype(ctx.clazz as EggProtoImplClass, ctx.loadUnit, ctx.prototypeInfo); } } diff --git a/core/dynamic-inject-runtime/src/impl/EggContextObjectFactory.ts b/core/dynamic-inject-runtime/src/impl/EggContextObjectFactory.ts deleted file mode 100644 index 1ae26911..00000000 --- a/core/dynamic-inject-runtime/src/impl/EggContextObjectFactory.ts +++ /dev/null @@ -1,28 +0,0 @@ -import type { EggContainerFactory, EggContext } from '@eggjs/tegg-runtime'; -import { EggAbstractClazz, QualifierImplUtil } from '@eggjs/tegg-dynamic-inject'; -import { EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE } from '../EggObjectFactoryPrototype'; -import { AccessLevel, ContextProto, PrototypeUtil, QualifierValue } from '@eggjs/core-decorator'; -import { AbstractEggObjectFactory } from '../AbstractEggObjectFactory'; - -@ContextProto({ - protoImplType: EGG_OBJECT_FACTORY_PROTO_IMPLE_TYPE, - name: 'eggObjectFactory', - accessLevel: AccessLevel.PUBLIC, -}) -export class EggContextObjectFactory extends AbstractEggObjectFactory { - eggContext?: EggContext; - eggContainerFactory: typeof EggContainerFactory; - - async getEggObject(abstractClazz: EggAbstractClazz, qualifierValue: QualifierValue) { - const implClazz = QualifierImplUtil.getQualifierImp(abstractClazz, qualifierValue); - if (!implClazz) { - throw new Error(`has no impl for ${abstractClazz.name} with qualifier ${qualifierValue}`); - } - const protoObj: any = PrototypeUtil.getClazzProto(implClazz); - if (!protoObj) { - throw new Error(`can not get proto for clazz ${implClazz.name}`); - } - const eggObject = await this.eggContainerFactory.getOrCreateEggObject(protoObj, protoObj.name, this.eggContext); - return eggObject.obj as T; - } -} diff --git a/core/dynamic-inject-runtime/test/index.test.ts b/core/dynamic-inject-runtime/test/index.test.ts index e56398fb..367ad530 100644 --- a/core/dynamic-inject-runtime/test/index.test.ts +++ b/core/dynamic-inject-runtime/test/index.test.ts @@ -23,26 +23,28 @@ describe('test/dynamic-inject-runtime.test.ts', () => { }); it('should work', async () => { - const ctx = new EggTestContext(); - const helloService = await CoreTestHelper.getObject(HelloService, ctx); - const msgs = await helloService.hello(); - assert.deepStrictEqual(msgs, [ - 'hello, foo(context:0)', - 'hello, bar(context:0)', - 'hello, foo(singleton:0)', - 'hello, bar(singleton:0)', - ]); + await EggTestContext.mockContext(async () => { + const helloService = await CoreTestHelper.getObject(HelloService); + const msgs = await helloService.hello(); + assert.deepStrictEqual(msgs, [ + 'hello, foo(context:0)', + 'hello, bar(context:0)', + 'hello, foo(singleton:0)', + 'hello, bar(singleton:0)', + ]); + }); - const ctx2 = new EggTestContext(); - const helloService2 = await CoreTestHelper.getObject(HelloService, ctx2); - const msgs2 = await helloService2.hello(); - assert.deepStrictEqual(msgs2, [ - 'hello, foo(context:0)', - 'hello, bar(context:0)', - // singleton use the same object - // counter should has cache - 'hello, foo(singleton:1)', - 'hello, bar(singleton:1)', - ]); + await EggTestContext.mockContext(async () => { + const helloService = await CoreTestHelper.getObject(HelloService); + const msgs = await helloService.hello(); + assert.deepStrictEqual(msgs, [ + 'hello, foo(context:0)', + 'hello, bar(context:0)', + // singleton use the same object + // counter should has cache + 'hello, foo(singleton:1)', + 'hello, bar(singleton:1)', + ]); + }); }); }); diff --git a/core/dynamic-inject-runtime/tsconfig.json b/core/dynamic-inject-runtime/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/dynamic-inject-runtime/tsconfig.json +++ b/core/dynamic-inject-runtime/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/dynamic-inject/CHANGELOG.md b/core/dynamic-inject/CHANGELOG.md index fb282ffe..66be0fbe 100644 --- a/core/dynamic-inject/CHANGELOG.md +++ b/core/dynamic-inject/CHANGELOG.md @@ -3,6 +3,207 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-dynamic-inject + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-dynamic-inject diff --git a/core/dynamic-inject/package.json b/core/dynamic-inject/package.json index e269c861..754df016 100644 --- a/core/dynamic-inject/package.json +++ b/core/dynamic-inject/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-dynamic-inject", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg dyniamic inject", "main": "dist/index.js", "files": [ @@ -15,11 +15,11 @@ "tegg" ], "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", - "tsc-pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc-pub", - "autod": "autod" + "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -39,9 +39,15 @@ "access": "public" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0" + "@eggjs/core-decorator": "^3.23.0" }, "devDependencies": { - "coffee": "^5.4.0" + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "coffee": "^5.4.0", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/dynamic-inject/tsconfig.json b/core/dynamic-inject/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/dynamic-inject/tsconfig.json +++ b/core/dynamic-inject/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/eventbus-decorator/CHANGELOG.md b/core/eventbus-decorator/CHANGELOG.md index 23e44676..25986382 100644 --- a/core/eventbus-decorator/CHANGELOG.md +++ b/core/eventbus-decorator/CHANGELOG.md @@ -3,6 +3,220 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + + +### Bug Fixes + +* eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/eventbus-decorator + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/eventbus-decorator diff --git a/core/eventbus-decorator/README.md b/core/eventbus-decorator/README.md index 2144f93c..1c8be6d8 100644 --- a/core/eventbus-decorator/README.md +++ b/core/eventbus-decorator/README.md @@ -3,6 +3,7 @@ ## Usage ### emit event + ```ts import { EventBus } from '@eggjs/eventbus-decorator' @@ -24,6 +25,27 @@ class Foo { } ``` +### cork events + +Cache events in memory until uncork. + +```ts +class Foo { + @Inject() + private readonly eventBus: ContextEventBus; + + bar() { + this.eventBus.cork(); + // ...do something + this.eventBus.emit('hello', '01'); + // ...do other things + + // emit all cached events + this.eventBus.uncork(); + } +} +``` + ### handle event ```ts diff --git a/core/eventbus-decorator/package.json b/core/eventbus-decorator/package.json index a5a7ac38..e0d8746d 100644 --- a/core/eventbus-decorator/package.json +++ b/core/eventbus-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/eventbus-decorator", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg eventbus decorator", "keywords": [ "egg", @@ -16,12 +16,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "ci": "npm run lint && npm run cov", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -35,15 +34,21 @@ "directory": "core/eventbus-decorator" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", "typed-emitter": "^1.3.1" }, "engines": { "node": ">=14.0.0" }, "devDependencies": { - "coffee": "^5.4.0" + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "coffee": "^5.4.0", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/core/eventbus-decorator/src/Event.ts b/core/eventbus-decorator/src/Event.ts index bd731d28..377e8e2b 100644 --- a/core/eventbus-decorator/src/Event.ts +++ b/core/eventbus-decorator/src/Event.ts @@ -1,4 +1,4 @@ -import { AccessLevel, ContextProto, PrototypeUtil } from '@eggjs/core-decorator'; +import { AccessLevel, PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { StackUtil } from '@eggjs/tegg-common-util'; import { EventHandler } from '../index'; import { EventInfoUtil } from './EventInfoUtil'; @@ -9,7 +9,7 @@ import { Events } from '@eggjs/tegg'; export function Event(eventName: E) { return function(clazz: new () => EventHandler) { EventInfoUtil.setEventName(eventName, clazz); - const func = ContextProto({ + const func = SingletonProto({ accessLevel: AccessLevel.PUBLIC, }); func(clazz); diff --git a/core/eventbus-decorator/src/EventBus.ts b/core/eventbus-decorator/src/EventBus.ts index 8603312c..abce2529 100644 --- a/core/eventbus-decorator/src/EventBus.ts +++ b/core/eventbus-decorator/src/EventBus.ts @@ -9,6 +9,19 @@ export type EventName = string | symbol; * use `emit` to emit a event */ export interface EventBus extends Pick, 'emit'> { + cork(corkId: string); + + /** + * @return true if uncorked + */ + uncork(corkId: string): boolean; +} + +export const CORK_ID = Symbol.for('eventBus#corkId'); + +export interface ContextEventBus extends EventBus { + cork(); + uncork(); } export type EventKeys = keyof Events; diff --git a/core/eventbus-decorator/test/Event.test.ts b/core/eventbus-decorator/test/Event.test.ts index 83dbe505..2185246e 100644 --- a/core/eventbus-decorator/test/Event.test.ts +++ b/core/eventbus-decorator/test/Event.test.ts @@ -20,7 +20,7 @@ describe('test/Event.test.ts', () => { tsc, [ '--noEmit', '--experimentalDecorators', ...files ], ) - .debug() + // .debug() .expect('stdout', /Type 'number' is not assignable to type 'string'/) .notExpect('code', 0) .end(); diff --git a/core/eventbus-decorator/tsconfig.json b/core/eventbus-decorator/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/eventbus-decorator/tsconfig.json +++ b/core/eventbus-decorator/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/eventbus-runtime/CHANGELOG.md b/core/eventbus-runtime/CHANGELOG.md index 62cc04ad..693e7308 100644 --- a/core/eventbus-runtime/CHANGELOG.md +++ b/core/eventbus-runtime/CHANGELOG.md @@ -3,6 +3,260 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + + +### Bug Fixes + +* eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + + +### Features + +* use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) +* fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) +* inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-runtime + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* eventbus runtime should wait all handlers done ([#51](https://github.com/eggjs/tegg/issues/51)) ([0651d30](https://github.com/eggjs/tegg/commit/0651d300f9a18bd97299548f3ebccad1d0382d28)) +* fix events type from any to keyof Events ([#54](https://github.com/eggjs/tegg/issues/54)) ([a2551b2](https://github.com/eggjs/tegg/commit/a2551b2d9f9eabf9ed5c87f83489615eefa3e6d1)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-runtime@1.4.0...@eggjs/tegg-eventbus-runtime@1.4.1) (2022-09-04) diff --git a/core/eventbus-runtime/package.json b/core/eventbus-runtime/package.json index 57e0bfb8..625bdb14 100644 --- a/core/eventbus-runtime/package.json +++ b/core/eventbus-runtime/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-eventbus-runtime", - "version": "1.5.0", + "version": "3.23.0", "description": "tegg eventbus runtime", "keywords": [ "egg", @@ -19,12 +19,11 @@ ], "typings": "dist/index.d.ts", "scripts": { - "clean": "tsc -b --clean", + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", + "clean": "tsc -b --clean && rm -rf dist", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "ci": "npm run lint && npm run cov", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -38,23 +37,28 @@ "directory": "core/eventbus-runtime" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/eventbus-decorator": "^1.4.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/eventbus-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", "await-event": "^2.1.0", - "await-first": "^1.0.0", - "typed-emitter": "^1.3.1" + "await-first": "^1.0.0" }, "engines": { "node": ">=14.0.0" }, "devDependencies": { - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", "coffee": "^5.4.0", - "egg": "^2.29.3", - "mm": "^3.2.0" + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "mm": "^3.2.0", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/core/eventbus-runtime/src/EventHandlerFactory.ts b/core/eventbus-runtime/src/EventHandlerFactory.ts index 9ccf510c..f813a286 100644 --- a/core/eventbus-runtime/src/EventHandlerFactory.ts +++ b/core/eventbus-runtime/src/EventHandlerFactory.ts @@ -1,5 +1,5 @@ import { EventHandler, EventName, Events } from '@eggjs/eventbus-decorator'; -import { EggContext } from '@eggjs/tegg-runtime'; +import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { MapUtil } from '@eggjs/tegg-common-util'; import { AccessLevel, SingletonProto } from '@eggjs/core-decorator'; @@ -19,10 +19,10 @@ export class EventHandlerFactory { return this.handlerProtoMap.has(event); } - async getHandlers(event: EventName, ctx: EggContext): Promise>> { + async getHandlers(event: EventName): Promise>> { const handlerProtos = this.handlerProtoMap.get(event) || []; const eggObjs = await Promise.all(handlerProtos.map(proto => { - return ctx.getOrCreateEggObject(proto.name, proto, ctx); + return EggContainerFactory.getOrCreateEggObject(proto, proto.name); })); return eggObjs.map(t => t.obj as EventHandler); } diff --git a/core/eventbus-runtime/src/SingletonEventBus.ts b/core/eventbus-runtime/src/SingletonEventBus.ts index c1ae66d6..b95c24ea 100644 --- a/core/eventbus-runtime/src/SingletonEventBus.ts +++ b/core/eventbus-runtime/src/SingletonEventBus.ts @@ -1,6 +1,6 @@ import { AccessLevel, Inject, SingletonProto } from '@eggjs/core-decorator'; -import { EventBus, Events, EventWaiter, EventName } from '@eggjs/eventbus-decorator'; -import { EggContext } from '@eggjs/tegg-runtime'; +import { EventBus, Events, EventWaiter, EventName, CORK_ID } from '@eggjs/eventbus-decorator'; +import { ContextHandler, EggContext } from '@eggjs/tegg-runtime'; import type { EggLogger } from 'egg'; import { EventContextFactory } from './EventContextFactory'; import { EventHandlerFactory } from './EventHandlerFactory'; @@ -9,10 +9,21 @@ import awaitEvent from 'await-event'; import awaitFirst from 'await-first'; // from typed-emitter -type Arguments = [ T ] extends [ (...args: infer U) => any ] +type Array = [ T ] extends [ (...args: infer U) => any ] ? U : [ T ] extends [ void ] ? [] : [ T ]; +export interface Event { + name: EventName; + args: Array; + context?: EggContext; +} + +export interface CorkEvents { + times: number; + events: Array; +} + @SingletonProto({ // TODO 需要考虑支持别名 // SingletonEventBus 同时实现了两个接口 @@ -28,9 +39,15 @@ export class SingletonEventBus implements EventBus, EventWaiter { @Inject() private readonly eventHandlerFactory: EventHandlerFactory; - @Inject() + @Inject({ + name: 'logger', + }) private readonly logger: EggLogger; + private corkIdSequence = 0; + + private readonly corkedEvents = new Map(); + /** * only use for ensure event will happen */ @@ -39,64 +56,117 @@ export class SingletonEventBus implements EventBus, EventWaiter { return this; } - async await(event: E): Promise> { + async await(event: E): Promise> { return awaitEvent(this.emitter, event); } - awaitFirst(...e: Array): Promise<{ event: EventName, args: Arguments }> { + awaitFirst(...e: Array): Promise<{ event: EventName, args: Array }> { return awaitFirst(this.emitter, e); } - emit(event: E, ...args: Arguments): boolean { + emit(event: E, ...args: Array): boolean { const ctx = this.eventContextFactory.createContext(); const hasListener = this.eventHandlerFactory.hasListeners(event); this.doEmit(ctx, event, args); return hasListener; } - emitWithContext(parentContext: EggContext, event: E, args: Arguments): boolean { - const ctx = this.eventContextFactory.createContext(parentContext); + generateCorkId(): string { + return String(++this.corkIdSequence); + } + + cork(corkId: string) { + let corkEvents = this.corkedEvents.get(corkId); + if (!corkEvents) { + corkEvents = { + times: 0, + events: [], + } as unknown as CorkEvents; + this.corkedEvents.set(corkId, corkEvents); + } + corkEvents!.times++; + } + + uncork(corkId: string) { + const corkEvents = this.corkedEvents.get(corkId); + if (!corkEvents) { + throw new Error(`eventbus corkId ${corkId} not found`); + } + if (--corkEvents.times !== 0) { + return false; + } + this.corkedEvents.delete(corkId); + for (const event of corkEvents.events) { + if (event.context) { + this.doEmitWithContext(event.context, event.name, event.args); + } + } + return true; + } + + queueEvent(corkId: string, event: Event) { + const corkdEvents = this.corkedEvents.get(corkId); + if (!corkdEvents) { + throw new Error(`eventbus corkId ${corkId} not found`); + } + corkdEvents.events.push(event); + } + + emitWithContext(parentContext: EggContext, event: E, args: Array): boolean { + const corkId = parentContext.get(CORK_ID); + const hasListener = this.eventHandlerFactory.hasListeners(event); + if (corkId) { + this.queueEvent(corkId, { name: event, args, context: parentContext }); + return hasListener; + } + return this.doEmitWithContext(parentContext, event, args); + } + + private doEmitWithContext(parentContext: EggContext, event: EventName, args: Array): boolean { const hasListener = this.eventHandlerFactory.hasListeners(event); + const ctx = this.eventContextFactory.createContext(parentContext); this.doEmit(ctx, event, args); return hasListener; } - doOnceEmit(event: E, args: Arguments) { + private doOnceEmit(event: EventName, args: Array) { try { this.emitter.emit(event, ...args); } catch (e) { - e.message = `[EventBus] process once event ${event} failed: ${e.message}`; + e.message = `[EventBus] process once event ${String(event)} failed: ${e.message}`; this.logger.error(e); } } - async doEmit(ctx: EggContext, event: E, args: Arguments) { - const lifecycle = {}; - if (ctx.init) { - await ctx.init(lifecycle); - } - try { - const handlers = await this.eventHandlerFactory.getHandlers(event, ctx); - await Promise.all(handlers.map(async handler => { - try { - await Reflect.apply(handler.handle, handler, args); - } catch (e) { - // should wait all handlers done then destroy ctx - e.message = `[EventBus] process event ${event} failed: ${e.message}`; - this.logger.error(e); + private async doEmit(ctx: EggContext, event: EventName, args: Array) { + await ContextHandler.run(ctx, async () => { + const lifecycle = {}; + if (ctx.init) { + await ctx.init(lifecycle); + } + try { + const handlers = await this.eventHandlerFactory.getHandlers(event); + await Promise.all(handlers.map(async handler => { + try { + await Reflect.apply(handler.handle, handler, args); + } catch (e) { + // should wait all handlers done then destroy ctx + e.message = `[EventBus] process event ${String(event)} failed: ${e.message}`; + this.logger.error(e); + } + })); + } catch (e) { + e.message = `[EventBus] process event ${String(event)} failed: ${e.message}`; + this.logger.error(e); + } finally { + if (ctx.destroy) { + ctx.destroy(lifecycle).catch(e => { + e.message = '[tegg/SingletonEventBus] destroy tegg ctx failed:' + e.message; + this.logger.error(e); + }); } - })); - } catch (e) { - e.message = `[EventBus] process event ${event} failed: ${e.message}`; - this.logger.error(e); - } finally { - if (ctx.destroy) { - ctx.destroy(lifecycle).catch(e => { - e.message = '[tegg/SingletonEventBus] destroy tegg ctx failed:' + e.message; - this.logger.error(e); - }); } - } - this.doOnceEmit(event, args); + this.doOnceEmit(event, args); + }); } } diff --git a/core/eventbus-runtime/test/EventBus.test.ts b/core/eventbus-runtime/test/EventBus.test.ts index dc11e4eb..abcd467d 100644 --- a/core/eventbus-runtime/test/EventBus.test.ts +++ b/core/eventbus-runtime/test/EventBus.test.ts @@ -1,40 +1,20 @@ import path from 'path'; import mm from 'mm'; -import { EggContainerFactory, EggContext, LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; -import { EggLoadUnitType, EggPrototype, LoadUnitFactory } from '@eggjs/tegg-metadata'; +import assert from 'assert'; +import { LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; +import { EggPrototype, LoadUnitFactory } from '@eggjs/tegg-metadata'; +import { TimerUtil } from '@eggjs/tegg-common-util'; import { HelloHandler, HelloProducer } from './fixtures/modules/event/HelloEvent'; -import { EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; -import { LoaderFactory } from '../../loader'; -import { EggTestContext } from '../../test-util'; +import { PrototypeUtil } from '@eggjs/core-decorator'; +import { EventInfoUtil, CORK_ID } from '@eggjs/eventbus-decorator'; +import { CoreTestHelper, EggTestContext } from '@eggjs/module-test-util'; import { EventContextFactory, EventHandlerFactory, SingletonEventBus } from '..'; -import { EventInfoUtil } from '@eggjs/eventbus-decorator'; -import assert from 'assert'; import { Timeout0Handler, Timeout100Handler, TimeoutProducer } from './fixtures/modules/event/MultiEvent'; describe('test/EventBus.test.ts', () => { - async function getLoadUnitInstance(moduleDir: string): Promise { - const loader = LoaderFactory.createLoader(moduleDir, EggLoadUnitType.MODULE); - const loadUnit = await LoadUnitFactory.createLoadUnit(moduleDir, EggLoadUnitType.MODULE, loader); - return await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); - } - - async function prepareModules(moduleDirs: string[]): Promise> { - const instances: Array = []; - for (const moduleDir of moduleDirs) { - instances.push(await getLoadUnitInstance(moduleDir)); - } - return instances; - } - - async function getObject(clazz: EggProtoImplClass, ctx?: EggContext): Promise { - const proto = PrototypeUtil.getClazzProto(clazz as any) as EggPrototype; - const eggObj = await EggContainerFactory.getOrCreateEggObject(proto, proto.name, ctx); - return eggObj.obj as unknown as T; - } - let modules: Array; beforeEach(async () => { - modules = await prepareModules([ + modules = await CoreTestHelper.prepareModules([ path.join(__dirname, 'fixtures/modules/mock-module'), path.join(__dirname, '..'), path.join(__dirname, 'fixtures/modules/event'), @@ -49,76 +29,146 @@ describe('test/EventBus.test.ts', () => { }); it('should work', async () => { - const ctx = new EggTestContext(); - const eventContextFactory = await getObject(EventContextFactory); - eventContextFactory.registerContextCreator(() => { - return ctx; - }); - const eventHandlerFactory = await getObject(EventHandlerFactory); - eventHandlerFactory.registerHandler( - EventInfoUtil.getEventName(HelloHandler)!, - PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); - - const eventBus = await getObject(SingletonEventBus); - const helloProducer = await getObject(HelloProducer); - const helloHandler = await getObject(HelloHandler, ctx); - const helloEvent = eventBus.await('hello'); - let msg: string | undefined; - mm(helloHandler, 'handle', async m => { - msg = m; - }); - helloProducer.trigger(); + await EggTestContext.mockContext(async (ctx: EggTestContext) => { + const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); + eventContextFactory.registerContextCreator(() => { + return ctx; + }); + const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); + eventHandlerFactory.registerHandler( + EventInfoUtil.getEventName(HelloHandler)!, + PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); - await helloEvent; - assert(msg === '01'); - assert(msg); + const eventBus = await CoreTestHelper.getObject(SingletonEventBus); + const helloProducer = await CoreTestHelper.getObject(HelloProducer); + const helloHandler = await CoreTestHelper.getObject(HelloHandler); + const helloEvent = eventBus.await('hello'); + let msg: string | undefined; + mm(helloHandler, 'handle', async (m: string) => { + msg = m; + }); + helloProducer.trigger(); + + await helloEvent; + assert(msg === '01'); + }); }); it('destroy should be called', async () => { - const ctx = new EggTestContext(); - let destroyCalled = false; - mm(ctx, 'destroy', async () => { - destroyCalled = true; + await EggTestContext.mockContext(async (ctx: EggTestContext) => { + let destroyCalled = false; + mm(ctx, 'destroy', async () => { + destroyCalled = true; + }); + const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); + eventContextFactory.registerContextCreator(() => { + return ctx; + }); + const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); + eventHandlerFactory.registerHandler( + EventInfoUtil.getEventName(HelloHandler)!, + PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); + + const eventBus = await CoreTestHelper.getObject(SingletonEventBus); + const helloProducer = await CoreTestHelper.getObject(HelloProducer); + const helloEvent = eventBus.await('hello'); + + helloProducer.trigger(); + + await helloEvent; + assert(destroyCalled); }); - const eventContextFactory = await getObject(EventContextFactory); - eventContextFactory.registerContextCreator(() => { - return ctx; + }); + + it('should wait all handler done', async () => { + await EggTestContext.mockContext(async (ctx: EggTestContext) => { + const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); + eventContextFactory.registerContextCreator(() => { + return ctx; + }); + const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); + eventHandlerFactory.registerHandler( + EventInfoUtil.getEventName(Timeout0Handler)!, + PrototypeUtil.getClazzProto(Timeout0Handler) as EggPrototype); + eventHandlerFactory.registerHandler( + EventInfoUtil.getEventName(Timeout100Handler)!, + PrototypeUtil.getClazzProto(Timeout100Handler) as EggPrototype); + + const eventBus = await CoreTestHelper.getObject(SingletonEventBus); + const timeoutProducer = await CoreTestHelper.getObject(TimeoutProducer); + const timeoutEvent = eventBus.await('timeout'); + timeoutProducer.trigger(); + + await timeoutEvent; + assert(Timeout100Handler.called); }); - const eventHandlerFactory = await getObject(EventHandlerFactory); - eventHandlerFactory.registerHandler( - EventInfoUtil.getEventName(HelloHandler)!, - PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); + }); + + it('cork should work', async () => { + await EggTestContext.mockContext(async (ctx: EggTestContext) => { + const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); + eventContextFactory.registerContextCreator(() => { + return ctx; + }); + const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); + eventHandlerFactory.registerHandler( + EventInfoUtil.getEventName(HelloHandler)!, + PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); - const eventBus = await getObject(SingletonEventBus); - const helloProducer = await getObject(HelloProducer); - const helloEvent = eventBus.await('hello'); + const eventBus = await CoreTestHelper.getObject(SingletonEventBus); + const corkId = eventBus.generateCorkId(); + ctx.set(CORK_ID, corkId); + eventBus.cork(corkId); - helloProducer.trigger(); + const helloHandler = await CoreTestHelper.getObject(HelloHandler); + const helloEvent = eventBus.await('hello'); + let eventTime = 0; + mm(helloHandler, 'handle', async () => { + eventTime = Date.now(); + }); + eventBus.emitWithContext(ctx, 'hello', [ '01' ]); + const triggerTime = Date.now(); - await helloEvent; - assert(destroyCalled === true); + await TimerUtil.sleep(100); + eventBus.uncork(corkId); + await helloEvent; + assert(eventTime >= triggerTime + 100); + }); }); - it('should wait all handler done', async () => { - const ctx = new EggTestContext(); - const eventContextFactory = await getObject(EventContextFactory); - eventContextFactory.registerContextCreator(() => { - return ctx; + it('multi cork should work', async () => { + await EggTestContext.mockContext(async (ctx: EggTestContext) => { + const eventContextFactory = await CoreTestHelper.getObject(EventContextFactory); + eventContextFactory.registerContextCreator(() => { + return ctx; + }); + const eventHandlerFactory = await CoreTestHelper.getObject(EventHandlerFactory); + eventHandlerFactory.registerHandler( + EventInfoUtil.getEventName(HelloHandler)!, + PrototypeUtil.getClazzProto(HelloHandler) as EggPrototype); + + const eventBus = await CoreTestHelper.getObject(SingletonEventBus); + const corkId = eventBus.generateCorkId(); + ctx.set(CORK_ID, corkId); + eventBus.cork(corkId); + eventBus.cork(corkId); + + const helloHandler = await CoreTestHelper.getObject(HelloHandler); + const helloEvent = eventBus.await('hello'); + let eventTime = 0; + mm(helloHandler, 'handle', async () => { + eventTime = Date.now(); + }); + eventBus.emitWithContext(ctx, 'hello', [ '01' ]); + const triggerTime = Date.now(); + + await TimerUtil.sleep(100); + eventBus.uncork(corkId); + await TimerUtil.sleep(100); + eventBus.uncork(corkId); + + await helloEvent; + assert(eventTime >= triggerTime + 200); }); - const eventHandlerFactory = await getObject(EventHandlerFactory); - eventHandlerFactory.registerHandler( - EventInfoUtil.getEventName(Timeout0Handler)!, - PrototypeUtil.getClazzProto(Timeout0Handler) as EggPrototype); - eventHandlerFactory.registerHandler( - EventInfoUtil.getEventName(Timeout100Handler)!, - PrototypeUtil.getClazzProto(Timeout100Handler) as EggPrototype); - - const eventBus = await getObject(SingletonEventBus); - const timeoutProducer = await getObject(TimeoutProducer); - const timeoutEvent = eventBus.await('timeout'); - timeoutProducer.trigger(); - - await timeoutEvent; - assert(Timeout100Handler.called === true); }); }); diff --git a/core/eventbus-runtime/test/fixtures/modules/event/MultiEvent.ts b/core/eventbus-runtime/test/fixtures/modules/event/MultiEvent.ts index 520811e8..8beb98ae 100644 --- a/core/eventbus-runtime/test/fixtures/modules/event/MultiEvent.ts +++ b/core/eventbus-runtime/test/fixtures/modules/event/MultiEvent.ts @@ -1,6 +1,6 @@ import { Event, EventBus } from '@eggjs/eventbus-decorator'; import { AccessLevel, Inject, SingletonProto } from '@eggjs/core-decorator'; -import sleep from 'mz-modules/sleep'; +import { TimerUtil } from '@eggjs/tegg-common-util'; import type { EggLogger } from 'egg'; declare module '@eggjs/eventbus-decorator' { @@ -36,7 +36,7 @@ export class Timeout100Handler { private readonly logger: EggLogger; async handle() { - await sleep(100); + await TimerUtil.sleep(100); // access logger, ensure context still alive this.logger.info('timeout 100'); Timeout100Handler.called = true; diff --git a/core/eventbus-runtime/test/fixtures/modules/mock-module/MockLogger.ts b/core/eventbus-runtime/test/fixtures/modules/mock-module/MockLogger.ts index ff68c675..ef029d66 100644 --- a/core/eventbus-runtime/test/fixtures/modules/mock-module/MockLogger.ts +++ b/core/eventbus-runtime/test/fixtures/modules/mock-module/MockLogger.ts @@ -1,4 +1,4 @@ -import { AccessLevel, SingletonProto } from '@eggjs/core-decorator'; +import { AccessLevel, ContextProto, SingletonProto } from '@eggjs/core-decorator'; @SingletonProto({ accessLevel: AccessLevel.PUBLIC, @@ -14,3 +14,19 @@ export class MockLogger { } } } + + +@ContextProto({ + accessLevel: AccessLevel.PUBLIC, + name: 'logger', +}) +export class MockContextLogger { + constructor() { + const methods = Object.keys(console); + for (const method of methods) { + this[method] = (...args) => { + console[method](...args); + } + } + } +} diff --git a/core/eventbus-runtime/tsconfig.json b/core/eventbus-runtime/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/eventbus-runtime/tsconfig.json +++ b/core/eventbus-runtime/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/lifecycle/.autod.conf.js b/core/lifecycle/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/lifecycle/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/lifecycle/CHANGELOG.md b/core/lifecycle/CHANGELOG.md index 069bfce6..94732127 100644 --- a/core/lifecycle/CHANGELOG.md +++ b/core/lifecycle/CHANGELOG.md @@ -3,6 +3,164 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-lifecycle + + + + + # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/tegg-lifecycle diff --git a/core/lifecycle/index.ts b/core/lifecycle/index.ts index dc8d4a1e..5ac490bf 100644 --- a/core/lifecycle/index.ts +++ b/core/lifecycle/index.ts @@ -2,3 +2,4 @@ export * from './src/LifecycleHook'; export * from './src/EggObjectLifecycle'; export * from './src/LifycycleUtil'; export * from './src/IdenticalObject'; +export * from './src/decorator'; diff --git a/core/lifecycle/package.json b/core/lifecycle/package.json index 52c82be7..84b0366d 100644 --- a/core/lifecycle/package.json +++ b/core/lifecycle/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-lifecycle", - "version": "1.0.0", + "version": "3.23.0", "description": "tegg lifecycle definition", "keywords": [ "egg", @@ -15,12 +15,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "ci": "npm run lint && npm run cov", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -38,5 +37,18 @@ }, "publishConfig": { "access": "public" + }, + "dependencies": { + "@eggjs/core-decorator": "^3.23.0" + }, + "devDependencies": { + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/lifecycle/src/EggObjectLifecycle.ts b/core/lifecycle/src/EggObjectLifecycle.ts index a09f1d2a..42260e9c 100644 --- a/core/lifecycle/src/EggObjectLifecycle.ts +++ b/core/lifecycle/src/EggObjectLifecycle.ts @@ -1,3 +1,5 @@ +import type { EggObject, EggObjectLifeCycleContext } from '@eggjs/tegg-runtime'; + /** * lifecycle hook interface for egg object */ @@ -5,30 +7,30 @@ export interface EggObjectLifecycle { /** * call after construct */ - postConstruct?(): Promise; + postConstruct?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * call before inject deps */ - preInject?(): Promise; + preInject?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * call after inject deps */ - postInject?(): Promise; + postInject?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * before object is ready */ - init?(): Promise; + init?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * call before destroy */ - preDestroy?(): Promise; + preDestroy?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; /** * destroy the object */ - destroy?(): Promise; + destroy?(ctx: EggObjectLifeCycleContext, eggObj: EggObject): Promise; } diff --git a/core/lifecycle/src/LifycycleUtil.ts b/core/lifecycle/src/LifycycleUtil.ts index 782124fd..34fdd9e7 100644 --- a/core/lifecycle/src/LifycycleUtil.ts +++ b/core/lifecycle/src/LifycycleUtil.ts @@ -1,4 +1,9 @@ +import { EggProtoImplClass, MetadataUtil } from '@eggjs/core-decorator'; +import type { EggPrototype } from '@eggjs/tegg-metadata'; import { LifecycleContext, LifecycleHook, LifecycleObject } from './LifecycleHook'; +import { EggObjectLifecycle } from './EggObjectLifecycle'; + +export type LifecycleHookName = keyof EggObjectLifecycle; export class LifecycleUtil> { @@ -28,6 +33,10 @@ export class LifecycleUtil[] { if (this.objLifecycleSet.has(obj.id)) { return Array.from(this.objLifecycleSet.get(obj.id)!); @@ -76,4 +85,14 @@ export class LifecycleUtil(LIFECYCLE_HOOK); + } } diff --git a/core/lifecycle/src/decorator/index.ts b/core/lifecycle/src/decorator/index.ts new file mode 100644 index 00000000..0923c63b --- /dev/null +++ b/core/lifecycle/src/decorator/index.ts @@ -0,0 +1,18 @@ +import { EggProtoImplClass } from '@eggjs/core-decorator'; +import { LifecycleUtil, LifecycleHookName } from '../LifycycleUtil'; + +function createLifecycle(hookName: LifecycleHookName) { + return () => { + return function(target: object, methodName: string) { + const clazz = target.constructor as EggProtoImplClass; + LifecycleUtil.setLifecycleHook(methodName, hookName, clazz); + }; + }; +} + +export const LifecyclePostConstruct = createLifecycle('postConstruct'); +export const LifecyclePreInject = createLifecycle('preInject'); +export const LifecyclePostInject = createLifecycle('postInject'); +export const LifecycleInit = createLifecycle('init'); +export const LifecyclePreDestroy = createLifecycle('preDestroy'); +export const LifecycleDestroy = createLifecycle('destroy'); diff --git a/core/loader/.autod.conf.js b/core/loader/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/loader/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/loader/CHANGELOG.md b/core/loader/CHANGELOG.md index 4ca283e2..35123e5d 100644 --- a/core/loader/CHANGELOG.md +++ b/core/loader/CHANGELOG.md @@ -3,6 +3,207 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + + +### Bug Fixes + +* loader should not deps metadata ([#94](https://github.com/eggjs/tegg/issues/94)) ([ff57de4](https://github.com/eggjs/tegg/commit/ff57de4f3e0d0dc33d77d05a887242fcb4c32024)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + +**Note:** Version bump only for package @eggjs/tegg-loader + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/tegg-loader diff --git a/core/loader/package.json b/core/loader/package.json index 14279e7c..6c067fac 100644 --- a/core/loader/package.json +++ b/core/loader/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-loader", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg default loader implement", "keywords": [ "egg", @@ -15,11 +15,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -36,13 +36,21 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-metadata": "^1.4.0", - "globby": "^10.0.2", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "globby": "^11.1.0", "is-type-of": "^1.2.1" }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@eggjs/tegg-metadata": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/loader/src/LoaderFactory.ts b/core/loader/src/LoaderFactory.ts index c3cb3828..f4e4d3be 100644 --- a/core/loader/src/LoaderFactory.ts +++ b/core/loader/src/LoaderFactory.ts @@ -1,4 +1,4 @@ -import { EggLoadUnitTypeLike, Loader } from '@eggjs/tegg-metadata'; +import type { EggLoadUnitTypeLike, Loader } from '@eggjs/tegg-metadata'; export type LoaderCreator = (unitPath: string) => Loader; diff --git a/core/loader/src/LoaderUtil.ts b/core/loader/src/LoaderUtil.ts index 2a1dd9ee..c2032dad 100644 --- a/core/loader/src/LoaderUtil.ts +++ b/core/loader/src/LoaderUtil.ts @@ -56,7 +56,8 @@ export class LoaderUtil { const exportNames = Object.keys(exports); for (const exportName of exportNames) { const clazz = exports[exportName]; - if (!is.class(clazz) || !PrototypeUtil.isEggPrototype(clazz)) { + const isEggProto = is.class(clazz) && (PrototypeUtil.isEggPrototype(clazz) || PrototypeUtil.isEggMultiInstancePrototype(clazz)); + if (!isEggProto) { continue; } clazzList.push(clazz); diff --git a/core/loader/src/impl/ModuleLoader.ts b/core/loader/src/impl/ModuleLoader.ts index 2b4e458d..219aa1bf 100644 --- a/core/loader/src/impl/ModuleLoader.ts +++ b/core/loader/src/impl/ModuleLoader.ts @@ -5,7 +5,7 @@ import { LoaderUtil } from '../LoaderUtil'; import { EggProtoImplClass } from '@eggjs/core-decorator'; import { LoaderFactory } from '../LoaderFactory'; -import { EggLoadUnitType, Loader } from '@eggjs/tegg-metadata'; +import type { Loader } from '@eggjs/tegg-metadata'; export class ModuleLoader implements Loader { private readonly moduleDir: string; @@ -40,4 +40,4 @@ export class ModuleLoader implements Loader { } } -LoaderFactory.registerLoader(EggLoadUnitType.MODULE, ModuleLoader.createModuleLoader); +LoaderFactory.registerLoader('MODULE', ModuleLoader.createModuleLoader); diff --git a/core/loader/tsconfig.json b/core/loader/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/loader/tsconfig.json +++ b/core/loader/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/metadata/.autod.conf.js b/core/metadata/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/metadata/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/metadata/CHANGELOG.md b/core/metadata/CHANGELOG.md index e0afc891..582dd0a1 100644 --- a/core/metadata/CHANGELOG.md +++ b/core/metadata/CHANGELOG.md @@ -3,6 +3,221 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + + +### Features + +* add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + + +### Bug Fixes + +* fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-metadata + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) diff --git a/core/metadata/index.ts b/core/metadata/index.ts index f7adfd33..da61c6b0 100644 --- a/core/metadata/index.ts +++ b/core/metadata/index.ts @@ -7,6 +7,8 @@ export * from './src/model/LoadUnit'; export * from './src/model/Loader'; export * from './src/errors'; export * from './src/util/ClassUtil'; +export * from './src/impl/LoadUnitMultiInstanceProtoHook'; +export * from './src/model/AppGraph'; import './src/impl/ModuleLoadUnit'; import './src/impl/EggPrototypeBuilder'; diff --git a/core/metadata/package.json b/core/metadata/package.json index fefcfcb3..6bc99fa5 100644 --- a/core/metadata/package.json +++ b/core/metadata/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-metadata", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg metadata", "keywords": [ "egg", @@ -15,10 +15,10 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -35,13 +35,20 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-lifecycle": "^1.0.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", "egg-errors": "^2.2.3" }, "devDependencies": { - "is-type-of": "^1.2.1" + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "globby": "^11.1.0", + "is-type-of": "^1.2.1", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/core/metadata/src/factory/EggPrototypeCreatorFactory.ts b/core/metadata/src/factory/EggPrototypeCreatorFactory.ts index 31c1a4a7..a3efb42f 100644 --- a/core/metadata/src/factory/EggPrototypeCreatorFactory.ts +++ b/core/metadata/src/factory/EggPrototypeCreatorFactory.ts @@ -1,4 +1,4 @@ -import { EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; +import { EggProtoImplClass, EggPrototypeInfo, PrototypeUtil } from '@eggjs/core-decorator'; import { LoadUnit } from '../model/LoadUnit'; import { EggPrototype, EggPrototypeLifecycleContext, EggPrototypeLifecycleUtil } from '../model/EggPrototype'; @@ -15,24 +15,47 @@ export class EggPrototypeCreatorFactory { return this.creatorMap.get(type); } - static async createProto(clazz: EggProtoImplClass, loadUnit: LoadUnit): Promise { - const property = PrototypeUtil.getProperty(clazz)!; - const creator = this.getPrototypeCreator(property.protoImplType); - if (!creator) { - throw new Error(`not found proto creator for type: ${property.protoImplType}`); + static async createProto(clazz: EggProtoImplClass, loadUnit: LoadUnit): Promise { + let properties: EggPrototypeInfo[] = []; + if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { + const multiInstanceProtoInfo = PrototypeUtil.getMultiInstanceProperty(clazz, { + unitPath: loadUnit.unitPath, + })!; + for (const obj of multiInstanceProtoInfo.objects) { + properties.push({ + name: obj.name, + protoImplType: multiInstanceProtoInfo.protoImplType, + initType: multiInstanceProtoInfo.initType, + accessLevel: multiInstanceProtoInfo.accessLevel, + qualifiers: obj.qualifiers, + className: multiInstanceProtoInfo.className, + }); + } + } else { + properties = [ PrototypeUtil.getProperty(clazz)! ]; } - const ctx: EggPrototypeLifecycleContext = { - clazz, - loadUnit, - }; - const proto = creator(ctx); - // TODO release egg prototype - await EggPrototypeLifecycleUtil.objectPreCreate(ctx, proto); - if (proto.init) { - await proto.init(ctx); + const protos: EggPrototype[] = []; + for (const property of properties) { + const creator = this.getPrototypeCreator(property.protoImplType); + if (!creator) { + throw new Error(`not found proto creator for type: ${property.protoImplType}`); + } + const ctx: EggPrototypeLifecycleContext = { + clazz, + loadUnit, + prototypeInfo: property, + }; + const proto = creator(ctx); + // TODO release egg prototype + await EggPrototypeLifecycleUtil.objectPreCreate(ctx, proto); + if (proto.init) { + await proto.init(ctx); + } + await EggPrototypeLifecycleUtil.objectPostCreate(ctx, proto); + PrototypeUtil.setClazzProto(clazz, proto); + protos.push(proto); } - await EggPrototypeLifecycleUtil.objectPostCreate(ctx, proto); - PrototypeUtil.setClazzProto(clazz, proto); - return proto; + return protos; + } } diff --git a/core/metadata/src/factory/LoadUnitFactory.ts b/core/metadata/src/factory/LoadUnitFactory.ts index 562d1022..124747e1 100644 --- a/core/metadata/src/factory/LoadUnitFactory.ts +++ b/core/metadata/src/factory/LoadUnitFactory.ts @@ -48,6 +48,7 @@ export class LoadUnitFactory { } finally { this.loadUnitMap.delete(loadUnit.unitPath); this.loadUnitIdMap.delete(loadUnit.id); + LoadUnitLifecycleUtil.clearObjectLifecycle(loadUnit); } } diff --git a/core/metadata/src/impl/EggPrototypeBuilder.ts b/core/metadata/src/impl/EggPrototypeBuilder.ts index 5bd11b1b..d10798bd 100644 --- a/core/metadata/src/impl/EggPrototypeBuilder.ts +++ b/core/metadata/src/impl/EggPrototypeBuilder.ts @@ -4,16 +4,16 @@ import { EggProtoImplClass, EggPrototypeName, InitTypeQualifierAttribute, ObjectInitTypeLike, PrototypeUtil, - QualifierInfo, QualifierUtil, ObjectInitType, - DEFAULT_PROTO_IMPL_TYPE } from '@eggjs/core-decorator'; + QualifierInfo, QualifierUtil, + DEFAULT_PROTO_IMPL_TYPE, ObjectInitType, +} from '@eggjs/core-decorator'; import { LoadUnit } from '../model/LoadUnit'; import { EggPrototype, EggPrototypeLifecycleContext, InjectObjectProto } from '../model/EggPrototype'; import { EggPrototypeFactory } from '../factory/EggPrototypeFactory'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; -import { FrameworkErrorFormater } from 'egg-errors'; import { EggPrototypeImpl } from '../impl/EggPrototypeImpl'; import { EggPrototypeCreatorFactory } from '../factory/EggPrototypeCreatorFactory'; -import { MultiPrototypeFound, IncompatibleProtoInject } from '../errors'; +import { EggPrototypeNotFound, MultiPrototypeFound } from '../errors'; export interface InjectObject { /** @@ -40,56 +40,81 @@ export class EggPrototypeBuilder { private injectObjects: Array = []; private loadUnit: LoadUnit; private qualifiers: QualifierInfo[] = []; + private className?: string; static create(ctx: EggPrototypeLifecycleContext): EggPrototype { const { clazz, loadUnit } = ctx; const filepath = PrototypeUtil.getFilePath(clazz); assert(filepath, 'not find filepath'); - let property = PrototypeUtil.getProperty(clazz); - assert(property, 'not find property'); - property = property!; const builder = new EggPrototypeBuilder(); builder.clazz = clazz; - builder.name = property.name; - builder.initType = property.initType; - builder.accessLevel = property.accessLevel; + builder.name = ctx.prototypeInfo.name; + builder.className = ctx.prototypeInfo.className; + builder.initType = ctx.prototypeInfo.initType; + builder.accessLevel = ctx.prototypeInfo.accessLevel; builder.filepath = filepath!; builder.injectObjects = PrototypeUtil.getInjectObjects(clazz) || []; builder.loadUnit = loadUnit; - builder.qualifiers = QualifierUtil.getProtoQualifiers(clazz); + builder.qualifiers = [ + ...QualifierUtil.getProtoQualifiers(clazz), + ...(ctx.prototypeInfo.qualifiers ?? []), + ]; return builder.build(); } + private tryFindDefaultPrototype(injectObject: InjectObject): EggPrototype { + const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); + return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers); + } + + private tryFindContextPrototype(injectObject: InjectObject): EggPrototype { + let propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); + propertyQualifiers = [ + ...propertyQualifiers, + { + attribute: InitTypeQualifierAttribute, + value: ObjectInitType.CONTEXT, + }, + ]; + return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers); + } + + private tryFindSelfInitTypePrototype(injectObject: InjectObject): EggPrototype { + let propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); + propertyQualifiers = [ + ...propertyQualifiers, + { + attribute: InitTypeQualifierAttribute, + value: this.initType, + }, + ]; + return EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers); + } + + private findInjectObjectPrototype(injectObject: InjectObject): EggPrototype { + const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); + try { + return this.tryFindDefaultPrototype(injectObject); + } catch (e) { + if (!(e instanceof MultiPrototypeFound && !propertyQualifiers.find(t => t.attribute === InitTypeQualifierAttribute))) { + throw e; + } + } + try { + return this.tryFindContextPrototype(injectObject); + } catch (e) { + if (!(e instanceof EggPrototypeNotFound)) { + throw e; + } + } + return this.tryFindSelfInitTypePrototype(injectObject); + } + public build(): EggPrototype { const injectObjectProtos: InjectObjectProto[] = []; for (const injectObject of this.injectObjects) { const propertyQualifiers = QualifierUtil.getProperQualifiers(this.clazz, injectObject.refName); - let proto: EggPrototype; - try { - proto = EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers); - } catch (e) { - // If multi proto found and property has no init type qualifier - // try use self init type as init type qualifier - if ( - e instanceof MultiPrototypeFound - && !propertyQualifiers.find(t => t.attribute === InitTypeQualifierAttribute) - ) { - propertyQualifiers.push({ - attribute: InitTypeQualifierAttribute, - value: this.initType, - }); - proto = EggPrototypeFactory.instance.getPrototype(injectObject.objName, this.loadUnit, propertyQualifiers); - } else { - throw e; - } - } - - // throw when try to inject ContextProto into singleton - if (this.initType === ObjectInitType.SINGLETON && proto.initType === ObjectInitType.CONTEXT) { - const err = new IncompatibleProtoInject(`can not inject ContextProto(${String(proto.name)}) in SingletonProto(${String(this.name)})`); - throw FrameworkErrorFormater.formatError(err); - } - + const proto = this.findInjectObjectPrototype(injectObject); injectObjectProtos.push({ refName: injectObject.refName, objName: injectObject.objName, @@ -108,6 +133,7 @@ export class EggPrototypeBuilder { injectObjectProtos, this.loadUnit.id, this.qualifiers, + this.className, ); } } diff --git a/core/metadata/src/impl/EggPrototypeImpl.ts b/core/metadata/src/impl/EggPrototypeImpl.ts index e2183698..5548c045 100644 --- a/core/metadata/src/impl/EggPrototypeImpl.ts +++ b/core/metadata/src/impl/EggPrototypeImpl.ts @@ -4,7 +4,7 @@ import { EggProtoImplClass, EggPrototypeName, MetaDataKey, MetadataUtil, ObjectInitTypeLike, - QualifierInfo, + QualifierInfo, QualifierValue, } from '@eggjs/core-decorator'; import { Id } from '@eggjs/tegg-lifecycle'; @@ -20,6 +20,7 @@ export class EggPrototypeImpl implements EggPrototype { readonly accessLevel: AccessLevel; readonly injectObjects: InjectObjectProto[]; readonly loadUnitId: Id; + readonly className?: string; constructor( id: string, @@ -31,6 +32,7 @@ export class EggPrototypeImpl implements EggPrototype { injectObjectMap: InjectObjectProto[], loadUnitId: Id, qualifiers: QualifierInfo[], + className?: string, ) { this.id = id; this.clazz = clazz; @@ -41,6 +43,7 @@ export class EggPrototypeImpl implements EggPrototype { this.injectObjects = injectObjectMap; this.loadUnitId = loadUnitId; this.qualifiers = qualifiers; + this.className = className; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { @@ -57,6 +60,10 @@ export class EggPrototypeImpl implements EggPrototype { return selfQualifiers?.value === qualifier.value; } + getQualifier(attribute: string): QualifierValue | undefined { + return this.qualifiers.find(t => t.attribute === attribute)?.value; + } + constructEggObject(): object { return Reflect.construct(this.clazz, []); } diff --git a/core/metadata/src/impl/LoadUnitMultiInstanceProtoHook.ts b/core/metadata/src/impl/LoadUnitMultiInstanceProtoHook.ts new file mode 100644 index 00000000..ba3c7a3a --- /dev/null +++ b/core/metadata/src/impl/LoadUnitMultiInstanceProtoHook.ts @@ -0,0 +1,28 @@ +import { LifecycleHook } from '@eggjs/tegg-lifecycle'; +import { EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; +import { + EggPrototypeCreatorFactory, EggPrototypeFactory, + LoadUnit, + LoadUnitLifecycleContext, +} from '@eggjs/tegg-metadata'; + +export class LoadUnitMultiInstanceProtoHook implements LifecycleHook { + multiInstanceClazzSet: Set = new Set(); + + async preCreate(ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { + const clazzList = ctx.loader.load(); + const multiInstanceClazzList = Array.from(this.multiInstanceClazzSet); + for (const clazz of clazzList) { + if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { + this.multiInstanceClazzSet.add(clazz); + } + } + for (const clazz of multiInstanceClazzList) { + const protos = await EggPrototypeCreatorFactory.createProto(clazz, loadUnit); + for (const proto of protos) { + EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); + } + } + } +} + diff --git a/core/metadata/src/impl/ModuleLoadUnit.ts b/core/metadata/src/impl/ModuleLoadUnit.ts index 9d9c6130..5f020cfd 100644 --- a/core/metadata/src/impl/ModuleLoadUnit.ts +++ b/core/metadata/src/impl/ModuleLoadUnit.ts @@ -32,13 +32,14 @@ class ProtoNode implements GraphNodeObj { readonly qualifiers: QualifierInfo[]; readonly initType: ObjectInitTypeLike; - constructor(clazz: EggProtoImplClass) { - const property = PrototypeUtil.getProperty(clazz)!; - this.name = property.name; + constructor(clazz: EggProtoImplClass, objName: EggPrototypeName, unitPath: string) { + this.name = objName; this.id = '' + (id++); this.clazz = clazz; this.qualifiers = QualifierUtil.getProtoQualifiers(clazz); - this.initType = property.initType; + this.initType = PrototypeUtil.getInitType(clazz, { + unitPath, + })!; } verifyQualifiers(qualifiers: QualifierInfo[]): boolean { @@ -63,10 +64,12 @@ class ProtoNode implements GraphNodeObj { export class ModuleGraph { private graph: Graph; clazzList: EggProtoImplClass[]; + readonly unitPath: string; - constructor(clazzList: EggProtoImplClass[]) { + constructor(clazzList: EggProtoImplClass[], unitPath: string) { this.clazzList = clazzList; this.graph = new Graph(); + this.unitPath = unitPath; this.build(); } @@ -96,7 +99,15 @@ export class ModuleGraph { } private build() { - const protoGraphNodes = this.clazzList.map(t => new GraphNode(new ProtoNode(t))); + const protoGraphNodes: GraphNode[] = []; + for (const clazz of this.clazzList) { + const objNames = PrototypeUtil.getObjNames(clazz, { + unitPath: this.unitPath, + }); + for (const objName of objNames) { + protoGraphNodes.push(new GraphNode(new ProtoNode(clazz, objName, this.unitPath))); + } + } for (const node of protoGraphNodes) { if (!this.graph.addVertex(node)) { throw new Error(`duplicate proto: ${node.val.id}`); @@ -120,7 +131,11 @@ export class ModuleGraph { if (loopPath) { throw new Error('proto has recursive deps: ' + loopPath); } - this.clazzList = this.graph.sort().map(t => t.val.clazz); + const clazzSet = new Set(); + for (const clazz of this.graph.sort()) { + clazzSet.add(clazz.val.clazz); + } + this.clazzList = Array.from(clazzSet); } } @@ -144,10 +159,11 @@ export class ModuleLoadUnit implements LoadUnit { private loadClazz(): EggProtoImplClass[] { const clazzList = this.loader.load(); for (const clazz of clazzList) { - const property = PrototypeUtil.getProperty(clazz)!; const defaultQualifier = [{ attribute: InitTypeQualifierAttribute, - value: property.initType, + value: PrototypeUtil.getInitType(clazz, { + unitPath: this.unitPath, + })!, }, { attribute: LoadUnitNameQualifierAttribute, value: this.name, @@ -161,12 +177,14 @@ export class ModuleLoadUnit implements LoadUnit { async init() { const clazzList = this.loadClazz(); - const protoGraph = new ModuleGraph(clazzList); + const protoGraph = new ModuleGraph(clazzList, this.unitPath); protoGraph.sort(); this.clazzList = protoGraph.clazzList; for (const clazz of this.clazzList) { - const proto = await EggPrototypeCreatorFactory.createProto(clazz, this); - EggPrototypeFactory.instance.registerPrototype(proto, this); + const protos = await EggPrototypeCreatorFactory.createProto(clazz, this); + for (const proto of protos) { + EggPrototypeFactory.instance.registerPrototype(proto, this); + } } } diff --git a/core/metadata/src/model/AppGraph.ts b/core/metadata/src/model/AppGraph.ts new file mode 100644 index 00000000..461a5516 --- /dev/null +++ b/core/metadata/src/model/AppGraph.ts @@ -0,0 +1,254 @@ +import assert from 'node:assert'; +import util from 'node:util'; +import { Graph, GraphNode, GraphNodeObj, ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util'; +import { + AccessLevel, + EggProtoImplClass, + EggPrototypeName, + INIT_TYPE_TRY_ORDER, + InitTypeQualifierAttribute, + LoadUnitNameQualifierAttribute, + PrototypeUtil, + QualifierInfo, + QualifierUtil, +} from '@eggjs/core-decorator'; + +export interface InstanceClazzMeta { + name: PropertyKey; + qualifiers: QualifierInfo[]; + accessLevel: AccessLevel, + instanceModule: GraphNode; + ownerModule: GraphNode; +} + +export type ClazzMetaMap = Record; + +function verifyQualifier(clazzQualifiers: QualifierInfo[], qualifier: QualifierInfo) { + const selfQualifiers = clazzQualifiers.find(t => t.attribute === qualifier.attribute); + return selfQualifiers?.value === qualifier.value; +} + +function verifyQualifiers(clazzQualifiers: QualifierInfo[], qualifiers: QualifierInfo[]) { + for (const qualifier of qualifiers) { + if (!verifyQualifier(clazzQualifiers, qualifier)) { + return false; + } + } + return true; +} + +export class ClazzMap { + private clazzMap: ClazzMetaMap; + + constructor(graph: Graph) { + this.build(graph); + } + + private build(graph: Graph) { + /** + * 1. iterate all module get all MultiInstanceClazz + * 2. iterate MultiInstanceClazz and all module get object meta + * 3. iterate object meta and build clazz map + */ + const clazzMap: ClazzMetaMap = {}; + for (const ownerNode of graph.nodes.values()) { + for (const clazz of ownerNode.val.getClazzList()) { + const qualifiers = QualifierUtil.getProtoQualifiers(clazz); + if (PrototypeUtil.isEggMultiInstancePrototype(clazz)) { + for (const instanceNode of graph.nodes.values()) { + const property = PrototypeUtil.getMultiInstanceProperty(clazz, { + unitPath: instanceNode.val.moduleConfig.path, + }); + assert(property, `multi instance property not found for ${clazz.name}`); + for (const info of property.objects) { + clazzMap[info.name] = clazzMap[info.name] || []; + clazzMap[info.name].push({ + name: info.name, + accessLevel: PrototypeUtil.getAccessLevel(clazz, { + unitPath: instanceNode.val.moduleConfig.path, + }) as AccessLevel, + qualifiers: [ + ...qualifiers, + ...info.qualifiers, + ], + instanceModule: instanceNode, + ownerModule: ownerNode, + }); + } + } + } else { + const property = PrototypeUtil.getProperty(clazz); + assert(property, `property not found for ${clazz.name}`); + clazzMap[property.name] = clazzMap[property.name] || []; + clazzMap[property.name].push({ + name: property.name, + accessLevel: PrototypeUtil.getAccessLevel(clazz, { + unitPath: ownerNode.val.moduleConfig.path, + }) as AccessLevel, + qualifiers, + ownerModule: ownerNode, + instanceModule: ownerNode, + }); + } + } + } + this.clazzMap = clazzMap; + } + + findDependencyModule(objName: EggPrototypeName, properQualifiers: QualifierInfo[], intoModule: GraphNode): GraphNode[] { + const result: Set> = new Set(); + const objInfo = this.clazzMap[objName]; + if (!objInfo) { + return []; + } + let mayObjs = objInfo.filter(obj => { + // 1. check accessLevel + if (obj.instanceModule !== intoModule && obj.accessLevel === AccessLevel.PRIVATE) { + return false; + } + // 2. check qualifier + return verifyQualifiers(obj.qualifiers, properQualifiers); + }); + + // 3. auto set init type qualifier + if (mayObjs.length > 1) { + const initTypeQualifiers = INIT_TYPE_TRY_ORDER.map(type => ({ + attribute: InitTypeQualifierAttribute, + value: type, + })); + for (const initTypeQualifier of initTypeQualifiers) { + const mayInitTypeObjs = mayObjs.filter(obj => { + return verifyQualifiers(obj.qualifiers, [ + ...properQualifiers, + initTypeQualifier, + ]); + }); + if (mayInitTypeObjs.length > 0) { + mayObjs = mayInitTypeObjs; + } + } + } + + // 4. auto set load unit name qualifier + if (mayObjs.length > 1) { + const moduleNameQualifiers = { + attribute: LoadUnitNameQualifierAttribute, + value: intoModule.val.name, + }; + const mayLoadUnitNameObjs = mayObjs.filter(obj => { + return verifyQualifiers(obj.qualifiers, [ + ...properQualifiers, + moduleNameQualifiers, + ]); + }); + if (mayLoadUnitNameObjs.length > 0) { + mayObjs = mayLoadUnitNameObjs; + } + } + + if (mayObjs.length > 1) { + const message = util.format('multi class found for %s@%o in module %j', + objName, + properQualifiers, + mayObjs.map(t => { + return t.instanceModule.val.moduleConfig.path; + })); + throw new Error(message); + } + + for (const obj of mayObjs) { + result.add(obj.instanceModule); + result.add(obj.ownerModule); + } + return Array.from(result); + } +} + +export class ModuleNode implements GraphNodeObj { + readonly id: string; + readonly name: string; + readonly moduleConfig: ModuleReference; + private readonly clazzList: EggProtoImplClass[]; + + constructor(moduleConfig: ModuleReference) { + this.moduleConfig = moduleConfig; + this.id = moduleConfig.path; + this.name = ModuleConfigUtil.readModuleNameSync(moduleConfig.path); + this.clazzList = []; + } + + addClazz(clazz: EggProtoImplClass) { + if (!this.clazzList.includes(clazz)) { + this.clazzList.push(clazz); + } + const defaultQualifier = [{ + attribute: InitTypeQualifierAttribute, + value: PrototypeUtil.getInitType(clazz, { + unitPath: this.moduleConfig.path, + })!, + }, { + attribute: LoadUnitNameQualifierAttribute, + value: this.name, + }]; + for (const qualifier of defaultQualifier) { + QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value); + } + } + + toString() { + return `${this.name}@${this.moduleConfig.path}`; + } + + getClazzList(): readonly EggProtoImplClass[] { + return this.clazzList; + } +} + +export class AppGraph { + private graph: Graph; + private clazzMap: ClazzMap; + moduleConfigList: Array; + + constructor() { + this.graph = new Graph(); + } + + addNode(moduleNode: ModuleNode) { + if (!this.graph.addVertex(new GraphNode(moduleNode))) { + throw new Error(`duplicate module: ${moduleNode}`); + } + } + + build() { + this.clazzMap = new ClazzMap(this.graph); + + // 1. iterate all modules + for (const node of this.graph.nodes.values()) { + // 2. iterate all class + for (const clazz of node.val.getClazzList()) { + const injectObjects = PrototypeUtil.getInjectObjects(clazz); + // 3. iterate all inject objects + for (const injectObject of injectObjects) { + const properQualifiers = QualifierUtil.getProperQualifiers(clazz, injectObject.refName); + // 4. find dependency module + const dependencyModules = this.clazzMap.findDependencyModule(injectObject.objName, properQualifiers, node); + for (const moduleNode of dependencyModules) { + // 5. add edge + if (node !== moduleNode) { + this.graph.addEdge(node, moduleNode); + } + } + } + } + } + } + + sort() { + const loopPath = this.graph.loopPath(); + if (loopPath) { + throw new Error('module has recursive deps: ' + loopPath); + } + this.moduleConfigList = this.graph.sort() + .map(t => t.val.moduleConfig); + } +} diff --git a/core/metadata/src/model/EggPrototype.ts b/core/metadata/src/model/EggPrototype.ts index 280b0884..83a38ec0 100644 --- a/core/metadata/src/model/EggPrototype.ts +++ b/core/metadata/src/model/EggPrototype.ts @@ -4,7 +4,7 @@ import { AccessLevel, ObjectInitTypeLike, MetaDataKey, - EggProtoImplClass, + EggProtoImplClass, EggPrototypeInfo, QualifierValue, QualifierAttribute, } from '@eggjs/core-decorator'; import { LifecycleObject, LifecycleContext, LifecycleUtil } from '@eggjs/tegg-lifecycle'; import { LoadUnit } from './LoadUnit'; @@ -32,6 +32,7 @@ export type EggPrototypeClass = new (...args: any[]) => EggPrototype; export interface EggPrototypeLifecycleContext extends LifecycleContext { clazz: EggProtoImplClass; + prototypeInfo: EggPrototypeInfo; loadUnit: LoadUnit; } @@ -44,6 +45,7 @@ export interface EggPrototype extends LifecycleObject { @@ -16,9 +17,10 @@ describe('test/LoadUnit/LoadUnit.test.ts', () => { const appRepoProto = loadUnit.getEggPrototype('appRepo', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON }]); const sprintRepoProto = loadUnit.getEggPrototype('sprintRepo', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON }]); const userRepoProto = loadUnit.getEggPrototype('userRepo', [{ attribute: InitTypeQualifierAttribute, value: ObjectInitType.SINGLETON }]); - assert(appRepoProto); - assert(sprintRepoProto); - assert(userRepoProto); + assert.strictEqual(appRepoProto.length, 1); + assert.strictEqual(appRepoProto[0].className, 'AppRepo'); + assert.strictEqual(sprintRepoProto.length, 1); + assert.strictEqual(userRepoProto.length, 1); await LoadUnitFactory.destroyLoadUnit(loadUnit); }); @@ -57,18 +59,6 @@ describe('test/LoadUnit/LoadUnit.test.ts', () => { assert(e.message.includes('faq/TEGG_MULTI_PROTO_FOUND')); } }); - - it('should init failed with incompatilble proto injection', async () => { - const invalidateModulePath = path.join(__dirname, './fixtures/modules/incompatible-proto-inject'); - const loader = new TestLoader(invalidateModulePath); - try { - await LoadUnitFactory.createLoadUnit(invalidateModulePath, EggLoadUnitType.MODULE, loader); - throw new Error('should throw error'); - } catch (e) { - assert(e.message.includes('can not inject ContextProto(logger) in SingletonProto(base)')); - assert(e.message.includes('faq/TEGG_INCOMPATIBLE_PROTO_INJECT')); - } - }); }); describe('try use obj init type as property init type qualifier', () => { @@ -76,8 +66,52 @@ describe('test/LoadUnit/LoadUnit.test.ts', () => { const sameObjectModulePath = path.join(__dirname, './fixtures/modules/same-name-object'); const loader = new TestLoader(sameObjectModulePath); const loadUnit = await LoadUnitFactory.createLoadUnit(sameObjectModulePath, EggLoadUnitType.MODULE, loader); - const countServiceProto = loadUnit.getEggPrototype('countService', []); + const countServiceProto = loadUnit.getEggPrototype('countService', [])[0]; assert(countServiceProto); }); + + it('should use context proto first', async () => { + const sameObjectModulePath = path.join(__dirname, './fixtures/modules/same-name-object'); + const loader = new TestLoader(sameObjectModulePath); + const loadUnit = await LoadUnitFactory.createLoadUnit(sameObjectModulePath, EggLoadUnitType.MODULE, loader); + const singletonProto = loadUnit.getEggPrototype('singletonCountService', [])[0]; + assert(singletonProto); + const injectProto = singletonProto.injectObjects.find(t => t.objName === 'appCache'); + assert(injectProto); + assert(injectProto.proto.initType === ObjectInitType.CONTEXT); + }); + }); + + describe('MultiInstance proto', () => { + it('should load static work', async () => { + const multiInstanceModule = path.join(__dirname, './fixtures/modules/multi-instance-module'); + const loader = new TestLoader(multiInstanceModule); + const loadUnit = await LoadUnitFactory.createLoadUnit(multiInstanceModule, EggLoadUnitType.MODULE, loader); + assert(loadUnit.id === 'LOAD_UNIT:multiInstanceModule'); + assert(loadUnit.unitPath === multiInstanceModule); + const foo1Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo1' }]); + const foo2Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo2' }]); + assert(foo1Prototype); + assert(foo1Prototype.length === 1); + assert.strictEqual(foo1Prototype[0].className, 'FooLogger'); + assert(foo2Prototype); + assert(foo2Prototype.length === 1); + await LoadUnitFactory.destroyLoadUnit(loadUnit); + }); + + it('should load callback work', async () => { + const multiCallbackInstanceModule = path.join(__dirname, './fixtures/modules/multi-callback-instance-module'); + const loader = new TestLoader(multiCallbackInstanceModule); + const loadUnit = await LoadUnitFactory.createLoadUnit(multiCallbackInstanceModule, EggLoadUnitType.MODULE, loader); + assert(loadUnit.id === 'LOAD_UNIT:multiCallbackInstanceModule'); + assert(loadUnit.unitPath === multiCallbackInstanceModule); + const foo1Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'foo' }]); + const foo2Prototype = loadUnit.getEggPrototype('foo', [{ attribute: FOO_ATTRIBUTE, value: 'bar' }]); + assert(foo1Prototype); + assert(foo1Prototype.length === 1); + assert(foo2Prototype); + assert(foo2Prototype.length === 1); + await LoadUnitFactory.destroyLoadUnit(loadUnit); + }); }); }); diff --git a/core/metadata/test/ModuleGraph.test.ts b/core/metadata/test/ModuleGraph.test.ts index 48af76f9..8085c2df 100644 --- a/core/metadata/test/ModuleGraph.test.ts +++ b/core/metadata/test/ModuleGraph.test.ts @@ -7,7 +7,7 @@ describe('test/ModuleGraph.test.ts', () => { const modulePath = path.join(__dirname, './fixtures/modules/extends-module'); const loader = new TestLoader(modulePath); const clazzList = loader.load(); - const graph = new ModuleGraph(clazzList); + const graph = new ModuleGraph(clazzList, modulePath); graph.sort(); }); }); diff --git a/core/metadata/test/fixtures/LoaderUtil.ts b/core/metadata/test/fixtures/LoaderUtil.ts index 82cbc3c4..b46a7a4c 100644 --- a/core/metadata/test/fixtures/LoaderUtil.ts +++ b/core/metadata/test/fixtures/LoaderUtil.ts @@ -14,7 +14,8 @@ export class LoaderUtil { const exportNames = Object.keys(exports); for (const exportName of exportNames) { const clazz = exports[exportName]; - if (!is.class(clazz) || !PrototypeUtil.isEggPrototype(clazz)) { + const isEggProto = is.class(clazz) && (PrototypeUtil.isEggPrototype(clazz) || PrototypeUtil.isEggMultiInstancePrototype(clazz)); + if (!isEggProto) { continue; } PrototypeUtil.setFilePath(clazz, filePath); diff --git a/core/metadata/test/fixtures/TestLoader.ts b/core/metadata/test/fixtures/TestLoader.ts index 2b0a15c3..96d089a1 100644 --- a/core/metadata/test/fixtures/TestLoader.ts +++ b/core/metadata/test/fixtures/TestLoader.ts @@ -14,7 +14,7 @@ export class TestLoader implements Loader { load(): EggProtoImplClass[] { const protoClassList: EggProtoImplClass[] = []; - const files = globby.sync([ '**/*' ], { cwd: this.moduleDir }); + const files = globby.sync([ '**/*.(js|ts)' ], { cwd: this.moduleDir }); for (const file of files) { const realPath = path.join(this.moduleDir, file); const protoClazz = LoaderUtil.loadFile(realPath); diff --git a/core/metadata/test/fixtures/modules/extends-module/Base.ts b/core/metadata/test/fixtures/modules/extends-module/Base.ts index d4bd2c49..9af2a4a4 100644 --- a/core/metadata/test/fixtures/modules/extends-module/Base.ts +++ b/core/metadata/test/fixtures/modules/extends-module/Base.ts @@ -14,9 +14,3 @@ export class Base { export class Foo extends Base { } - -@ContextProto() -export class Bar extends Base { - @Inject() - foo: Foo; -} diff --git a/core/metadata/test/fixtures/modules/multi-callback-instance-module/MultiInstance.ts b/core/metadata/test/fixtures/modules/multi-callback-instance-module/MultiInstance.ts new file mode 100644 index 00000000..37ff70fc --- /dev/null +++ b/core/metadata/test/fixtures/modules/multi-callback-instance-module/MultiInstance.ts @@ -0,0 +1,29 @@ +import { + AccessLevel, + ObjectInitType, + MultiInstanceProto, + MultiInstancePrototypeGetObjectsContext, +} from '@eggjs/core-decorator'; +import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; + +export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); + +@MultiInstanceProto({ + accessLevel: AccessLevel.PUBLIC, + initType: ObjectInitType.SINGLETON, + getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { + const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath); + return (config as any).features.logger.map(name => { + return { + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: name, + }], + } + }); + }, +}) +export class FooDynamicLogger { + +} diff --git a/core/metadata/test/fixtures/modules/multi-callback-instance-module/module.yml b/core/metadata/test/fixtures/modules/multi-callback-instance-module/module.yml new file mode 100644 index 00000000..eb532ee2 --- /dev/null +++ b/core/metadata/test/fixtures/modules/multi-callback-instance-module/module.yml @@ -0,0 +1,4 @@ +features: + logger: + - foo + - bar diff --git a/core/metadata/test/fixtures/modules/multi-callback-instance-module/package.json b/core/metadata/test/fixtures/modules/multi-callback-instance-module/package.json new file mode 100644 index 00000000..3120ad3f --- /dev/null +++ b/core/metadata/test/fixtures/modules/multi-callback-instance-module/package.json @@ -0,0 +1,6 @@ +{ + "name": "multi-callback-instance-module", + "eggModule": { + "name": "multiCallbackInstanceModule" + } +} diff --git a/core/metadata/test/fixtures/modules/multi-instance-module/MultiInstance.ts b/core/metadata/test/fixtures/modules/multi-instance-module/MultiInstance.ts new file mode 100644 index 00000000..356a9606 --- /dev/null +++ b/core/metadata/test/fixtures/modules/multi-instance-module/MultiInstance.ts @@ -0,0 +1,25 @@ +import { AccessLevel, ObjectInitType, MultiInstanceProto } from '@eggjs/core-decorator'; + + +export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); + +@MultiInstanceProto({ + accessLevel: AccessLevel.PUBLIC, + initType: ObjectInitType.SINGLETON, + objects: [{ + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo1', + }], + }, { + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo2', + }], + }], +}) +export class FooLogger { + +} diff --git a/core/metadata/test/fixtures/modules/multi-instance-module/package.json b/core/metadata/test/fixtures/modules/multi-instance-module/package.json new file mode 100644 index 00000000..7c7ca109 --- /dev/null +++ b/core/metadata/test/fixtures/modules/multi-instance-module/package.json @@ -0,0 +1,6 @@ +{ + "name": "multi-instance-module", + "eggModule": { + "name": "multiInstanceModule" + } +} diff --git a/core/metadata/test/fixtures/modules/same-name-object/CountService.ts b/core/metadata/test/fixtures/modules/same-name-object/CountService.ts index d987b9ee..de7c2598 100644 --- a/core/metadata/test/fixtures/modules/same-name-object/CountService.ts +++ b/core/metadata/test/fixtures/modules/same-name-object/CountService.ts @@ -1,8 +1,18 @@ -import { ContextProto, Inject } from '@eggjs/core-decorator'; +import { ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; import { AppCache } from './AppCache'; @ContextProto() -export default class CountService { +export class CountService { + @Inject() + appCache: AppCache; + + async getCount(): Promise { + return this.appCache.getCount(); + } +} + +@SingletonProto() +export class SingletonCountService { @Inject() appCache: AppCache; diff --git a/core/metadata/tsconfig.json b/core/metadata/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/metadata/tsconfig.json +++ b/core/metadata/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/orm-decorator/.autod.conf.js b/core/orm-decorator/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/orm-decorator/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/orm-decorator/CHANGELOG.md b/core/orm-decorator/CHANGELOG.md index 84a2e01b..6ab95e30 100644 --- a/core/orm-decorator/CHANGELOG.md +++ b/core/orm-decorator/CHANGELOG.md @@ -3,6 +3,241 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + + +### Features + +* use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + + +### Features + +* export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-orm-decorator + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* use getMetaData for ModelMetadataUtil ([#44](https://github.com/eggjs/tegg/issues/44)) ([87a306c](https://github.com/eggjs/tegg/commit/87a306c4fba51fd519a47c0caaa79442643ea107)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) + + + + + ## [1.4.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-decorator@1.4.0...@eggjs/tegg-orm-decorator@1.4.1) (2022-07-20) diff --git a/core/orm-decorator/README.md b/core/orm-decorator/README.md index f773ca24..003be63a 100644 --- a/core/orm-decorator/README.md +++ b/core/orm-decorator/README.md @@ -24,10 +24,10 @@ export class App extends Bone { ## Use Model ```ts -import { ContextProto, Inject } from '@eggjs/tegg'; +import { SingletonProto, Inject } from '@eggjs/tegg'; import { App } from './model/App'; -@ContextProto() +@SingletonProto() export class AppService { @Inject() App: typeof App; diff --git a/core/orm-decorator/package.json b/core/orm-decorator/package.json index cd69cad8..cfcde046 100644 --- a/core/orm-decorator/package.json +++ b/core/orm-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-orm-decorator", - "version": "1.5.0", + "version": "3.23.0", "description": "tegg orm decorator", "main": "dist/index.js", "files": [ @@ -15,11 +15,11 @@ "tegg" ], "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -36,9 +36,9 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-metadata": "^1.4.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", "lodash": "^4.17.21", "pluralize": "^8.0.0" }, @@ -47,5 +47,13 @@ }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/orm-decorator/src/decorator/Model.ts b/core/orm-decorator/src/decorator/Model.ts index 3fecf9dc..a0bfb22a 100644 --- a/core/orm-decorator/src/decorator/Model.ts +++ b/core/orm-decorator/src/decorator/Model.ts @@ -1,8 +1,9 @@ -import { AccessLevel, ContextProto, EggProtoImplClass } from '@eggjs/core-decorator'; +import { AccessLevel, EggProtoImplClass, SingletonProto } from '@eggjs/core-decorator'; import { ModelInfoUtil } from '../util/ModelInfoUtil'; export interface ModelParams { tableName?: string; + dataSource?: string; } export const MODEL_PROTO_IMPL_TYPE = 'MODEL_PROTO'; @@ -10,7 +11,7 @@ export const MODEL_PROTO_IMPL_TYPE = 'MODEL_PROTO'; export function Model(param?: ModelParams) { return function(clazz: EggProtoImplClass) { ModelInfoUtil.setIsModel(true, clazz); - const func = ContextProto({ + const func = SingletonProto({ name: clazz.name, accessLevel: AccessLevel.PUBLIC, protoImplType: MODEL_PROTO_IMPL_TYPE, @@ -18,6 +19,9 @@ export function Model(param?: ModelParams) { if (param?.tableName) { ModelInfoUtil.setTableName(param.tableName, clazz); } + if (param?.dataSource) { + ModelInfoUtil.setDataSource(param.dataSource, clazz); + } func(clazz); }; } diff --git a/core/orm-decorator/tsconfig.json b/core/orm-decorator/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/orm-decorator/tsconfig.json +++ b/core/orm-decorator/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/runtime/.autod.conf.js b/core/runtime/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/core/runtime/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/core/runtime/CHANGELOG.md b/core/runtime/CHANGELOG.md index f8335738..d05c4414 100644 --- a/core/runtime/CHANGELOG.md +++ b/core/runtime/CHANGELOG.md @@ -3,6 +3,251 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + + +### Features + +* add helper to get EggObject from class ([#148](https://github.com/eggjs/tegg/issues/148)) ([77eaf38](https://github.com/eggjs/tegg/commit/77eaf38383ad974b30d13f4c30c489fb7fa7274d)) + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + + +### Bug Fixes + +* fix contextEggObjectGetProperty conflict ([#105](https://github.com/eggjs/tegg/issues/105)) ([c570315](https://github.com/eggjs/tegg/commit/c570315ece6ef7443ecf3df2b45aa8c934a5aa38)) + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + + +### Bug Fixes + +* inject property should be configurable ([#85](https://github.com/eggjs/tegg/issues/85)) ([c13ab55](https://github.com/eggjs/tegg/commit/c13ab55d7b483a5c4a6e4293a6095aa98d070a8b)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + + +### Bug Fixes + +* fix nest inject ctx obj to singleton obj ([#74](https://github.com/eggjs/tegg/issues/74)) ([e4b6252](https://github.com/eggjs/tegg/commit/e4b6252aa79925e16185e568bf7b220f367253ab)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-runtime + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) diff --git a/core/runtime/index.ts b/core/runtime/index.ts index e348297d..10aeb3fa 100644 --- a/core/runtime/index.ts +++ b/core/runtime/index.ts @@ -8,6 +8,7 @@ export * from './src/factory/EggContainerFactory'; export * from './src/factory/EggObjectFactory'; export * from './src/factory/LoadUnitInstanceFactory'; export * from './src/impl/ModuleLoadUnitInstance'; +export * from './src/model/ContextHandler'; import './src/impl/EggAlwaysNewObjectContainer'; import './src/impl/ModuleLoadUnitInstance'; diff --git a/core/runtime/package.json b/core/runtime/package.json index 3fe19187..ba0cec90 100644 --- a/core/runtime/package.json +++ b/core/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-runtime", - "version": "1.4.0", + "version": "3.23.0", "description": "tegg runtime", "main": "dist/index.js", "files": [ @@ -15,11 +15,11 @@ "tegg" ], "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -36,13 +36,20 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-metadata": "^1.4.0" + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0" }, "devDependencies": { - "@eggjs/tegg-loader": "^1.4.0" + "@eggjs/tegg-loader": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mm": "^3.2.1", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/core/runtime/src/factory/EggContainerFactory.ts b/core/runtime/src/factory/EggContainerFactory.ts index f877aaaa..2e2765fe 100644 --- a/core/runtime/src/factory/EggContainerFactory.ts +++ b/core/runtime/src/factory/EggContainerFactory.ts @@ -1,12 +1,12 @@ import { EggPrototype } from '@eggjs/tegg-metadata'; -import { EggContext } from '../model/EggContext'; import { EggContainer } from '../model/EggContainer'; import { LifecycleContext } from '@eggjs/tegg-lifecycle'; -import { EggObjectName, ObjectInitTypeLike } from '@eggjs/core-decorator'; +import { EggObjectName, ObjectInitTypeLike, PrototypeUtil, EggProtoImplClass } from '@eggjs/core-decorator'; import { EggObject } from '../model/EggObject'; +import { ContextHandler } from '../model/ContextHandler'; +import { ContextInitiator } from '../impl/ContextInitiator'; - -export type ContainerGetMethod = (proto: EggPrototype, ctx?: EggContext) => EggContainer; +export type ContainerGetMethod = (proto: EggPrototype) => EggContainer; export class EggContainerFactory { private static containerGetMethodMap: Map = new Map(); @@ -15,23 +15,47 @@ export class EggContainerFactory { this.containerGetMethodMap.set(initType, method); } - static getContainer(proto: EggPrototype, ctx?: EggContext): EggContainer { + static getContainer(proto: EggPrototype): EggContainer { const method = this.containerGetMethodMap.get(proto.initType); if (!method) { throw new Error(`InitType ${proto.initType} has not register ContainerGetMethod`); } - return method(proto, ctx); + return method(proto); } - static async getOrCreateEggObject(proto: EggPrototype, name?: EggObjectName, ctx?: EggContext): Promise { - const container = this.getContainer(proto, ctx); + /** + * get or create egg object + * If get singleton egg object in context, + * will create context egg object for it. + */ + static async getOrCreateEggObject(proto: EggPrototype, name?: EggObjectName): Promise { + const container = this.getContainer(proto); name = name || proto.name; - return container.getOrCreateEggObject(name, proto, ctx); + const obj = await container.getOrCreateEggObject(name, proto); + const ctx = ContextHandler.getContext(); + if (ctx) { + const initiator = ContextInitiator.createContextInitiator(ctx); + await initiator.init(obj); + } + return obj; + } + + /** + * get or create egg object from the Class + * If get singleton egg object in context, + * will create context egg object for it. + */ + static async getOrCreateEggObjectFromClazz(clazz: EggProtoImplClass, name?: EggObjectName): Promise { + const proto = PrototypeUtil.getClazzProto(clazz) as EggPrototype; + if (!proto) { + throw new Error(`can not get proto for clazz ${clazz.name}`); + } + return await this.getOrCreateEggObject(proto, name); } - static getEggObject(proto: EggPrototype, name?: EggObjectName, ctx?: EggContext): EggObject { - const container = this.getContainer(proto, ctx); + static getEggObject(proto: EggPrototype, name?: EggObjectName): EggObject { + const container = this.getContainer(proto); name = name || proto.name; - return container.getEggObject(name, proto, ctx); + return container.getEggObject(name, proto); } } diff --git a/core/runtime/src/factory/EggObjectFactory.ts b/core/runtime/src/factory/EggObjectFactory.ts index 3b6d9e8a..088c83bf 100644 --- a/core/runtime/src/factory/EggObjectFactory.ts +++ b/core/runtime/src/factory/EggObjectFactory.ts @@ -1,7 +1,6 @@ -import { EggObject, EggObjectLifeCycleContext } from '../model/EggObject'; +import { EggObject, EggObjectLifeCycleContext, EggObjectLifecycleUtil } from '../model/EggObject'; import { EggObjectName } from '@eggjs/core-decorator'; import { EggPrototype, EggPrototypeClass, LoadUnitFactory } from '@eggjs/tegg-metadata'; -import { EggContext } from '../model/EggContext'; import EggObjectImpl from '../impl/EggObjectImpl'; import { LoadUnitInstanceFactory } from './LoadUnitInstanceFactory'; @@ -10,7 +9,7 @@ interface EggObjectPair { ctx: EggObjectLifeCycleContext; } -export type CreateObjectMethod = (name: EggObjectName, proto: EggPrototype, lifecycleContext: EggObjectLifeCycleContext, ctx?: EggContext) => Promise; +export type CreateObjectMethod = (name: EggObjectName, proto: EggPrototype, lifecycleContext: EggObjectLifeCycleContext) => Promise; export class EggObjectFactory { static eggObjectMap: Map = new Map(); @@ -27,7 +26,7 @@ export class EggObjectFactory { return EggObjectImpl.createObject; } - static async createObject(name: EggObjectName, proto: EggPrototype, ctx?: EggContext): Promise { + static async createObject(name: EggObjectName, proto: EggPrototype): Promise { const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId); if (!loadUnit) { throw new Error(`not found load unit ${proto.loadUnitId}`); @@ -38,7 +37,7 @@ export class EggObjectFactory { loadUnitInstance: loadUnitInstance!, }; const method = this.getEggObjectCreateMethod(proto.constructor as EggPrototypeClass); - const args = [ name, proto, lifecycleContext, ctx ]; + const args = [ name, proto, lifecycleContext ]; const obj = await Reflect.apply(method, null, args); this.eggObjectMap.set(obj.id, { obj, ctx: lifecycleContext }); return obj; @@ -52,6 +51,7 @@ export class EggObjectFactory { } } finally { this.eggObjectMap.delete(obj.id); + EggObjectLifecycleUtil.clearObjectLifecycle(obj); } } } diff --git a/core/runtime/src/factory/LoadUnitInstanceFactory.ts b/core/runtime/src/factory/LoadUnitInstanceFactory.ts index b761957b..42bddebe 100644 --- a/core/runtime/src/factory/LoadUnitInstanceFactory.ts +++ b/core/runtime/src/factory/LoadUnitInstanceFactory.ts @@ -1,10 +1,13 @@ -import { LoadUnitInstance, LoadUnitInstanceLifecycleContext } from '../model/LoadUnitInstance'; +import { + LoadUnitInstance, + LoadUnitInstanceLifecycleContext, + LoadUnitInstanceLifecycleUtil, +} from '../model/LoadUnitInstance'; import { EggLoadUnitTypeLike, EggPrototype, LoadUnit } from '@eggjs/tegg-metadata'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { EggContainerFactory } from './EggContainerFactory'; import { ObjectInitType } from '@eggjs/core-decorator'; - type LoadUnitInstanceCreator = (ctx: LoadUnitInstanceLifecycleContext) => LoadUnitInstance; interface LoadUnitInstancePair { instance: LoadUnitInstance; @@ -48,10 +51,12 @@ export class LoadUnitInstanceFactory { static async destroyLoadUnitInstance(loadUnitInstance: LoadUnitInstance) { const { ctx } = this.instanceMap.get(loadUnitInstance.id)!; + await LoadUnitInstanceLifecycleUtil.objectPreDestroy(ctx, loadUnitInstance); if (loadUnitInstance.destroy) { await loadUnitInstance.destroy(ctx); } this.instanceMap.delete(loadUnitInstance.id); + LoadUnitInstanceLifecycleUtil.clearObjectLifecycle(loadUnitInstance); } static getLoadUnitInstanceByProto(proto: EggPrototype): LoadUnitInstance { diff --git a/core/runtime/src/impl/ContextInitiator.ts b/core/runtime/src/impl/ContextInitiator.ts new file mode 100644 index 00000000..9f59305a --- /dev/null +++ b/core/runtime/src/impl/ContextInitiator.ts @@ -0,0 +1,43 @@ +import { LoadUnitFactory } from '@eggjs/tegg-metadata'; +import { EggContext } from '../model/EggContext'; +import { EggObject } from '../model/EggObject'; +import { ContextObjectGraph } from './ContextObjectGraph'; +import { EggContainerFactory } from '../factory/EggContainerFactory'; + +const CONTEXT_INITIATOR = Symbol('EggContext#ContextInitiator'); + +export class ContextInitiator { + private readonly eggContext: EggContext; + private readonly eggObjectInitRecorder: WeakMap; + + constructor(eggContext: EggContext) { + this.eggContext = eggContext; + this.eggObjectInitRecorder = new WeakMap(); + this.eggContext.set(CONTEXT_INITIATOR, this); + } + + async init(obj: EggObject) { + if (this.eggObjectInitRecorder.get(obj) === true) { + return; + } + this.eggObjectInitRecorder.set(obj, true); + const injectObjectProtos = ContextObjectGraph.getContextProto(obj.proto); + await Promise.all(injectObjectProtos.map(async injectObject => { + const proto = injectObject.proto; + const loadUnit = LoadUnitFactory.getLoadUnitById(proto.loadUnitId); + if (!loadUnit) { + throw new Error(`can not find load unit: ${proto.loadUnitId}`); + } + await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName); + })); + } + + static createContextInitiator(context: EggContext): ContextInitiator { + let initiator = context.get(CONTEXT_INITIATOR); + if (!initiator) { + initiator = new ContextInitiator(context); + context.set(CONTEXT_INITIATOR, initiator); + } + return initiator; + } +} diff --git a/core/runtime/src/impl/ContextObjectGraph.ts b/core/runtime/src/impl/ContextObjectGraph.ts new file mode 100644 index 00000000..621c431c --- /dev/null +++ b/core/runtime/src/impl/ContextObjectGraph.ts @@ -0,0 +1,44 @@ +import { EggPrototype, InjectObjectProto } from '@eggjs/tegg-metadata'; +import { ObjectInitType } from '@eggjs/core-decorator'; + +class InjectProtoHolder { + private idSet: Set = new Set(); + private injectProtos: Array = []; + + addInjectProto(injectObjectProto: InjectObjectProto) { + const id = `${String(injectObjectProto.objName)}:${injectObjectProto.proto.id}`; + if (this.idSet.has(id)) { + return; + } + this.idSet.add(id); + this.injectProtos.push(injectObjectProto); + } + + dumpProtos(): Array { + return this.injectProtos; + } +} + +export class ContextObjectGraph { + private static eggObjectInitRecorder: WeakMap> = new WeakMap(); + + static getContextProto(proto: EggPrototype): InjectObjectProto[] { + if (ContextObjectGraph.eggObjectInitRecorder.has(proto)) { + return ContextObjectGraph.eggObjectInitRecorder.get(proto)!; + } + const holder = new InjectProtoHolder(); + this.doGetContextProto(proto, holder); + const injectObjectProtos = holder.dumpProtos(); + ContextObjectGraph.eggObjectInitRecorder.set(proto, injectObjectProtos); + return injectObjectProtos; + } + + private static doGetContextProto(proto: EggPrototype, holder: InjectProtoHolder) { + for (const injectObject of proto.injectObjects) { + if (injectObject.proto.initType === ObjectInitType.CONTEXT && proto.initType !== ObjectInitType.CONTEXT) { + holder.addInjectProto(injectObject); + } + ContextObjectGraph.doGetContextProto(injectObject.proto, holder); + } + } +} diff --git a/core/runtime/src/impl/EggAlwaysNewObjectContainer.ts b/core/runtime/src/impl/EggAlwaysNewObjectContainer.ts index 407238e6..ccd7fd8d 100644 --- a/core/runtime/src/impl/EggAlwaysNewObjectContainer.ts +++ b/core/runtime/src/impl/EggAlwaysNewObjectContainer.ts @@ -3,7 +3,6 @@ import { Id, LifecycleContext } from '@eggjs/tegg-lifecycle'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { EggObjectName, ObjectInitType } from '@eggjs/core-decorator'; import { EggObject } from '../model/EggObject'; -import { EggContext } from '../model/EggContext'; import { EggObjectFactory } from '../factory/EggObjectFactory'; import { EggContainerFactory } from '../factory/EggContainerFactory'; @@ -31,8 +30,8 @@ export class EggAlwaysNewObjectContainer implements EggContainer { - return EggObjectFactory.createObject(name, proto, ctx); + async getOrCreateEggObject(name: string, proto: EggPrototype): Promise { + return EggObjectFactory.createObject(name, proto); } async destroy(): Promise { diff --git a/core/runtime/src/impl/EggObjectImpl.ts b/core/runtime/src/impl/EggObjectImpl.ts index 1e7abdc6..b037f22f 100644 --- a/core/runtime/src/impl/EggObjectImpl.ts +++ b/core/runtime/src/impl/EggObjectImpl.ts @@ -1,25 +1,24 @@ import { EggObject, EggObjectLifeCycleContext, EggObjectLifecycleUtil, EggObjectStatus } from '../model/EggObject'; import { EggPrototype, LoadUnitFactory } from '@eggjs/tegg-metadata'; -import { EggContext } from '../model/EggContext'; -import { EggObjectName } from '@eggjs/core-decorator'; -import { EggObjectLifecycle, IdenticalUtil } from '@eggjs/tegg-lifecycle'; +import { EggObjectName, ObjectInitType } from '@eggjs/core-decorator'; +import { IdenticalUtil, EggObjectLifecycle } from '@eggjs/tegg-lifecycle'; import { EggContainerFactory } from '../factory/EggContainerFactory'; import { EggObjectUtil } from './EggObjectUtil'; +import { ContextHandler } from '../model/ContextHandler'; export default class EggObjectImpl implements EggObject { private _obj: object; private status: EggObjectStatus = EggObjectStatus.PENDING; readonly proto: EggPrototype; - readonly ctx?: EggContext; readonly name: EggObjectName; readonly id: string; - constructor(name: EggObjectName, proto: EggPrototype, ctx?: EggContext) { + constructor(name: EggObjectName, proto: EggPrototype) { this.name = name; this.proto = proto; - this.ctx = ctx; - this.id = IdenticalUtil.createObjectId(this.proto.id, this.ctx?.id); + const ctx = ContextHandler.getContext(); + this.id = IdenticalUtil.createObjectId(this.proto.id, ctx?.id); } async init(ctx: EggObjectLifeCycleContext) { @@ -35,12 +34,14 @@ export default class EggObjectImpl implements EggObject { // global hook await EggObjectLifecycleUtil.objectPreCreate(ctx, this); // self hook - if (objLifecycleHook.postConstruct) { - await objLifecycleHook.postConstruct(); + const postConstructMethod = EggObjectLifecycleUtil.getLifecycleHook('postConstruct', this.proto) ?? 'postConstruct'; + if (objLifecycleHook[postConstructMethod]) { + await objLifecycleHook[postConstructMethod](ctx, this); } - if (objLifecycleHook.preInject) { - await objLifecycleHook.preInject(); + const preInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('preInject', this.proto) ?? 'preInject'; + if (objLifecycleHook[preInjectMethod]) { + await objLifecycleHook[preInjectMethod](ctx, this); } await Promise.all(this.proto.injectObjects.map(async injectObject => { const proto = injectObject.proto; @@ -48,20 +49,26 @@ export default class EggObjectImpl implements EggObject { if (!loadUnit) { throw new Error(`can not find load unit: ${proto.loadUnitId}`); } - const injectObj = await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName, this.ctx); - this.injectProperty(injectObject.refName, EggObjectUtil.eggObjectGetProperty(injectObj)); + if (this.proto.initType !== ObjectInitType.CONTEXT && injectObject.proto.initType === ObjectInitType.CONTEXT) { + this.injectProperty(injectObject.refName, EggObjectUtil.contextEggObjectGetProperty(proto, injectObject.objName)); + } else { + const injectObj = await EggContainerFactory.getOrCreateEggObject(proto, injectObject.objName); + this.injectProperty(injectObject.refName, EggObjectUtil.eggObjectGetProperty(injectObj)); + } })); // global hook await EggObjectLifecycleUtil.objectPostCreate(ctx, this); // self hook - if (objLifecycleHook.postInject) { - await objLifecycleHook.postInject(); + const postInjectMethod = EggObjectLifecycleUtil.getLifecycleHook('postInject', this.proto) ?? 'postInject'; + if (objLifecycleHook[postInjectMethod]) { + await objLifecycleHook[postInjectMethod](ctx, this); } - if (objLifecycleHook.init) { - await objLifecycleHook.init(); + const initMethod = EggObjectLifecycleUtil.getLifecycleHook('init', this.proto) ?? 'init'; + if (objLifecycleHook[initMethod]) { + await objLifecycleHook[initMethod](ctx, this); } this.status = EggObjectStatus.READY; @@ -79,12 +86,14 @@ export default class EggObjectImpl implements EggObject { // self hook const objLifecycleHook = this._obj as EggObjectLifecycle; - if (objLifecycleHook.preDestroy) { - await objLifecycleHook.preDestroy(); + const preDestroyMethod = EggObjectLifecycleUtil.getLifecycleHook('preDestroy', this.proto) ?? 'preDestroy'; + if (objLifecycleHook[preDestroyMethod]) { + await objLifecycleHook[preDestroyMethod](ctx, this); } - if (objLifecycleHook.destroy) { - await objLifecycleHook.destroy(); + const destroyMethod = EggObjectLifecycleUtil.getLifecycleHook('destroy', this.proto) ?? 'destroy'; + if (objLifecycleHook[destroyMethod]) { + await objLifecycleHook[destroyMethod](ctx, this); } this.status = EggObjectStatus.DESTROYED; @@ -103,8 +112,8 @@ export default class EggObjectImpl implements EggObject { return this.status === EggObjectStatus.READY; } - static async createObject(name: EggObjectName, proto: EggPrototype, lifecycleContext: EggObjectLifeCycleContext, ctx?: EggContext): Promise { - const obj = new EggObjectImpl(name, proto, ctx); + static async createObject(name: EggObjectName, proto: EggPrototype, lifecycleContext: EggObjectLifeCycleContext): Promise { + const obj = new EggObjectImpl(name, proto); await obj.init(lifecycleContext); return obj; } diff --git a/core/runtime/src/impl/EggObjectUtil.ts b/core/runtime/src/impl/EggObjectUtil.ts index 5190d737..7fe87297 100644 --- a/core/runtime/src/impl/EggObjectUtil.ts +++ b/core/runtime/src/impl/EggObjectUtil.ts @@ -1,4 +1,6 @@ import { EggObject } from '../model/EggObject'; +import { EggContainerFactory } from '../factory/EggContainerFactory'; +import { EggPrototype } from '@eggjs/tegg-metadata'; export class EggObjectUtil { static eggObjectGetProperty(eggObject: EggObject): PropertyDescriptor { @@ -6,8 +8,23 @@ export class EggObjectUtil { get(): any { return eggObject.obj; }, - configurable: false, + configurable: true, enumerable: true, }; } + + static contextEggObjectGetProperty(proto: EggPrototype, objName: PropertyKey): PropertyDescriptor { + const PROTO_OBJ_GETTER = Symbol(`EggPrototype#objGetter#${String(objName)}`); + if (!proto[PROTO_OBJ_GETTER]) { + proto[PROTO_OBJ_GETTER] = { + get(): any { + const eggObject = EggContainerFactory.getEggObject(proto, objName); + return eggObject.obj; + }, + configurable: true, + enumerable: true, + }; + } + return proto[PROTO_OBJ_GETTER]; + } } diff --git a/core/runtime/src/impl/ModuleLoadUnitInstance.ts b/core/runtime/src/impl/ModuleLoadUnitInstance.ts index 7bf2faf6..d8e071a6 100644 --- a/core/runtime/src/impl/ModuleLoadUnitInstance.ts +++ b/core/runtime/src/impl/ModuleLoadUnitInstance.ts @@ -15,7 +15,7 @@ export class ModuleLoadUnitInstance implements LoadUnitInstance { readonly loadUnit: LoadUnit; readonly id: string; readonly name: string; - private protoToCreateMap: Map = new Map(); + private protoToCreateMap: [EggPrototypeName, EggPrototype][] = []; private eggObjectMap: Map> = new Map(); private eggObjectPromiseMap: Map>> = new Map(); @@ -25,22 +25,27 @@ export class ModuleLoadUnitInstance implements LoadUnitInstance { const iterator = this.loadUnit.iterateEggPrototype(); for (const proto of iterator) { if (proto.initType === ObjectInitType.SINGLETON) { - this.protoToCreateMap.set(proto.name, proto); + this.protoToCreateMap.push([ + proto.name, proto, + ]); } } this.id = IdenticalUtil.createLoadUnitInstanceId(loadUnit.id); } iterateProtoToCreate(): IterableIterator<[ EggObjectName, EggPrototype ]> { - return this.protoToCreateMap.entries(); + return this.protoToCreateMap[Symbol.iterator](); } addProtoToCreate(name: string, proto: EggPrototype) { - this.protoToCreateMap.set(name, proto); + this.protoToCreateMap.push([ name, proto ]); } deleteProtoToCreate(name: string) { - this.protoToCreateMap.delete(name); + const index = this.protoToCreateMap.findIndex(([ protoName ]) => protoName === name); + if (index !== -1) { + this.protoToCreateMap.splice(index, 1); + } } async init(ctx: LoadUnitInstanceLifecycleContext): Promise { @@ -51,14 +56,13 @@ export class ModuleLoadUnitInstance implements LoadUnitInstance { await LoadUnitInstanceLifecycleUtil.objectPostCreate(ctx, this); } - async destroy(ctx: LoadUnitInstanceLifecycleContext): Promise { - await LoadUnitInstanceLifecycleUtil.objectPreDestroy(ctx, this); - const objs: EggObject[] = Array.from(this.eggObjectMap.values()) - .map(protoObjMap => Array.from(protoObjMap.values())) - .reduce((p, c) => { - p = p.concat(c); - return p; - }, []); + async destroy(): Promise { + const objs: EggObject[] = []; + for (const protoObjMap of this.eggObjectMap.values()) { + for (const obj of protoObjMap.values()) { + objs.push(obj); + } + } this.eggObjectMap.clear(); await Promise.all(objs.map(async obj => { await EggObjectFactory.destroyObject(obj); diff --git a/core/runtime/src/model/AbstractEggContext.ts b/core/runtime/src/model/AbstractEggContext.ts index 3d3beeff..58c3517d 100644 --- a/core/runtime/src/model/AbstractEggContext.ts +++ b/core/runtime/src/model/AbstractEggContext.ts @@ -1,17 +1,19 @@ import { EggContext, EggContextLifecycleContext, EggContextLifecycleUtil } from './EggContext'; import { EggObjectName, EggPrototypeName, ObjectInitType } from '@eggjs/core-decorator'; -import { EggPrototype } from '@eggjs/tegg-metadata'; +import { EggPrototype, TeggError } from '@eggjs/tegg-metadata'; import { EggObject } from './EggObject'; import { Id } from '@eggjs/tegg-lifecycle'; import { MapUtil } from '@eggjs/tegg-common-util'; import { EggContainerFactory } from '../factory/EggContainerFactory'; import { EggObjectFactory } from '../factory/EggObjectFactory'; +import { ContextHandler } from './ContextHandler'; export abstract class AbstractEggContext implements EggContext { private contextData: Map = new Map(); private protoToCreate: Map = new Map(); private eggObjectMap: Map> = new Map(); private eggObjectPromiseMap: Map>> = new Map(); + private destroyed = false; abstract id: string; @@ -36,6 +38,8 @@ export abstract class AbstractEggContext implements EggContext { await EggObjectFactory.destroyObject(obj); })); this.contextData.clear(); + this.destroyed = true; + await EggContextLifecycleUtil.clearObjectLifecycle(this); } get(key: string | symbol): any | undefined { @@ -43,6 +47,9 @@ export abstract class AbstractEggContext implements EggContext { } getEggObject(name: EggPrototypeName, proto: EggPrototype): EggObject { + if (this.destroyed) { + throw TeggError.create(`Can not read property \`${String(name)}\` because egg ctx has been destroyed`, 'read_after_ctx_destroyed'); + } const protoObjMap = this.eggObjectMap.get(proto.id); if (!protoObjMap || !protoObjMap.has(name)) { @@ -51,12 +58,12 @@ export abstract class AbstractEggContext implements EggContext { return protoObjMap.get(name)!; } - async getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype, ctx?: EggContext): Promise { + async getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype): Promise { const protoObjMap = MapUtil.getOrStore(this.eggObjectMap, proto.id, new Map()); if (!protoObjMap.has(name)) { const protoObjPromiseMap = MapUtil.getOrStore(this.eggObjectPromiseMap, proto.id, new Map()); if (!protoObjPromiseMap.has(name)) { - const objPromise = EggObjectFactory.createObject(name, proto, ctx); + const objPromise = EggObjectFactory.createObject(name, proto); protoObjPromiseMap.set(name, objPromise); const obj = await objPromise; protoObjPromiseMap.delete(name); @@ -74,7 +81,7 @@ export abstract class AbstractEggContext implements EggContext { async init(ctx: EggContextLifecycleContext): Promise { await EggContextLifecycleUtil.objectPreCreate(ctx, this); for (const [ name, proto ] of this.protoToCreate) { - await this.getOrCreateEggObject(name, proto, this); + await this.getOrCreateEggObject(name, proto); } await EggContextLifecycleUtil.objectPostCreate(ctx, this); } @@ -88,7 +95,8 @@ export abstract class AbstractEggContext implements EggContext { } } -EggContainerFactory.registerContainerGetMethod(ObjectInitType.CONTEXT, (_: EggPrototype, ctx?: EggContext) => { +EggContainerFactory.registerContainerGetMethod(ObjectInitType.CONTEXT, () => { + const ctx = ContextHandler.getContext(); if (!ctx) { throw new Error('ctx is required'); } diff --git a/core/runtime/src/model/ContextHandler.ts b/core/runtime/src/model/ContextHandler.ts new file mode 100644 index 00000000..9e6cb1c0 --- /dev/null +++ b/core/runtime/src/model/ContextHandler.ts @@ -0,0 +1,19 @@ +import assert from 'assert'; +import { EggContext } from './EggContext'; + +type runInContextCallback = (context: EggContext, fn: () => Promise) => Promise; + +export class ContextHandler { + static getContextCallback: () => EggContext | undefined; + static runInContextCallback: runInContextCallback; + + static getContext(): EggContext | undefined { + assert(this.getContextCallback, 'getContextCallback not set'); + return this.getContextCallback ? this.getContextCallback() : undefined; + } + + static run(context: EggContext, fn: () => Promise): Promise { + assert(this.runInContextCallback, 'runInContextCallback not set'); + return this.runInContextCallback(context, fn); + } +} diff --git a/core/runtime/src/model/EggContainer.ts b/core/runtime/src/model/EggContainer.ts index d0dd3675..e1679510 100644 --- a/core/runtime/src/model/EggContainer.ts +++ b/core/runtime/src/model/EggContainer.ts @@ -1,7 +1,6 @@ import { LifecycleContext, LifecycleObject } from '@eggjs/tegg-lifecycle'; import { EggObjectName, EggPrototypeName } from '@eggjs/core-decorator'; import { EggPrototype } from '@eggjs/tegg-metadata'; -import { EggContext } from './EggContext'; import { EggObject } from './EggObject'; export interface EggContainer extends LifecycleObject { @@ -12,9 +11,9 @@ export interface EggContainer extends LifecycleObjec deleteProtoToCreate(name: EggPrototypeName); // async method for get or create object - getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype, ctx?: EggContext): Promise; + getOrCreateEggObject(name: EggPrototypeName, proto: EggPrototype): Promise; // sync method for get object // object should be created before get, or throw Error - getEggObject(name: EggPrototypeName, proto: EggPrototype, ctx?: EggContext): EggObject; + getEggObject(name: EggPrototypeName, proto: EggPrototype): EggObject; } diff --git a/core/runtime/src/model/LoadUnitInstance.ts b/core/runtime/src/model/LoadUnitInstance.ts index 52bdba5c..59dd720b 100644 --- a/core/runtime/src/model/LoadUnitInstance.ts +++ b/core/runtime/src/model/LoadUnitInstance.ts @@ -2,7 +2,6 @@ import { EggContainer } from './EggContainer'; import { LoadUnit } from '@eggjs/tegg-metadata'; import { LifecycleUtil } from '@eggjs/tegg-lifecycle'; - export interface LoadUnitInstanceLifecycleContext { loadUnit: LoadUnit; } diff --git a/core/runtime/test/EggObject.test.ts b/core/runtime/test/EggObject.test.ts index 30904c8b..44a95613 100644 --- a/core/runtime/test/EggObject.test.ts +++ b/core/runtime/test/EggObject.test.ts @@ -4,20 +4,38 @@ import { EggTestContext } from './fixtures/EggTestContext'; import TestUtil from './util'; import { EggContainerFactory } from '..'; import { Foo, Bar } from './fixtures/modules/lifecycle-hook/object'; +import { Bar as ExtendsBar } from './fixtures/modules/extends-module/Base'; +import mm from 'mm'; +import { ContextHandler } from '../src/model/ContextHandler'; +import { SingletonBar } from './fixtures/modules/inject-context-to-singleton/object'; describe('test/EggObject.test.ts', () => { + let ctx: EggTestContext; + + beforeEach(() => { + ctx = new EggTestContext(); + }); + + afterEach(() => { + mm.restore(); + }); describe('lifecycle', () => { + beforeEach(() => { + mm(ContextHandler, 'getContext', () => { + return ctx; + }); + }); + describe('context proto', () => { it('should work', async () => { const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); - const ctx = new EggTestContext(); const fooProto = EggPrototypeFactory.instance.getPrototype('foo'); - const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name, ctx); + const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name); const foo = fooObj.obj as Foo; - await ctx.destroy({}); await TestUtil.destroyLoadUnitInstance(instance); const called = foo.getLifecycleCalled(); + await ctx.destroy({}); assert.deepStrictEqual(called, [ 'construct', 'postConstruct', @@ -31,9 +49,8 @@ describe('test/EggObject.test.ts', () => { it('should clear eggObjectMap/eggObjectPromiseMap/contextData after destroy', async () => { const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); - const ctx = new EggTestContext(); const fooProto = EggPrototypeFactory.instance.getPrototype('foo'); - const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name, ctx); + const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name); assert(fooObj.obj); await ctx.destroy({}); await TestUtil.destroyLoadUnitInstance(instance); @@ -49,10 +66,14 @@ describe('test/EggObject.test.ts', () => { describe('singleton proto', () => { it('should work', async () => { const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); - const ctx = new EggTestContext(); const barProto = EggPrototypeFactory.instance.getPrototype('bar'); - const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name, ctx); + const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as Bar; + // get obj from class + const barObj2 = await EggContainerFactory.getOrCreateEggObjectFromClazz((barProto as any).clazz, barProto.name); + assert.equal(barObj2, barObj); + assert.equal(barObj2.obj, barObj.obj); + await TestUtil.destroyLoadUnitInstance(instance); const called = bar.getLifecycleCalled(); assert.deepStrictEqual(called, [ @@ -67,4 +88,44 @@ describe('test/EggObject.test.ts', () => { }); }); }); + + describe('inject context to singleton', () => { + it('should work', async () => { + mm(ContextHandler, 'getContext', () => { + return; + }); + const instance = await TestUtil.createLoadUnitInstance('inject-context-to-singleton'); + const barProto = EggPrototypeFactory.instance.getPrototype('singletonBar'); + mm(ContextHandler, 'getContext', () => { + return ctx; + }); + const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); + const bar = barObj.obj as SingletonBar; + const msg = await bar.hello(); + assert(msg === 'hello from depth2'); + await TestUtil.destroyLoadUnitInstance(instance); + await ctx.destroy({}); + }); + }); + + describe('property mock', () => { + beforeEach(() => { + mm(ContextHandler, 'getContext', () => { + return ctx; + }); + }); + + it('should work', async () => { + const instance = await TestUtil.createLoadUnitInstance('extends-module'); + const barProto = EggPrototypeFactory.instance.getPrototype('bar', instance.loadUnit); + const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); + const bar = barObj.obj as ExtendsBar; + const foo = {}; + mm(bar, 'foo', foo); + assert(bar.foo === foo); + + await TestUtil.destroyLoadUnitInstance(instance); + await ctx.destroy({}); + }); + }); }); diff --git a/core/runtime/test/EggObjectUtil.test.ts b/core/runtime/test/EggObjectUtil.test.ts new file mode 100644 index 00000000..a51df59e --- /dev/null +++ b/core/runtime/test/EggObjectUtil.test.ts @@ -0,0 +1,27 @@ +import TestUtil from './util'; +import assert from 'assert'; +import mm from 'mm'; +import { EggPrototypeFactory } from '@eggjs/tegg-metadata'; +import { EggObjectUtil } from '../src/impl/EggObjectUtil'; +import { ContextHandler } from '../src/model/ContextHandler'; +import { EggTestContext } from './fixtures/EggTestContext'; + +describe('test/EggObjectUtil.test.ts', () => { + let ctx: EggTestContext; + + beforeEach(() => { + ctx = new EggTestContext(); + mm(ContextHandler, 'getContext', () => { + return ctx; + }); + }); + + it('should name should has self descriptor', async () => { + const instance = await TestUtil.createLoadUnitInstance('lifecycle-hook'); + const fooProto = EggPrototypeFactory.instance.getPrototype('foo'); + const fooDesc = EggObjectUtil.contextEggObjectGetProperty(fooProto, 'foo'); + const barDesc = EggObjectUtil.contextEggObjectGetProperty(fooProto, 'bar'); + assert(fooDesc !== barDesc); + await TestUtil.destroyLoadUnitInstance(instance); + }); +}); diff --git a/core/runtime/test/LoadUnitInstance.test.ts b/core/runtime/test/LoadUnitInstance.test.ts index f548e72c..98209227 100644 --- a/core/runtime/test/LoadUnitInstance.test.ts +++ b/core/runtime/test/LoadUnitInstance.test.ts @@ -6,15 +6,31 @@ import { EggContainerFactory, LoadUnitInstance } from '..'; import CountController from './fixtures/modules/module-for-load-unit-instance/CountController'; import AppService from './fixtures/modules/multi-module/multi-module-service/AppService'; import { Bar, Foo } from './fixtures/modules/extends-module/Base'; +import mm from 'mm'; +import { ContextHandler } from '../src/model/ContextHandler'; +import { EggContextStorage } from './fixtures/EggContextStorage'; +import { FOO_ATTRIBUTE, FooLogger } from './fixtures/modules/multi-instance-module/MultiInstance'; describe('test/LoadUnit/LoadUnitInstance.test.ts', () => { describe('ModuleLoadUnitInstance', () => { + let ctx: EggTestContext; + + beforeEach(() => { + ctx = new EggTestContext(); + mm(ContextHandler, 'getContext', () => { + return ctx; + }); + }); + + afterEach(async () => { + await ctx.destroy({}); + mm.restore(); + }); it('should create success', async () => { const instance = await TestUtil.createLoadUnitInstance('module-for-load-unit-instance'); - const ctx = new EggTestContext(); const countControllerProto = EggPrototypeFactory.instance.getPrototype('countController'); - const countControllerObj = await EggContainerFactory.getOrCreateEggObject(countControllerProto, countControllerProto.name, ctx); + const countControllerObj = await EggContainerFactory.getOrCreateEggObject(countControllerProto, countControllerProto.name); const countController = countControllerObj.obj as CountController; const countResult = await countController.getCount(); assert.deepStrictEqual(countResult, { @@ -35,19 +51,43 @@ describe('test/LoadUnit/LoadUnitInstance.test.ts', () => { it('should load extends class success', async () => { const instance = await TestUtil.createLoadUnitInstance('extends-module'); - const ctx = new EggTestContext(); const barProto = EggPrototypeFactory.instance.getPrototype('bar', instance.loadUnit); - const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name, ctx); + const barObj = await EggContainerFactory.getOrCreateEggObject(barProto, barProto.name); const bar = barObj.obj as Bar; const fooProto = EggPrototypeFactory.instance.getPrototype('foo', instance.loadUnit); - const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name, ctx); + const fooObj = await EggContainerFactory.getOrCreateEggObject(fooProto, fooProto.name); const foo = fooObj.obj as Foo; assert(bar.foo); assert(bar.logger); assert(foo.logger); await TestUtil.destroyLoadUnitInstance(instance); }); + + it('should load multi instance', async () => { + const instance = await TestUtil.createLoadUnitInstance('multi-instance-module'); + const foo1Proto = EggPrototypeFactory.instance.getPrototype('foo', instance.loadUnit, [{ + attribute: FOO_ATTRIBUTE, + value: 'foo1', + }]); + const foo1Obj = await EggContainerFactory.getOrCreateEggObject(foo1Proto, foo1Proto.name); + const foo1 = foo1Obj.obj as FooLogger; + + const foo2Proto = EggPrototypeFactory.instance.getPrototype('foo', instance.loadUnit, [{ + attribute: FOO_ATTRIBUTE, + value: 'foo2', + }]); + const foo2Obj = await EggContainerFactory.getOrCreateEggObject(foo2Proto, foo2Proto.name); + const foo2 = foo2Obj.obj as FooLogger; + assert(foo1); + assert(foo2); + assert(foo1 !== foo2); + assert(foo1.loadUnitPath); + assert(foo1.foo === 'foo1'); + assert(foo2.loadUnitPath); + assert(foo2.foo === 'foo2'); + await TestUtil.destroyLoadUnitInstance(instance); + }); }); describe('MultiModule', () => { @@ -56,6 +96,7 @@ describe('test/LoadUnit/LoadUnitInstance.test.ts', () => { let serviceInstance: LoadUnitInstance; before(async () => { + EggContextStorage.register(); commonInstance = await TestUtil.createLoadUnitInstance('multi-module/multi-module-common'); repoInstance = await TestUtil.createLoadUnitInstance('multi-module/multi-module-repo'); serviceInstance = await TestUtil.createLoadUnitInstance('multi-module/multi-module-service'); @@ -69,17 +110,24 @@ describe('test/LoadUnit/LoadUnitInstance.test.ts', () => { it('should get appService', async () => { const saveCtx = new EggTestContext(); - const saveAppServiceProto = EggPrototypeFactory.instance.getPrototype('appService', serviceInstance.loadUnit); - const saveAppServiceObj = await EggContainerFactory.getOrCreateEggObject(saveAppServiceProto, saveAppServiceProto.name, saveCtx); - const saveAppService = saveAppServiceObj.obj as AppService; - await saveAppService.save({ - name: 'mock-app', - desc: 'mock-desc', - }); const findCtx = new EggTestContext(); - const findAppServiceObj = await EggContainerFactory.getOrCreateEggObject(saveAppServiceProto, saveAppServiceProto.name, findCtx); - const findAppService = findAppServiceObj.obj as AppService; - + const saveAppServiceProto = EggPrototypeFactory.instance.getPrototype('appService', serviceInstance.loadUnit); + const [ saveAppService, findAppService ] = await Promise.all([ + ContextHandler.run(saveCtx, async () => { + const saveAppServiceObj = await EggContainerFactory.getOrCreateEggObject(saveAppServiceProto, saveAppServiceProto.name); + const saveAppService = saveAppServiceObj.obj as AppService; + await saveAppService.save({ + name: 'mock-app', + desc: 'mock-desc', + }); + return saveAppService; + }), + ContextHandler.run(findCtx, async () => { + const findAppServiceObj = await EggContainerFactory.getOrCreateEggObject(saveAppServiceProto, saveAppServiceProto.name); + const findAppService = findAppServiceObj.obj as AppService; + return findAppService; + }), + ]); // not same service because ctx is different assert(saveAppService !== findAppService); diff --git a/core/runtime/test/QualifierLoadUnitInstance.test.ts b/core/runtime/test/QualifierLoadUnitInstance.test.ts index aea7611f..0534d890 100644 --- a/core/runtime/test/QualifierLoadUnitInstance.test.ts +++ b/core/runtime/test/QualifierLoadUnitInstance.test.ts @@ -4,14 +4,29 @@ import { EggTestContext } from './fixtures/EggTestContext'; import CacheService from './fixtures/modules/init-type-qualifier-module/CacheService'; import { EggPrototypeFactory } from '@eggjs/tegg-metadata'; import { EggContainerFactory } from '..'; +import mm from 'mm'; +import { ContextHandler } from '../src/model/ContextHandler'; describe('test/LoadUnit/QualifierLoadUnitInstance.test.ts', () => { + let ctx: EggTestContext; + + beforeEach(() => { + ctx = new EggTestContext(); + mm(ContextHandler, 'getContext', () => { + return ctx; + }); + }); + + afterEach(async () => { + await ctx.destroy({}); + mm.restore(); + }); + describe('init type qualifier', () => { it('should work', async () => { const instance = await TestUtil.createLoadUnitInstance('init-type-qualifier-module'); - const ctx = new EggTestContext(); const cacheServiceProto = EggPrototypeFactory.instance.getPrototype('cacheService', instance.loadUnit); - const cacheServiceObj = await EggContainerFactory.getOrCreateEggObject(cacheServiceProto, cacheServiceProto.name, ctx); + const cacheServiceObj = await EggContainerFactory.getOrCreateEggObject(cacheServiceProto, cacheServiceProto.name); const cacheService = cacheServiceObj.obj as CacheService; cacheService.setContextCache('cacheKey', 'cacheVal'); cacheService.setSingletonCache('cacheKey', 'cacheVal'); diff --git a/core/runtime/test/fixtures/EggContextStorage.ts b/core/runtime/test/fixtures/EggContextStorage.ts new file mode 100644 index 00000000..4a6c707e --- /dev/null +++ b/core/runtime/test/fixtures/EggContextStorage.ts @@ -0,0 +1,16 @@ +import { AsyncLocalStorage } from 'async_hooks'; +import { EggContext } from '../../src/model/EggContext'; +import { ContextHandler } from '../../src/model/ContextHandler'; + +export class EggContextStorage { + static storage = new AsyncLocalStorage(); + + static register() { + ContextHandler.getContextCallback = () => { + return EggContextStorage.storage.getStore(); + }; + ContextHandler.runInContextCallback = (context: EggContext, fn: () => Promise) => { + return EggContextStorage.storage.run(context, fn); + }; + } +} diff --git a/core/runtime/test/fixtures/modules/inject-context-to-singleton/object.ts b/core/runtime/test/fixtures/modules/inject-context-to-singleton/object.ts new file mode 100644 index 00000000..2b9776b0 --- /dev/null +++ b/core/runtime/test/fixtures/modules/inject-context-to-singleton/object.ts @@ -0,0 +1,59 @@ +import { AccessLevel, ContextProto, Inject, SingletonProto } from '@eggjs/core-decorator'; + +@ContextProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class ContextFooDepth2 { + async hello() { + return 'hello from depth2'; + } +} + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class SingletonBarDepth3 { + @Inject() + contextFooDepth2: ContextFooDepth2; + + async hello() { + return this.contextFooDepth2.hello(); + } +} + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class SingletonBarDepth2 { + @Inject() + singletonBarDepth3: SingletonBarDepth3; + + async hello() { + return this.singletonBarDepth3.hello(); + } +} + + +@ContextProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class ContextFoo { + @Inject() + private readonly singletonBarDepth2: SingletonBarDepth2; + + async hello() { + return this.singletonBarDepth2.hello(); + } +} + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class SingletonBar { + @Inject() + foo: ContextFoo; + + async hello() { + return this.foo.hello(); + } +} diff --git a/core/runtime/test/fixtures/modules/inject-context-to-singleton/package.json b/core/runtime/test/fixtures/modules/inject-context-to-singleton/package.json new file mode 100644 index 00000000..b4430b03 --- /dev/null +++ b/core/runtime/test/fixtures/modules/inject-context-to-singleton/package.json @@ -0,0 +1,6 @@ +{ + "name": "multi-module-service", + "eggModule": { + "name": "extendsModule" + } +} diff --git a/core/runtime/test/fixtures/modules/lifecycle-hook/object.ts b/core/runtime/test/fixtures/modules/lifecycle-hook/object.ts index 12930176..4dde9f2b 100644 --- a/core/runtime/test/fixtures/modules/lifecycle-hook/object.ts +++ b/core/runtime/test/fixtures/modules/lifecycle-hook/object.ts @@ -1,5 +1,13 @@ import { AccessLevel, ContextProto, SingletonProto } from '@eggjs/core-decorator'; -import { EggObjectLifecycle } from '@eggjs/tegg-lifecycle'; +import { + EggObjectLifecycle, + LifecyclePostConstruct, + LifecyclePreInject, + LifecyclePostInject, + LifecycleInit, + LifecyclePreDestroy, + LifecycleDestroy, +} from '@eggjs/tegg-lifecycle'; @ContextProto({ accessLevel: AccessLevel.PUBLIC, @@ -43,7 +51,7 @@ export class Foo implements EggObjectLifecycle { @SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) -export class Bar implements EggObjectLifecycle { +export class Bar { private called: string[] = []; getLifecycleCalled() { @@ -54,27 +62,37 @@ export class Bar implements EggObjectLifecycle { this.called.push('construct'); } - async postConstruct(): Promise { + @LifecyclePostConstruct() + protected async _postConstruct() { this.called.push('postConstruct'); } - async preInject(): Promise { + @LifecyclePreInject() + protected async _preInject() { this.called.push('preInject'); } - async postInject(): Promise { + @LifecyclePostInject() + protected async _postInject() { this.called.push('postInject'); } - async init(): Promise { + protected async init() { + this.called.push('init should not called'); + } + + @LifecycleInit() + protected async _init() { this.called.push('init'); } - async preDestroy(): Promise { + @LifecyclePreDestroy() + protected async _preDestroy() { this.called.push('preDestroy'); } - async destroy(): Promise { + @LifecycleDestroy() + protected async _destroy() { this.called.push('destroy'); } } diff --git a/core/runtime/test/fixtures/modules/multi-instance-module/MultiInstance.ts b/core/runtime/test/fixtures/modules/multi-instance-module/MultiInstance.ts new file mode 100644 index 00000000..e579019d --- /dev/null +++ b/core/runtime/test/fixtures/modules/multi-instance-module/MultiInstance.ts @@ -0,0 +1,34 @@ +import { AccessLevel, ObjectInitType, MultiInstanceProto, QualifierValue } from '@eggjs/core-decorator'; +import { LifecycleInit } from '@eggjs/tegg-lifecycle'; +import { EggObject, EggObjectLifeCycleContext } from '../../../../src/model/EggObject'; + + +export const FOO_ATTRIBUTE = Symbol.for('FOO_ATTRIBUTE'); + +@MultiInstanceProto({ + accessLevel: AccessLevel.PUBLIC, + initType: ObjectInitType.SINGLETON, + objects: [{ + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo1', + }], + }, { + name: 'foo', + qualifiers: [{ + attribute: FOO_ATTRIBUTE, + value: 'foo2', + }], + }], +}) +export class FooLogger { + loadUnitPath: string; + foo: QualifierValue | undefined; + + @LifecycleInit() + async init(ctx: EggObjectLifeCycleContext, obj: EggObject) { + this.loadUnitPath = ctx.loadUnit.unitPath; + this.foo = obj.proto.getQualifier(FOO_ATTRIBUTE); + } +} diff --git a/core/runtime/test/fixtures/modules/multi-instance-module/package.json b/core/runtime/test/fixtures/modules/multi-instance-module/package.json new file mode 100644 index 00000000..7c7ca109 --- /dev/null +++ b/core/runtime/test/fixtures/modules/multi-instance-module/package.json @@ -0,0 +1,6 @@ +{ + "name": "multi-instance-module", + "eggModule": { + "name": "multiInstanceModule" + } +} diff --git a/core/runtime/tsconfig.json b/core/runtime/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/runtime/tsconfig.json +++ b/core/runtime/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/schedule-decorator/CHANGELOG.md b/core/schedule-decorator/CHANGELOG.md index eb0aa0f3..1170c405 100644 --- a/core/schedule-decorator/CHANGELOG.md +++ b/core/schedule-decorator/CHANGELOG.md @@ -3,6 +3,209 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-schedule-decorator + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + # 1.5.0 (2022-09-04) diff --git a/core/schedule-decorator/package.json b/core/schedule-decorator/package.json index 29b59da8..42140eba 100644 --- a/core/schedule-decorator/package.json +++ b/core/schedule-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-schedule-decorator", - "version": "1.6.0", + "version": "3.23.0", "description": "tegg schedule decorator", "main": "dist/index.js", "files": [ @@ -18,8 +18,7 @@ "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -36,13 +35,19 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-metadata": "^1.4.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", "cron-parser": "^2.18.0" }, "devDependencies": { - "egg-schedule": "^3.6.6" + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg-schedule": "^3.6.6", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/core/schedule-decorator/src/decorator/Schedule.ts b/core/schedule-decorator/src/decorator/Schedule.ts index 43591e51..7f32a41c 100644 --- a/core/schedule-decorator/src/decorator/Schedule.ts +++ b/core/schedule-decorator/src/decorator/Schedule.ts @@ -1,5 +1,5 @@ import { ScheduleTypeLike } from '../model/ScheduleMetadata'; -import { AccessLevel, ContextProto, EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; +import { AccessLevel, EggProtoImplClass, PrototypeUtil, SingletonProto } from '@eggjs/core-decorator'; import { ScheduleInfoUtil } from '../util/ScheduleInfoUtil'; import { StackUtil } from '@eggjs/tegg-common-util'; @@ -40,7 +40,7 @@ export function Schedule(param: ScheduleParams, options?: ScheduleOptions) if (options) { ScheduleInfoUtil.setScheduleOptions(options, clazz); } - const func = ContextProto({ + const func = SingletonProto({ name: clazz.name, accessLevel: AccessLevel.PUBLIC, }); diff --git a/core/schedule-decorator/tsconfig.json b/core/schedule-decorator/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/schedule-decorator/tsconfig.json +++ b/core/schedule-decorator/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/standalone-decorator/CHANGELOG.md b/core/standalone-decorator/CHANGELOG.md index 1ecee655..533f4e74 100644 --- a/core/standalone-decorator/CHANGELOG.md +++ b/core/standalone-decorator/CHANGELOG.md @@ -3,6 +3,169 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/standalone-decorator + + + + + # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/standalone-decorator diff --git a/core/standalone-decorator/package.json b/core/standalone-decorator/package.json index 5d07a843..258c27b1 100644 --- a/core/standalone-decorator/package.json +++ b/core/standalone-decorator/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/standalone-decorator", - "version": "1.2.0", + "version": "3.23.0", "description": "tegg standalone decorator", "keywords": [ "egg", @@ -19,8 +19,7 @@ "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -37,10 +36,18 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/tegg-common-util": "^1.2.0", + "@eggjs/tegg-common-util": "^3.23.0", "reflect-metadata": "^0.1.13" }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/standalone-decorator/tsconfig.json b/core/standalone-decorator/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/standalone-decorator/tsconfig.json +++ b/core/standalone-decorator/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/tegg/CHANGELOG.md b/core/tegg/CHANGELOG.md index e172221a..0285b976 100644 --- a/core/tegg/CHANGELOG.md +++ b/core/tegg/CHANGELOG.md @@ -3,6 +3,285 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + + +### Features + +* implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + + +### Features + +* export transaction decorator from tegg ([8be0521](https://github.com/eggjs/tegg/commit/8be05212b62fe7f111688efaec935be64d623918)) + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + # [1.4.0](https://github.com/eggjs/tegg/compare/@eggjs/tegg@1.3.5...@eggjs/tegg@1.4.0) (2022-09-04) diff --git a/core/tegg/index.ts b/core/tegg/index.ts index 8a85dd44..e913b9a8 100644 --- a/core/tegg/index.ts +++ b/core/tegg/index.ts @@ -7,3 +7,4 @@ export * from '@eggjs/tegg-background-task'; export * as aop from '@eggjs/aop-decorator'; export * as orm from '@eggjs/tegg-orm-decorator'; export * as schedule from '@eggjs/tegg-schedule-decorator'; +export { RuntimeConfig } from '@eggjs/tegg-common-util'; diff --git a/core/tegg/package.json b/core/tegg/package.json index 24583665..9b5ad9b2 100644 --- a/core/tegg/package.json +++ b/core/tegg/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg", - "version": "1.5.0", + "version": "3.23.0", "description": "tegg decorator packages", "keywords": [ "egg", @@ -15,10 +15,10 @@ ], "typings": "index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -35,22 +35,31 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/aop-decorator": "^1.4.0", - "@eggjs/controller-decorator": "^1.6.0", - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/eventbus-decorator": "^1.4.0", - "@eggjs/standalone-decorator": "^1.2.0", - "@eggjs/tegg-background-task": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-dynamic-inject": "^1.4.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-orm-decorator": "^1.5.0", - "@eggjs/tegg-runtime": "^1.4.0", - "@eggjs/tegg-schedule-decorator": "^1.6.0" + "@eggjs/aop-decorator": "^3.23.0", + "@eggjs/controller-decorator": "^3.23.0", + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/eventbus-decorator": "^3.23.0", + "@eggjs/standalone-decorator": "^3.23.0", + "@eggjs/tegg-background-task": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-dynamic-inject": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-orm-decorator": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", + "@eggjs/tegg-schedule-decorator": "^3.23.0", + "@eggjs/tegg-transaction-decorator": "^3.23.0" }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/tegg/transaction.ts b/core/tegg/transaction.ts new file mode 100644 index 00000000..963fc4e4 --- /dev/null +++ b/core/tegg/transaction.ts @@ -0,0 +1 @@ +export * from '@eggjs/tegg-transaction-decorator'; diff --git a/core/tegg/tsconfig.json b/core/tegg/tsconfig.json index 39b89400..49122286 100644 --- a/core/tegg/tsconfig.json +++ b/core/tegg/tsconfig.json @@ -5,6 +5,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/test-util/CHANGELOG.md b/core/test-util/CHANGELOG.md index 8c91ce85..ecbec342 100644 --- a/core/test-util/CHANGELOG.md +++ b/core/test-util/CHANGELOG.md @@ -3,6 +3,223 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/module-test-util + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + # [1.3.0](https://github.com/eggjs/tegg/compare/v1.2.0...v1.3.0) (2022-07-01) **Note:** Version bump only for package @eggjs/module-test-util diff --git a/core/test-util/CoreTestHelper.ts b/core/test-util/CoreTestHelper.ts index da99ac25..dd2d6286 100644 --- a/core/test-util/CoreTestHelper.ts +++ b/core/test-util/CoreTestHelper.ts @@ -1,23 +1,47 @@ -import { EggContainerFactory, EggContext, LoadUnitInstance, LoadUnitInstanceFactory } from '@eggjs/tegg-runtime'; +import { + ContextHandler, + EggContainerFactory, + EggContext, + LoadUnitInstance, + LoadUnitInstanceFactory, +} from '@eggjs/tegg-runtime'; import { EggLoadUnitType, EggPrototype, LoadUnitFactory } from '@eggjs/tegg-metadata'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { EggProtoImplClass, PrototypeUtil } from '@eggjs/core-decorator'; +import { AsyncLocalStorage } from 'async_hooks'; + +export class EggContextStorage { + static storage = new AsyncLocalStorage(); + + static register() { + ContextHandler.getContextCallback = () => { + return EggContextStorage.storage.getStore(); + }; + ContextHandler.runInContextCallback = (context, fn) => { + return EggContextStorage.storage.run(context, fn); + }; + } +} + export class CoreTestHelper { + static contextStorage = new AsyncLocalStorage(); + static async getLoadUnitInstance(moduleDir: string): Promise { const loader = LoaderFactory.createLoader(moduleDir, EggLoadUnitType.MODULE); const loadUnit = await LoadUnitFactory.createLoadUnit(moduleDir, EggLoadUnitType.MODULE, loader); return await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); } static async prepareModules(moduleDirs: string[]): Promise> { + EggContextStorage.register(); const instances: Array = []; for (const moduleDir of moduleDirs) { instances.push(await CoreTestHelper.getLoadUnitInstance(moduleDir)); } return instances; } - static async getObject(clazz: EggProtoImplClass, ctx?: EggContext): Promise { + static async getObject(clazz: EggProtoImplClass): Promise { const proto = PrototypeUtil.getClazzProto(clazz as any) as EggPrototype; - const eggObj = await EggContainerFactory.getOrCreateEggObject(proto, proto.name, ctx); + const eggObj = await EggContainerFactory.getOrCreateEggObject(proto, proto.name); return eggObj.obj as unknown as T; } } diff --git a/core/test-util/EggTestContext.ts b/core/test-util/EggTestContext.ts index 2551a5ed..5c44b2d3 100644 --- a/core/test-util/EggTestContext.ts +++ b/core/test-util/EggTestContext.ts @@ -1,4 +1,5 @@ -import { AbstractEggContext } from '@eggjs/tegg-runtime'; +import mm from 'mm'; +import { AbstractEggContext, ContextHandler } from '@eggjs/tegg-runtime'; import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; const EGG_CTX = Symbol('TEgg#context'); @@ -20,5 +21,15 @@ export class EggTestContext extends AbstractEggContext { }; this.id = IdenticalUtil.createContextId(); this.set(EGG_CTX, mockCtx); + mm(ContextHandler, 'getContext', () => { + return this; + }); + } + + static async mockContext(cb: (ctx: EggTestContext) => Promise): Promise { + const ctx = new EggTestContext(); + return await ContextHandler.run(ctx, () => { + return cb(ctx); + }); } } diff --git a/core/test-util/index.ts b/core/test-util/index.ts index b146e591..7902286b 100644 --- a/core/test-util/index.ts +++ b/core/test-util/index.ts @@ -1,3 +1,4 @@ export * from './LoaderUtil'; export * from './TestLoader'; export * from './EggTestContext'; +export * from './CoreTestHelper'; diff --git a/core/test-util/package.json b/core/test-util/package.json index 68c65085..217ae44c 100644 --- a/core/test-util/package.json +++ b/core/test-util/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/module-test-util", - "version": "1.4.0", + "version": "3.23.0", "private": true, "description": "module test util", "keywords": [ @@ -18,8 +18,7 @@ "scripts": { "clean": "tsc -b --clean", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -36,10 +35,20 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/core-decorator": "^1.4.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0" + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", + "globby": "^11.1.0", + "mm": "^3.2.1" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/core/test-util/tsconfig.json b/core/test-util/tsconfig.json index ed206ac6..64b22405 100644 --- a/core/test-util/tsconfig.json +++ b/core/test-util/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/core/transaction-decorator/CHANGELOG.md b/core/transaction-decorator/CHANGELOG.md new file mode 100644 index 00000000..bdb436dd --- /dev/null +++ b/core/transaction-decorator/CHANGELOG.md @@ -0,0 +1,126 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + + +### Features + +* 事务注解增加数据源选项 ([#135](https://github.com/eggjs/tegg/issues/135)) ([c33b3b5](https://github.com/eggjs/tegg/commit/c33b3b5ec9d32a8c6675d986013042f0cb8e4370)) + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-transaction-decorator + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + + +### Features + +* impl transaction decorator ([#124](https://github.com/eggjs/tegg/issues/124)) ([4896615](https://github.com/eggjs/tegg/commit/4896615af951bbff940cda7abc116df40ed486e5)) diff --git a/core/transaction-decorator/README.md b/core/transaction-decorator/README.md new file mode 100644 index 00000000..605bf521 --- /dev/null +++ b/core/transaction-decorator/README.md @@ -0,0 +1,36 @@ +# @eggjs/tegg-transaction-decorator + +事务注解 + +## Usage +### 传播机制 +```ts +export class Foo { + + @Transactional({ propagation: PropagationType.ALWAYS_NEW }) + async bar() { + await this.foo(); + } + + @Transactional({ propagation: PropagationType.REQUIRED }) + async foo(msg) { + console.log('has msg: ', msg); + } + +} +``` + +### 数据源 +```ts +export class Bar { + + @Transactional({ dataSourceName: 'xx' }) + async bar() { + await this.foo(); + } + +} + +``` + +Foo.bar 始终会在一个独立的事务中执行,而 Foo.foo 会在 Foo.bar 的事务中执行 diff --git a/core/transaction-decorator/index.ts b/core/transaction-decorator/index.ts new file mode 100644 index 00000000..e9a0e223 --- /dev/null +++ b/core/transaction-decorator/index.ts @@ -0,0 +1,5 @@ +export * from './src/decorator/Transactional'; +export * from './src/Common'; +export * from './src/model/TransactionMetadata'; +export * from './src/builder/TransactionMetaBuilder'; +export * from './src/util/TransactionMetadataUtil'; diff --git a/core/transaction-decorator/package.json b/core/transaction-decorator/package.json new file mode 100644 index 00000000..e78196a9 --- /dev/null +++ b/core/transaction-decorator/package.json @@ -0,0 +1,54 @@ +{ + "name": "@eggjs/tegg-transaction-decorator", + "version": "3.23.0", + "description": "tegg transaction decorator", + "keywords": [ + "egg", + "typescript", + "decorator", + "transaction", + "tegg" + ], + "author": "qile222 ", + "homepage": "https://github.com/eggjs/tegg", + "repository": { + "type": "git", + "url": "git@github.com:eggjs/tegg.git", + "directory": "core/transaction-decorator" + }, + "dependencies": { + "@eggjs/core-decorator": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0" + }, + "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", + "clean": "tsc -b --clean", + "tsc": "npm run clean && tsc -p ./tsconfig.json", + "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", + "prepublishOnly": "npm run tsc:pub" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=14.0.0" + }, + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist/**/*.js", + "dist/**/*.d.ts" + ], + "bugs": { + "url": "https://github.com/eggjs/tegg/issues" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + } +} diff --git a/core/transaction-decorator/src/Common.ts b/core/transaction-decorator/src/Common.ts new file mode 100644 index 00000000..a8adda3f --- /dev/null +++ b/core/transaction-decorator/src/Common.ts @@ -0,0 +1,17 @@ +export enum PropagationType { + /** 不管是当前调用栈是否存在事务,始终让当前函数在新的事务中执行 */ + ALWAYS_NEW = 'ALWAYS_NEW', + /** 如果当前调用栈存在事务则复用,否则创建一个 */ + REQUIRED = 'REQUIRED', +} + +export interface TransactionalParams { + /** 事务传播方式,默认 REQUIRED */ + propagation?: PropagationType; + /** + * 数据源,默认使用 module 的数据源,非 module 时将使用 default 数据源 + * 需要注意的是数据源之间的连接是隔离的,回滚也是独立的 + * 比如函数 B 在函数 A 中执行,A 执行异常时,不会回滚 B 中执行的 sql + * */ + datasourceName?: string; +} diff --git a/core/transaction-decorator/src/builder/TransactionMetaBuilder.ts b/core/transaction-decorator/src/builder/TransactionMetaBuilder.ts new file mode 100644 index 00000000..5cf7965a --- /dev/null +++ b/core/transaction-decorator/src/builder/TransactionMetaBuilder.ts @@ -0,0 +1,18 @@ +import { EggProtoImplClass } from '@eggjs/tegg'; +import { TransactionMetadata } from '../model/TransactionMetadata'; +import { TransactionMetadataUtil } from '../util/TransactionMetadataUtil'; + +export class TransactionMetaBuilder { + private readonly clazz: EggProtoImplClass; + + constructor(clazz: EggProtoImplClass) { + this.clazz = clazz; + } + + build(): TransactionMetadata[] { + if (!TransactionMetadataUtil.isTransactionClazz(this.clazz)) { + return []; + } + return TransactionMetadataUtil.getTransactionMetadataList(this.clazz); + } +} diff --git a/core/transaction-decorator/src/decorator/Transactional.ts b/core/transaction-decorator/src/decorator/Transactional.ts new file mode 100644 index 00000000..cf87403c --- /dev/null +++ b/core/transaction-decorator/src/decorator/Transactional.ts @@ -0,0 +1,21 @@ +import { EggProtoImplClass } from '@eggjs/tegg'; +import { TransactionalParams, PropagationType } from '../Common'; +import { TransactionMetadataUtil } from '../util/TransactionMetadataUtil'; + +export function Transactional(params?: TransactionalParams) { + const propagation = params?.propagation || PropagationType.REQUIRED; + if (!Object.values(PropagationType).includes(propagation)) { + throw new Error(`unknown propagation type ${propagation}`); + } + const datasourceName = params?.datasourceName; + + return function(target: any, propertyKey: PropertyKey) { + const constructor: EggProtoImplClass = target.constructor; + TransactionMetadataUtil.setIsTransactionClazz(constructor); + TransactionMetadataUtil.addTransactionMetadata(constructor, { + propagation, + method: propertyKey, + datasourceName, + }); + }; +} diff --git a/core/transaction-decorator/src/model/TransactionMetadata.ts b/core/transaction-decorator/src/model/TransactionMetadata.ts new file mode 100644 index 00000000..efb851c9 --- /dev/null +++ b/core/transaction-decorator/src/model/TransactionMetadata.ts @@ -0,0 +1,7 @@ +import { PropagationType } from '../Common'; + +export interface TransactionMetadata { + propagation: PropagationType; + method: PropertyKey; + datasourceName?: string; +} diff --git a/core/transaction-decorator/src/util/TransactionMetadataUtil.ts b/core/transaction-decorator/src/util/TransactionMetadataUtil.ts new file mode 100644 index 00000000..2c91d164 --- /dev/null +++ b/core/transaction-decorator/src/util/TransactionMetadataUtil.ts @@ -0,0 +1,26 @@ +import { EggProtoImplClass, MetadataUtil } from '@eggjs/tegg'; +import { TransactionMetadata } from '../model/TransactionMetadata'; + +export const TRANSACTION_META_DATA = Symbol.for('EggPrototype#transaction#metaData'); +export const IS_TRANSACTION_CLAZZ = Symbol.for('EggPrototype#IS_TRANSACTION_CLAZZ'); + +export class TransactionMetadataUtil { + + static setIsTransactionClazz(clazz: EggProtoImplClass) { + MetadataUtil.defineMetaData(IS_TRANSACTION_CLAZZ, true, clazz); + } + + static isTransactionClazz(clazz: EggProtoImplClass): boolean { + return MetadataUtil.getBooleanMetaData(IS_TRANSACTION_CLAZZ, clazz); + } + + static addTransactionMetadata(clazz: EggProtoImplClass, data: TransactionMetadata) { + const list = MetadataUtil.initOwnArrayMetaData(TRANSACTION_META_DATA, clazz, []); + list.push(data); + } + + static getTransactionMetadataList(clazz: EggProtoImplClass): TransactionMetadata[] { + return MetadataUtil.getArrayMetaData(TRANSACTION_META_DATA, clazz); + } + +} diff --git a/core/transaction-decorator/test/builder/TransactionMetaBuilder.test.ts b/core/transaction-decorator/test/builder/TransactionMetaBuilder.test.ts new file mode 100644 index 00000000..bfb4e264 --- /dev/null +++ b/core/transaction-decorator/test/builder/TransactionMetaBuilder.test.ts @@ -0,0 +1,57 @@ +import assert from 'assert'; +import { TransactionMetadataUtil } from '../../src/util/TransactionMetadataUtil'; +import { TransactionMetaBuilder } from '../../src/builder/TransactionMetaBuilder'; +import { PropagationType } from '../../src/Common'; +import { Foo, Bar, FooBar, BarFoo } from '../fixtures/transaction'; +import { Transactional } from '../../src/decorator/Transactional'; + +describe('test/builder/TransactionMetaBuilder.test.ts', () => { + + it('should build meta data success', () => { + assert.ok(TransactionMetadataUtil.isTransactionClazz(Foo)); + + const fooBuilder = new TransactionMetaBuilder(Foo); + assert.deepStrictEqual(fooBuilder.build(), [{ + propagation: PropagationType.REQUIRED, + method: 'defaultPropagation', + datasourceName: undefined, + }, { + propagation: PropagationType.REQUIRED, + method: 'requiredPropagation', + datasourceName: 'testDatasourceName1', + }, { + propagation: PropagationType.ALWAYS_NEW, + method: 'alwaysNewPropagation', + datasourceName: undefined, + }]); + + assert.ok(TransactionMetadataUtil.isTransactionClazz(Bar)); + const barBuilder = new TransactionMetaBuilder(Bar); + assert.deepStrictEqual(barBuilder.build(), [{ + propagation: PropagationType.REQUIRED, + method: 'foo', + datasourceName: 'datasourceName2', + }, { + propagation: PropagationType.ALWAYS_NEW, + method: 'bar', + datasourceName: undefined, + }]); + + assert.ok(TransactionMetadataUtil.isTransactionClazz(FooBar)); + const fooBarBuilder = new TransactionMetaBuilder(FooBar); + assert.deepStrictEqual(fooBarBuilder.build(), [{ + propagation: PropagationType.ALWAYS_NEW, + method: 'foo', + datasourceName: undefined, + }]); + + const barFooBuilder = new TransactionMetaBuilder(BarFoo); + assert.ok(!TransactionMetadataUtil.isTransactionClazz(BarFoo)); + assert.deepStrictEqual(barFooBuilder.build(), []); + + assert.throws(() => { + Transactional({ propagation: 'xx' as PropagationType }); + }, new Error('unknown propagation type xx')); + }); + +}); diff --git a/core/transaction-decorator/test/fixtures/transaction.ts b/core/transaction-decorator/test/fixtures/transaction.ts new file mode 100644 index 00000000..b07aa2fa --- /dev/null +++ b/core/transaction-decorator/test/fixtures/transaction.ts @@ -0,0 +1,54 @@ +import { Transactional } from '../../src/decorator/Transactional'; +import { PropagationType } from '../../src/Common'; + +export class Foo { + + @Transactional() + async defaultPropagation(msg) { + console.log('msg: ', msg); + } + + @Transactional({ + datasourceName: 'testDatasourceName1', + }) + async requiredPropagation(msg) { + console.log('msg: ', msg); + } + + @Transactional({ propagation: PropagationType.ALWAYS_NEW }) + async alwaysNewPropagation(msg) { + console.log('msg: ', msg); + } + +} + +export class Bar { + + @Transactional({ datasourceName: 'datasourceName2' }) + async foo(msg) { + console.log('msg: ', msg); + } + + @Transactional({ propagation: PropagationType.ALWAYS_NEW }) + async bar(msg) { + console.log('msg: ', msg); + } + +} + +export class FooBar { + + @Transactional({ propagation: PropagationType.ALWAYS_NEW }) + async foo(msg) { + console.log('msg: ', msg); + } + +} + +export class BarFoo { + + async foo(msg) { + console.log('msg: ', msg); + } + +} diff --git a/core/transaction-decorator/tsconfig.json b/core/transaction-decorator/tsconfig.json new file mode 100644 index 00000000..ed206ac6 --- /dev/null +++ b/core/transaction-decorator/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "baseUrl": "./" + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/core/transaction-decorator/tsconfig.pub.json b/core/transaction-decorator/tsconfig.pub.json new file mode 100644 index 00000000..64b22405 --- /dev/null +++ b/core/transaction-decorator/tsconfig.pub.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "baseUrl": "./" + }, + "exclude": [ + "dist", + "node_modules", + "test" + ] +} diff --git a/lerna.json b/lerna.json index dba380e9..d63b1ea3 100644 --- a/lerna.json +++ b/lerna.json @@ -1,12 +1,8 @@ { - "packages": [ - "core/*", - "plugin/*", - "standalone/*" - ], - "version": "independent", + "useWorkspaces": true, + "version": "3.23.0", "npmClientArgs": [ "--package-lock=false" ], - "lerna": "3.22.1" + "lerna": "1.6.4" } diff --git a/package.json b/package.json index ed48b734..b37e2b4c 100644 --- a/package.json +++ b/package.json @@ -17,33 +17,30 @@ "scripts": { "lint:fix": "eslint . --ext .ts --fix", "lint": "eslint . --ext .ts", - "test": "cross-env NODE_ENV=test TS_NODE_TRANSPILE_ONLY=true mocha", - "cov": "npm run clean && nyc npm run test", - "pub": "lerna publish", + "test": "npm run test --workspaces --if-present", + "cov": "nyc npm run test", + "bump": "lerna version --conventional-commits --sign-git-commit --sign-git-tag --force-publish", + "pub": "lerna publish from-git", "pub-canary": "lerna publish --canary", "clean": "lerna run --parallel clean", + "clean-workspaces": "npm run clean --workspaces --if-present", "prepare-test": "lerna run --parallel prepare-test", "tsc": "lerna run tsc", + "tsc-workspaces": "npm run tsc --workspaces --if-present", "tsc:pub": "lerna run tsc:pub", - "ci": "npm run prepare-test && npm run lint && npm run tsc:pub && npm run cov" + "tsc:pub-workspaces": "npm run tsc:pub --workspaces --if-present", + "ci": "npm run prepare-test && npm run lint && npm run cov" }, "author": "killagu ", "license": "MIT", "devDependencies": { "@eggjs/tsconfig": "^1.0.0", "@istanbuljs/nyc-config-typescript": "^1.0.1", - "@types/mocha": "^7.0.1", - "@types/node": "^14.14.31", - "cross-env": "^7.0.3", "eslint": "^8.0.0", "eslint-config-egg": "^12.0.0", "eslint-plugin-import": "^2.24.2", - "espower-typescript": "^9.0.2", - "intelli-espower-loader": "^1.0.1", - "lerna": "^3.22.1", - "mocha": "^7.1.0", - "nyc": "^15.0.0", - "power-assert": "^1.6.1", + "lerna": "^6.1.0", + "nyc": "^15.1.0", "source-map-support": "^0.5.16", "test-exclude": "^6.0.0", "ts-node": "^10.1.0", diff --git a/plugin/aop/CHANGELOG.md b/plugin/aop/CHANGELOG.md index 559f86b5..ecdf6a6b 100644 --- a/plugin/aop/CHANGELOG.md +++ b/plugin/aop/CHANGELOG.md @@ -3,6 +3,347 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + + +### Bug Fixes + +* fix aop plugin files ([#140](https://github.com/eggjs/tegg/issues/140)) ([f47eef6](https://github.com/eggjs/tegg/commit/f47eef634efd442ac5a8f68567e36c940247e48b)) + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + + +### Bug Fixes + +* init all context advice if root proto miss ([#139](https://github.com/eggjs/tegg/issues/139)) ([0602ea8](https://github.com/eggjs/tegg/commit/0602ea81578bf717ee4b4c490ace8c1c133478c5)) + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + + +### Features + +* implement advice params ([76ec8ad](https://github.com/eggjs/tegg/commit/76ec8ad7b7170a637e59d74d49c1f00d8a201321)) + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) + + +### Features + +* impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-aop-plugin + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + ## [1.3.9](https://github.com/eggjs/tegg/compare/@eggjs/tegg-aop-plugin@1.3.8...@eggjs/tegg-aop-plugin@1.3.9) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-aop-plugin diff --git a/plugin/aop/README.md b/plugin/aop/README.md index 67c0444f..e9f0a11c 100644 --- a/plugin/aop/README.md +++ b/plugin/aop/README.md @@ -61,7 +61,10 @@ import { ContextProto } from '@eggjs/tegg'; @ContextProto() export class Hello { - @Pointcut(AdviceExample) + + // 创建 Hello.hello 的切面 AdviceExample,并传递 adviceParams 给 AdviceExample + // AdviceExample 的切面函数可以通过 ctx.adviceParams 拿到注解传入的参数 + @Pointcut(AdviceExample, { adviceParams: { foo: 'bar' } }) async hello(name: string) { return `hello ${name}`; } @@ -81,11 +84,13 @@ export class Hello { import { Crosscut, Advice, IAdvice } from '@eggjs/tegg/aop'; // 通过类型来指定 +// 创建 CrosscutClassAdviceExample.hello 的切面 CrosscutExample,并传递 adviceParams 给 CrosscutExample +// CrosscutExample 的切面函数可以通过 ctx.adviceParams 拿到注解传入的参数 @Crosscut({ type: PointcutType.CLASS, clazz: CrosscutExample, methodName: 'hello', -}) +}, { adviceParams: { foo: 'bar' } }) @Advice() export class CrosscutClassAdviceExample implements IAdvice { } diff --git a/plugin/aop/app.ts b/plugin/aop/app.ts index 7b4cb9c6..d342300d 100644 --- a/plugin/aop/app.ts +++ b/plugin/aop/app.ts @@ -1,6 +1,7 @@ import { Application } from 'egg'; import { CrosscutAdviceFactory } from '@eggjs/tegg/aop'; import { EggObjectAopHook, EggPrototypeCrossCutHook, LoadUnitAopHook } from '@eggjs/tegg-aop-runtime'; +import { AopContextHook } from './lib/AopContextHook'; export default class AopAppHook { private readonly app: Application; @@ -9,6 +10,7 @@ export default class AopAppHook { private readonly loadUnitAopHook: LoadUnitAopHook; private readonly eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; private readonly eggObjectAopHook: EggObjectAopHook; + private aopContextHook: AopContextHook; constructor(app) { this.app = app; @@ -24,9 +26,16 @@ export default class AopAppHook { this.app.eggObjectLifecycleUtil.registerLifecycle(this.eggObjectAopHook); } + async didLoad() { + await this.app.moduleHandler.ready(); + this.aopContextHook = new AopContextHook(this.app.moduleHandler); + this.app.eggContextLifecycleUtil.registerLifecycle(this.aopContextHook); + } + beforeClose() { this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.eggPrototypeCrossCutHook); this.app.loadUnitLifecycleUtil.deleteLifecycle(this.loadUnitAopHook); this.app.eggObjectLifecycleUtil.deleteLifecycle(this.eggObjectAopHook); + this.app.eggContextLifecycleUtil.deleteLifecycle(this.aopContextHook); } } diff --git a/plugin/aop/lib/AopContextHook.ts b/plugin/aop/lib/AopContextHook.ts new file mode 100644 index 00000000..487031c7 --- /dev/null +++ b/plugin/aop/lib/AopContextHook.ts @@ -0,0 +1,58 @@ +import type { Application } from 'egg'; +import type { EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; +import type { EggProtoImplClass, LifecycleHook } from '@eggjs/tegg'; +import { PrototypeUtil, ObjectInitType } from '@eggjs/tegg'; +import { AspectInfoUtil } from '@eggjs/aop-decorator'; +import { EggPrototype, TeggError } from '@eggjs/tegg-metadata'; +import { ROOT_PROTO } from '@eggjs/egg-module-common'; + +export interface EggPrototypeWithClazz extends EggPrototype { + clazz?: EggProtoImplClass; +} + +export interface ProtoToCreate { + name: string; + proto: EggPrototype; +} + +export class AopContextHook implements LifecycleHook { + private readonly moduleHandler: Application['moduleHandler']; + private requestProtoList: Array = []; + + constructor(moduleHandler: Application['moduleHandler']) { + this.moduleHandler = moduleHandler; + for (const loadUnitInstance of this.moduleHandler.loadUnitInstances) { + const iterator = loadUnitInstance.loadUnit.iterateEggPrototype(); + for (const proto of iterator) { + const protoWithClazz = proto as EggPrototypeWithClazz; + const clazz = protoWithClazz.clazz; + if (!clazz) continue; + const aspects = AspectInfoUtil.getAspectList(clazz); + for (const aspect of aspects) { + for (const advice of aspect.adviceList) { + const adviceProto = PrototypeUtil.getClazzProto(advice.clazz) as EggPrototype | undefined; + if (!adviceProto) { + throw TeggError.create(`Aop Advice(${advice.clazz.name}) not found in loadUnits`, 'advice_not_found'); + } + if (adviceProto.initType === ObjectInitType.CONTEXT) { + this.requestProtoList.push({ + name: advice.name, + proto: adviceProto, + }); + } + } + } + } + } + } + + async preCreate(_, ctx: EggContext): Promise { + // compatible with egg controller + // add context aspect to ctx + if (!ctx.get(ROOT_PROTO)) { + for (const proto of this.requestProtoList) { + ctx.addProtoToCreate(proto.name, proto.proto); + } + } + } +} diff --git a/plugin/aop/package.json b/plugin/aop/package.json index be6019fa..d62a3415 100644 --- a/plugin/aop/package.json +++ b/plugin/aop/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/tegg-aop-plugin", - "version": "1.4.0", + "version": "3.23.0", "eggPlugin": { "name": "aopModule", "dependencies": [ @@ -18,14 +18,16 @@ ], "files": [ "app.js", - "app.d.ts" + "app.d.ts", + "lib/**/*.js", + "lib/**/*.d.ts" ], "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -40,14 +42,23 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/tegg": "^1.5.0", - "@eggjs/tegg-aop-runtime": "^1.5.0" + "@eggjs/aop-decorator": "^3.23.0", + "@eggjs/egg-module-common": "^3.23.0", + "@eggjs/tegg": "^3.23.0", + "@eggjs/tegg-aop-runtime": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0" }, "devDependencies": { - "@eggjs/tegg-config": "^1.3.0", - "@eggjs/tegg-plugin": "^1.4.0", - "egg": "^2.29.4", - "egg-mock": "^4.0.1" + "@eggjs/tegg-config": "^3.23.0", + "@eggjs/tegg-plugin": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "egg-mock": "^5.5.0", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/plugin/aop/test/aop.test.ts b/plugin/aop/test/aop.test.ts index 762af406..dca10c46 100644 --- a/plugin/aop/test/aop.test.ts +++ b/plugin/aop/test/aop.test.ts @@ -34,4 +34,14 @@ describe('test/aop.test.ts', () => { msg: 'withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(foo))))', }); }); + + it('module aop should work', async () => { + app.mockCsrf(); + const res = await app.httpRequest() + .get('/singletonAop') + .expect(200); + assert.deepStrictEqual(res.body, { + msg: 'withContextPointAroundResult(hello withContextPointAroundParam(foo))', + }); + }); }); diff --git a/plugin/aop/test/fixtures/apps/aop-app/app/controller/app.ts b/plugin/aop/test/fixtures/apps/aop-app/app/controller/app.ts index 98df2967..b0c45602 100644 --- a/plugin/aop/test/fixtures/apps/aop-app/app/controller/app.ts +++ b/plugin/aop/test/fixtures/apps/aop-app/app/controller/app.ts @@ -8,4 +8,11 @@ export default class App extends Controller { this.ctx.status = 200; this.ctx.body = { msg }; } + + async contextAdviceWithSingleton() { + const hello: Hello = await (this.app.module as any).aopModule.singletonHello; + const msg = await hello.hello('foo'); + this.ctx.status = 200; + this.ctx.body = { msg }; + } } diff --git a/plugin/aop/test/fixtures/apps/aop-app/app/router.ts b/plugin/aop/test/fixtures/apps/aop-app/app/router.ts index 2066130d..e2ec5e36 100644 --- a/plugin/aop/test/fixtures/apps/aop-app/app/router.ts +++ b/plugin/aop/test/fixtures/apps/aop-app/app/router.ts @@ -2,4 +2,5 @@ import { Application } from 'egg'; module.exports = (app: Application) => { app.router.get('/aop', app.controller.app.aop); + app.router.get('/singletonAop', app.controller.app.contextAdviceWithSingleton); }; diff --git a/plugin/aop/test/fixtures/apps/aop-app/modules/aop-module/Hello.ts b/plugin/aop/test/fixtures/apps/aop-app/modules/aop-module/Hello.ts index 730f0a83..293a6cb0 100644 --- a/plugin/aop/test/fixtures/apps/aop-app/modules/aop-module/Hello.ts +++ b/plugin/aop/test/fixtures/apps/aop-app/modules/aop-module/Hello.ts @@ -1,4 +1,4 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; import { Advice, AdviceContext, Crosscut, IAdvice, Pointcut, PointcutType } from '@eggjs/tegg/aop'; import { EggLogger } from 'egg'; @@ -43,3 +43,32 @@ export class CrosscutAdvice implements IAdvice { return `withCrossAroundResult(${result})`; } } + + +@Advice() +export class ContextPointcutAdvice implements IAdvice { + async around(ctx: AdviceContext, next: () => Promise): Promise { + ctx.args[0] = `withContextPointAroundParam(${ctx.args[0]})`; + const result = await next(); + return `withContextPointAroundResult(${result})`; + } +} + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class SingletonHello { + id = 233; + + @Inject() + logger: EggLogger; + + @Pointcut(ContextPointcutAdvice) + async hello(name: string) { + return `hello ${name}`; + } + + async helloEggObjectAop() { + this.logger.info('foo'); + } +} diff --git a/plugin/aop/tsconfig.json b/plugin/aop/tsconfig.json index 74935f4b..8205bd19 100644 --- a/plugin/aop/tsconfig.json +++ b/plugin/aop/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": "./" }, "exclude": [ - "node_modules" + "node_modules", + "test" ] } diff --git a/plugin/common/CHANGELOG.md b/plugin/common/CHANGELOG.md index 82b33042..a101ae04 100644 --- a/plugin/common/CHANGELOG.md +++ b/plugin/common/CHANGELOG.md @@ -3,6 +3,161 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/egg-module-common + + + + + # [0.2.0](https://github.com/eggjs/tegg/compare/v0.1.19...v0.2.0) (2022-01-20) **Note:** Version bump only for package @eggjs/egg-module-common diff --git a/plugin/common/package.json b/plugin/common/package.json index 65c5de46..7b1c542d 100644 --- a/plugin/common/package.json +++ b/plugin/common/package.json @@ -1,6 +1,6 @@ { "name": "@eggjs/egg-module-common", - "version": "1.0.0", + "version": "3.23.0", "description": "common module", "keywords": [ "egg", @@ -18,8 +18,7 @@ "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "author": "killagu ", "license": "MIT", @@ -37,5 +36,13 @@ }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/plugin/common/tsconfig.json b/plugin/common/tsconfig.json index 74935f4b..8205bd19 100644 --- a/plugin/common/tsconfig.json +++ b/plugin/common/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": "./" }, "exclude": [ - "node_modules" + "node_modules", + "test" ] } diff --git a/plugin/config/CHANGELOG.md b/plugin/config/CHANGELOG.md index bdaf3ff1..59b157dd 100644 --- a/plugin/config/CHANGELOG.md +++ b/plugin/config/CHANGELOG.md @@ -3,6 +3,217 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + + +### Features + +* support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + + +### Features + +* The exposed module reads the options. ([#112](https://github.com/eggjs/tegg/issues/112)) ([a52b44b](https://github.com/eggjs/tegg/commit/a52b44b753463bfdef6fbbc39f920be8eccf1567)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-config + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* fix miss agent file ([0fa496b](https://github.com/eggjs/tegg/commit/0fa496bdbb4ffa4e911fffa3e176fa7bdf03fb12)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + ## [1.2.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-config@1.2.0...@eggjs/tegg-config@1.2.1) (2022-09-05) diff --git a/plugin/config/app.ts b/plugin/config/app.ts index 83be8a02..6e27c539 100644 --- a/plugin/config/app.ts +++ b/plugin/config/app.ts @@ -9,18 +9,20 @@ export default class App { } configWillLoad() { - this.app.moduleReferences = ModuleConfigUtil.readModuleReference(this.app.baseDir); + const { readModuleOptions } = this.app.config.tegg || {}; + this.app.moduleReferences = ModuleConfigUtil.readModuleReference(this.app.baseDir, readModuleOptions || {}); this.app.moduleConfigs = {}; for (const reference of this.app.moduleReferences) { const absoluteRef = { path: ModuleConfigUtil.resolveModuleDir(reference.path, this.app.baseDir), + name: reference.name, }; const moduleName = ModuleConfigUtil.readModuleNameSync(absoluteRef.path); this.app.moduleConfigs[moduleName] = { name: moduleName, reference: absoluteRef, - config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path) || {}, + config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path, undefined, this.app.config.env) || {}, }; } } diff --git a/plugin/config/package.json b/plugin/config/package.json index 8866083c..ac01bbcc 100644 --- a/plugin/config/package.json +++ b/plugin/config/package.json @@ -3,7 +3,7 @@ "eggPlugin": { "name": "teggConfig" }, - "version": "1.3.0", + "version": "3.23.0", "description": "module config plugin for egg", "keywords": [ "egg", @@ -22,11 +22,11 @@ ], "types": "typings/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -41,10 +41,16 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/tegg-common-util": "^1.2.0" + "@eggjs/tegg-common-util": "^3.23.0" }, "devDependencies": { - "egg": "^2.26.1" + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/plugin/config/test/ReadModule.test.ts b/plugin/config/test/ReadModule.test.ts new file mode 100644 index 00000000..a62c21f6 --- /dev/null +++ b/plugin/config/test/ReadModule.test.ts @@ -0,0 +1,41 @@ +import mm from 'egg-mock'; +import assert from 'assert'; +import path from 'path'; + +describe('test/ReadModule.test.ts', () => { + let app; + const fixturesPath = path.join(__dirname, './fixtures/apps/app-with-modules'); + + after(async () => { + await app.close(); + }); + + afterEach(() => { + mm.restore(); + }); + + before(async () => { + mm(process.env, 'EGG_TYPESCRIPT', true); + mm(process, 'cwd', () => { + return path.join(__dirname, '..'); + }); + app = mm.app({ + baseDir: fixturesPath, + framework: require.resolve('egg'), + }); + await app.ready(); + }); + + it('should work', async () => { + assert.deepStrictEqual(app.moduleConfigs, { + moduleA: { + config: {}, + name: 'moduleA', + reference: { + name: 'moduleA', + path: path.join(fixturesPath, 'app/module-a'), + }, + }, + }); + }); +}); diff --git a/plugin/config/test/fixtures/apps/app-with-modules/app/module-a/package.json b/plugin/config/test/fixtures/apps/app-with-modules/app/module-a/package.json new file mode 100644 index 00000000..b9be02c6 --- /dev/null +++ b/plugin/config/test/fixtures/apps/app-with-modules/app/module-a/package.json @@ -0,0 +1,6 @@ +{ + "name": "module-a", + "eggModule": { + "name": "moduleA" + } +} diff --git a/plugin/config/test/fixtures/apps/app-with-modules/config/config.default.ts b/plugin/config/test/fixtures/apps/app-with-modules/config/config.default.ts new file mode 100644 index 00000000..721eeeca --- /dev/null +++ b/plugin/config/test/fixtures/apps/app-with-modules/config/config.default.ts @@ -0,0 +1,9 @@ +export default () => { + return { + tegg: { + readModuleOptions: { + extraFilePattern: [ '!**/dist' ], + }, + }, + }; +}; diff --git a/plugin/config/test/fixtures/apps/app-with-modules/package.json b/plugin/config/test/fixtures/apps/app-with-modules/package.json new file mode 100644 index 00000000..bde99de9 --- /dev/null +++ b/plugin/config/test/fixtures/apps/app-with-modules/package.json @@ -0,0 +1,3 @@ +{ + "name": "foo" +} diff --git a/plugin/config/tsconfig.json b/plugin/config/tsconfig.json index 74935f4b..8205bd19 100644 --- a/plugin/config/tsconfig.json +++ b/plugin/config/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": "./" }, "exclude": [ - "node_modules" + "node_modules", + "test" ] } diff --git a/plugin/controller/CHANGELOG.md b/plugin/controller/CHANGELOG.md index 88ccb6f6..1e0cf539 100644 --- a/plugin/controller/CHANGELOG.md +++ b/plugin/controller/CHANGELOG.md @@ -3,6 +3,367 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + + +### Bug Fixes + +* ensure ContextInitiator be called after ctx ready ([#138](https://github.com/eggjs/tegg/issues/138)) ([79e16da](https://github.com/eggjs/tegg/commit/79e16dae913b6114ac8d13bde8de60164d57dab3)) + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + + +### Bug Fixes + +* router type ([#83](https://github.com/eggjs/tegg/issues/83)) ([b32d9b8](https://github.com/eggjs/tegg/commit/b32d9b8e94552d27dc0249c9f38e7223b24beff0)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-controller-plugin + + + + + +# [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) + + +### Features + +* impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* add 'globby' as dependencies ([#71](https://github.com/eggjs/tegg/issues/71)) ([76d85d9](https://github.com/eggjs/tegg/commit/76d85d9948527028f926ae0ff5a61111eb1cbd04)) +* fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) +* middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) +* multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + + +### Features + +* delete controller root hook ([bbb68f4](https://github.com/eggjs/tegg/commit/bbb68f43a1a9fcfd86c05581b10c56eeb77d4053)) + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* fix rootProtoManager.registerRootProto ([f416ed7](https://github.com/eggjs/tegg/commit/f416ed70af1c46d31ebf712b208205d67337d958)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Host decorator ([#48](https://github.com/eggjs/tegg/issues/48)) ([65dc7a8](https://github.com/eggjs/tegg/commit/65dc7a899ba72dd0851c35046562766d7f2b71b6)) +* middleware decorator allow multi middleware function ([#46](https://github.com/eggjs/tegg/issues/46)) ([a4b55f7](https://github.com/eggjs/tegg/commit/a4b55f7065c3d78e2c98c4b05f01871f666542ef)) +* multi host decorator ([#68](https://github.com/eggjs/tegg/issues/68)) ([f6679de](https://github.com/eggjs/tegg/commit/f6679de1495024ecb9182168843300aa91288508)) + + + + + ## [1.5.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-controller-plugin@1.5.2...@eggjs/tegg-controller-plugin@1.5.3) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-controller-plugin diff --git a/plugin/controller/README.md b/plugin/controller/README.md index 8c61a2fb..351ded61 100644 --- a/plugin/controller/README.md +++ b/plugin/controller/README.md @@ -48,13 +48,14 @@ Middleware 支持多个入参,依次传入要生效的中间件 ```ts // app/middleware/global_log.ts import { Context } from 'egg'; +import type { Next } from '@eggjs/controller-decorator'; -export default async function globalLog(ctx: Context, next: any) { +export default async function globalLog(ctx: Context, next: Next) { ctx.logger.info('have a request'); return next(); } -export default async function globalLog2(ctx: Context, next: any) { +export default async function globalLog2(ctx: Context, next: Next) { ctx.logger.info('have a request2'); return next(); } diff --git a/plugin/controller/app.ts b/plugin/controller/app.ts index e8ed97f4..fd7b87a1 100644 --- a/plugin/controller/app.ts +++ b/plugin/controller/app.ts @@ -1,14 +1,12 @@ import { Application } from 'egg'; import { CONTROLLER_LOAD_UNIT, ControllerLoadUnit } from './lib/ControllerLoadUnit'; -import { ControllerLoadUnitInstance } from './lib/ControllerLoadUnitInstance'; import { AppLoadUnitControllerHook } from './lib/AppLoadUnitControllerHook'; import { LoadUnitLifecycleContext } from '@eggjs/tegg-metadata'; import { ControllerMetaBuilderFactory, ControllerType } from '@eggjs/tegg'; import { HTTPControllerRegister } from './lib/impl/http/HTTPControllerRegister'; import { ControllerRegisterFactory } from './lib/ControllerRegisterFactory'; import { ControllerLoadUnitHandler } from './lib/ControllerLoadUnitHandler'; -import { EggControllerHook } from './lib/EggControllerHook'; -import { LoadUnitInstanceLifecycleContext } from '@eggjs/tegg-runtime'; +import { LoadUnitInstanceLifecycleContext, ModuleLoadUnitInstance } from '@eggjs/tegg-runtime'; import { ControllerMetadataManager } from './lib/ControllerMetadataManager'; import { EggControllerPrototypeHook } from './lib/EggControllerPrototypeHook'; import { RootProtoManager } from './lib/RootProtoManager'; @@ -22,7 +20,6 @@ import { EggControllerLoader } from './lib/EggControllerLoader'; export default class ControllerAppBootHook { private readonly app: Application; private readonly loadUnitHook: AppLoadUnitControllerHook; - private controllerHook: EggControllerHook; private readonly controllerRegisterFactory: ControllerRegisterFactory; private controllerLoadUnitHandler: ControllerLoadUnitHandler; private readonly controllerPrototypeHook: EggControllerPrototypeHook; @@ -57,8 +54,8 @@ export default class ControllerAppBootHook { }); this.app.loadUnitInstanceFactory.registerLoadUnitInstanceClass( CONTROLLER_LOAD_UNIT, - (ctx: LoadUnitInstanceLifecycleContext): ControllerLoadUnitInstance => { - return new ControllerLoadUnitInstance(ctx.loadUnit, this.app.loadUnitInstanceLifecycleUtil); + (ctx: LoadUnitInstanceLifecycleContext): ModuleLoadUnitInstance => { + return new ModuleLoadUnitInstance(ctx.loadUnit); }, ); @@ -80,8 +77,6 @@ export default class ControllerAppBootHook { await this.app.moduleHandler.ready(); this.controllerLoadUnitHandler = new ControllerLoadUnitHandler(this.app); await this.controllerLoadUnitHandler.ready(); - this.controllerHook = new EggControllerHook(); - this.app.eggContextLifecycleUtil.registerLifecycle(this.controllerHook); // The real register HTTP controller/method. // HTTP method should sort by priority @@ -97,9 +92,6 @@ export default class ControllerAppBootHook { this.app.loadUnitLifecycleUtil.deleteLifecycle(this.loadUnitHook); this.app.eggPrototypeLifecycleUtil.deleteLifecycle(this.controllerPrototypeHook); ControllerMetadataManager.instance.clear(); - if (this.controllerHook) { - this.app.eggContextLifecycleUtil.deleteLifecycle(this.controllerHook); - } HTTPControllerRegister.clean(); } } diff --git a/plugin/controller/lib/ControllerLoadUnit.ts b/plugin/controller/lib/ControllerLoadUnit.ts index 510f4e46..0c8776ae 100644 --- a/plugin/controller/lib/ControllerLoadUnit.ts +++ b/plugin/controller/lib/ControllerLoadUnit.ts @@ -44,8 +44,10 @@ export class ControllerLoadUnit implements LoadUnit { async init() { const clazzList = this.loader.load(); for (const clazz of clazzList) { - const proto = await this.eggPrototypeCreatorFactory.createProto(clazz, this); - this.eggPrototypeFactory.registerPrototype(proto, this); + const protos = await this.eggPrototypeCreatorFactory.createProto(clazz, this); + for (const proto of protos) { + this.eggPrototypeFactory.registerPrototype(proto, this); + } } } diff --git a/plugin/controller/lib/ControllerLoadUnitInstance.ts b/plugin/controller/lib/ControllerLoadUnitInstance.ts index 5d67dd02..98b2b997 100644 --- a/plugin/controller/lib/ControllerLoadUnitInstance.ts +++ b/plugin/controller/lib/ControllerLoadUnitInstance.ts @@ -37,10 +37,6 @@ export class ControllerLoadUnitInstance implements LoadUnitInstance { await this.loadUnitInstanceLifecycleUtil.objectPostCreate(ctx, this); } - async destroy(ctx: LoadUnitInstanceLifecycleContext): Promise { - await this.loadUnitInstanceLifecycleUtil.objectPreDestroy(ctx, this); - } - async getOrCreateEggObject(): Promise { throw new Error('controller load unit not allow have singleton proto'); } diff --git a/plugin/controller/lib/EggControllerHook.ts b/plugin/controller/lib/EggControllerHook.ts deleted file mode 100644 index d0aa339a..00000000 --- a/plugin/controller/lib/EggControllerHook.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; -import { EggPrototype } from '@eggjs/tegg-metadata'; -import { LifecycleHook } from '@eggjs/tegg'; -import { ROOT_PROTO } from '@eggjs/egg-module-common'; - -export class EggControllerHook implements LifecycleHook { - async preCreate(_, ctx: EggContext): Promise { - const rootProto = ctx.get(ROOT_PROTO); - if (rootProto) { - const proto: EggPrototype = rootProto; - ctx.addProtoToCreate(proto.name, proto); - } - } -} diff --git a/plugin/controller/lib/impl/http/HTTPControllerRegister.ts b/plugin/controller/lib/impl/http/HTTPControllerRegister.ts index 6778c5a9..0c174b3e 100644 --- a/plugin/controller/lib/impl/http/HTTPControllerRegister.ts +++ b/plugin/controller/lib/impl/http/HTTPControllerRegister.ts @@ -1,6 +1,5 @@ import assert from 'assert'; -import KoaRouter from 'koa-router'; -import { Application, Context } from 'egg'; +import { Application, Router } from 'egg'; import { CONTROLLER_META_DATA, ControllerMetadata, @@ -17,8 +16,8 @@ import { RootProtoManager } from '../../RootProtoManager'; export class HTTPControllerRegister implements ControllerRegister { static instance?: HTTPControllerRegister; - private readonly router: KoaRouter; - private readonly checkRouters: Map>; + private readonly router: Router; + private readonly checkRouters: Map; private readonly eggContainerFactory: typeof EggContainerFactory; private controllerProtos: EggPrototype[] = []; @@ -31,7 +30,7 @@ export class HTTPControllerRegister implements ControllerRegister { return HTTPControllerRegister.instance; } - constructor(router: KoaRouter, eggContainerFactory: typeof EggContainerFactory) { + constructor(router: Router, eggContainerFactory: typeof EggContainerFactory) { this.router = router; this.checkRouters = new Map(); this.checkRouters.set('default', router); diff --git a/plugin/controller/lib/impl/http/HTTPMethodRegister.ts b/plugin/controller/lib/impl/http/HTTPMethodRegister.ts index 912b2bda..4e9b98b6 100644 --- a/plugin/controller/lib/impl/http/HTTPMethodRegister.ts +++ b/plugin/controller/lib/impl/http/HTTPMethodRegister.ts @@ -1,6 +1,5 @@ import assert from 'assert'; -import KoaRouter from 'koa-router'; -import { Context } from 'egg'; +import { Context, Router } from 'egg'; import { EggContext, HTTPControllerMeta, @@ -13,7 +12,6 @@ import { } from '@eggjs/tegg'; import { EggContainerFactory } from '@eggjs/tegg-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; -import { TEGG_CONTEXT } from '@eggjs/egg-module-common'; import { RootProtoManager } from '../../RootProtoManager'; import pathToRegexp from 'path-to-regexp'; import { aclMiddlewareFactory } from './Acl'; @@ -27,8 +25,8 @@ const noop = () => { }; export class HTTPMethodRegister { - private readonly router: KoaRouter; - private readonly checkRouters: Map>; + private readonly router: Router; + private readonly checkRouters: Map; private readonly controllerMeta: HTTPControllerMeta; private readonly methodMeta: HTTPMethodMeta; private readonly proto: EggPrototype; @@ -38,8 +36,8 @@ export class HTTPMethodRegister { proto: EggPrototype, controllerMeta: HTTPControllerMeta, methodMeta: HTTPMethodMeta, - router: KoaRouter, - checkRouters: Map>, + router: Router, + checkRouters: Map, eggContainerFactory: typeof EggContainerFactory, ) { this.proto = proto; @@ -63,7 +61,7 @@ export class HTTPMethodRegister { } // HTTP decorator core implement // use controller metadata map http request to function arguments - const eggObj = self.eggContainerFactory.getEggObject(self.proto, self.proto.name, (ctx as any)[TEGG_CONTEXT]); + const eggObj = await self.eggContainerFactory.getOrCreateEggObject(self.proto, self.proto.name); const realObj = eggObj.obj; const realMethod = realObj[methodMeta.name]; const args: Array = new Array(methodArgsLength); @@ -123,28 +121,30 @@ export class HTTPMethodRegister { // 2. check duplicate with host tegg controller let hostRouter; - const host = this.controllerMeta.getMethodHost(this.methodMeta); - if (host) { - hostRouter = this.checkRouters.get(host); - if (!hostRouter) { - hostRouter = new EggRouter({ sensitive: true }, {}); - this.checkRouters.set(host, hostRouter!); + const hosts = this.controllerMeta.getMethodHosts(this.methodMeta) || []; + hosts.forEach(h => { + if (h) { + hostRouter = this.checkRouters.get(h); + if (!hostRouter) { + hostRouter = new EggRouter({ sensitive: true }, {}); + this.checkRouters.set(h, hostRouter!); + } } - } - if (hostRouter) { - this.checkDuplicateInRouter(hostRouter); - this.registerToRouter(hostRouter); - } + if (hostRouter) { + this.checkDuplicateInRouter(hostRouter); + this.registerToRouter(hostRouter); + } + }); } - private registerToRouter(router: KoaRouter) { + private registerToRouter(router: Router) { const routerFunc = router[this.methodMeta.method.toLowerCase()]; const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const methodName = this.controllerMeta.getMethodName(this.methodMeta); Reflect.apply(routerFunc, router, [ methodName, methodRealPath, noop ]); } - private checkDuplicateInRouter(router: KoaRouter) { + private checkDuplicateInRouter(router: Router) { const methodRealPath = this.controllerMeta.getMethodRealPath(this.methodMeta); const matched = router.match(methodRealPath, this.methodMeta.method); const methodName = this.controllerMeta.getMethodName(this.methodMeta); @@ -164,19 +164,20 @@ export class HTTPMethodRegister { if (aclMiddleware) { methodMiddlewares.push(aclMiddleware); } - const host = this.controllerMeta.getMethodHost(this.methodMeta); - const handler = this.createHandler(this.methodMeta, host); - Reflect.apply(routerFunc, this.router, - [ methodName, methodRealPath, ...methodMiddlewares, handler ]); - - // https://github.com/eggjs/egg-core/blob/0af6178022e7734c4a8b17bb56d592b315207883/lib/egg.js#L279 - const regExp = pathToRegexp(methodRealPath, { - sensitive: true, + const hosts = this.controllerMeta.getMethodHosts(this.methodMeta) || [ undefined ]; + hosts.forEach(h => { + const handler = this.createHandler(this.methodMeta, h); + Reflect.apply(routerFunc, this.router, + [ methodName, methodRealPath, ...methodMiddlewares, handler ]); + // https://github.com/eggjs/egg-core/blob/0af6178022e7734c4a8b17bb56d592b315207883/lib/egg.js#L279 + const regExp = pathToRegexp(methodRealPath, { + sensitive: true, + }); + rootProtoManager.registerRootProto(this.methodMeta.method, (ctx: EggContext) => { + if (regExp.test(ctx.path)) { + return this.proto; + } + }, h || ''); }); - rootProtoManager.registerRootProto(this.methodMeta.method, (ctx: EggContext) => { - if (regExp.test(ctx.path)) { - return this.proto; - } - }, host || ''); } } diff --git a/plugin/controller/package.json b/plugin/controller/package.json index 29c7332b..2afa4c05 100644 --- a/plugin/controller/package.json +++ b/plugin/controller/package.json @@ -7,7 +7,7 @@ "tegg" ] }, - "version": "1.6.0", + "version": "3.23.0", "description": "controller decorator for egg", "keywords": [ "egg", @@ -27,11 +27,11 @@ ], "types": "typings/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -46,26 +46,33 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/egg-module-common": "^1.0.0", - "@eggjs/tegg": "^1.5.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0", - "@types/koa-router": "^7.0.40", + "@eggjs/egg-module-common": "^3.23.0", + "@eggjs/router": "^2.0.1", + "@eggjs/tegg": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", "egg-errors": "^2.3.0", + "globby": "^10.0.2", "koa-compose": "^3.2.1", - "path-to-regexp": "^1.8.0" + "path-to-regexp": "^1.8.0", + "sdk-base": "^4.2.0" }, "devDependencies": { - "@eggjs/module-test-util": "^1.4.0", - "@eggjs/router": "^2.0.0", - "@eggjs/tegg-config": "^1.3.0", - "@eggjs/tegg-plugin": "^1.4.0", - "egg": "^2.26.0", - "egg-mock": "^3.25.1", - "egg-tracer": "^1.1.0", - "koa-router": "^8.0.8" + "@eggjs/module-test-util": "^3.23.0", + "@eggjs/tegg-config": "^3.23.0", + "@eggjs/tegg-plugin": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "egg-mock": "^5.5.0", + "egg-tracer": "^2.0.0", + "globby": "^11.1.0", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/plugin/controller/test/fixtures/apps/controller-app/app/controller/MiddlewareController.ts b/plugin/controller/test/fixtures/apps/controller-app/app/controller/MiddlewareController.ts index d044053b..0d26292a 100644 --- a/plugin/controller/test/fixtures/apps/controller-app/app/controller/MiddlewareController.ts +++ b/plugin/controller/test/fixtures/apps/controller-app/app/controller/MiddlewareController.ts @@ -8,6 +8,7 @@ import { import AppService from '../../modules/multi-module-service/AppService'; import { countMw } from '../middleware/count_mw'; import { logMwFactory } from '../middleware/log_mw'; +import { callModuleCtx } from '../middleware/call_module'; @HTTPController({ path: '/middleware', @@ -33,4 +34,13 @@ export class MiddlewareController { async middleware() { return {}; } + + @HTTPMethod({ + method: HTTPMethodEnum.GET, + path: '/methodCallModule', + }) + @Middleware(callModuleCtx) + async middlewareCallModule() { + return {}; + } } diff --git a/plugin/controller/test/fixtures/apps/controller-app/app/middleware/call_module.ts b/plugin/controller/test/fixtures/apps/controller-app/app/middleware/call_module.ts new file mode 100644 index 00000000..fc6d5c00 --- /dev/null +++ b/plugin/controller/test/fixtures/apps/controller-app/app/middleware/call_module.ts @@ -0,0 +1,7 @@ +import { Context } from 'egg'; +import { Next } from '@eggjs/tegg'; + +export async function callModuleCtx(ctx: Context, next: Next) { + await (ctx.module as any).multiModuleService.appService.findApp('foo'); + await next(); +} diff --git a/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/AppController2.ts b/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/AppController2.ts index b915ce0d..229416bb 100644 --- a/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/AppController2.ts +++ b/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/AppController2.ts @@ -5,7 +5,6 @@ import { Host, } from '@eggjs/tegg'; -@Host('bar.eggjs.com') @HTTPController({ path: '/apps', }) @@ -14,6 +13,7 @@ export class AppController2 { method: HTTPMethodEnum.GET, path: '/:id', }) + @Host('bar.eggjs.com') async get() { return 'bar'; } diff --git a/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiHostController.ts b/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiHostController.ts new file mode 100644 index 00000000..b5adf9af --- /dev/null +++ b/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiHostController.ts @@ -0,0 +1,29 @@ +import { + HTTPController, + HTTPMethod, + HTTPMethodEnum, + Host, +} from '@eggjs/tegg'; + +@Host([ 'apple.eggjs.com', 'a.eggjs.com' ]) +@HTTPController({ + controllerName: 'MultiHostController', + path: '/apps', +}) +export class MultiHostController { + @HTTPMethod({ + method: HTTPMethodEnum.GET, + path: '/apple', + }) + async apple() { + return 'apple'; + } + + @HTTPMethod({ + method: HTTPMethodEnum.GET, + path: '/a', + }) + async a() { + return 'a'; + } +} diff --git a/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiMethodHostController.ts b/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiMethodHostController.ts new file mode 100644 index 00000000..faca1497 --- /dev/null +++ b/plugin/controller/test/fixtures/apps/host-controller-app/app/controller/MultiMethodHostController.ts @@ -0,0 +1,30 @@ +import { + HTTPController, + HTTPMethod, + HTTPMethodEnum, + Host, +} from '@eggjs/tegg'; + +@HTTPController({ + controllerName: 'MultiMethodHostController', + path: '/apps', +}) +export class MultiMethodHostController { + @HTTPMethod({ + method: HTTPMethodEnum.GET, + path: '/orange', + }) + @Host([ 'orange.eggjs.com', 'o.eggjs.com' ]) + async orange() { + return 'orange'; + } + + @HTTPMethod({ + method: HTTPMethodEnum.GET, + path: '/juice', + }) + @Host('juice.eggjs.com') + async juice() { + return 'juice'; + } +} diff --git a/plugin/controller/test/http/host.test.ts b/plugin/controller/test/http/host.test.ts index 8f617817..8f574d67 100644 --- a/plugin/controller/test/http/host.test.ts +++ b/plugin/controller/test/http/host.test.ts @@ -51,4 +51,60 @@ describe('test/host.test.ts', () => { assert(res.text === 'bar'); }); }); + + it('multi class host should work', async () => { + app.mockCsrf(); + await app.httpRequest() + .get('/apps/apple') + .set('host', 'orange.eggjs.com') + .expect(404); + + await app.httpRequest() + .get('/apps/apple') + .set('host', 'apple.eggjs.com') + .expect(200) + .expect(res => { + assert(res.text === 'apple'); + }); + + await app.httpRequest() + .get('/apps/a') + .set('host', 'a.eggjs.com') + .expect(200) + .expect(res => { + assert(res.text === 'a'); + }); + }); + + it('method class host should work', async () => { + app.mockCsrf(); + await app.httpRequest() + .get('/apps/orange') + .set('host', 'o.eggjs.com') + .expect(200) + .expect(res => { + assert(res.text === 'orange'); + }); + + await app.httpRequest() + .get('/apps/orange') + .set('host', 'orange.eggjs.com') + .expect(200) + .expect(res => { + assert(res.text === 'orange'); + }); + + await app.httpRequest() + .get('/apps/juice') + .set('host', 'juice.eggjs.com') + .expect(200) + .expect(res => { + assert(res.text === 'juice'); + }); + + await app.httpRequest() + .get('/apps/juice') + .set('host', 'o.eggjs.com') + .expect(404); + }); }); diff --git a/plugin/controller/test/http/middleware.test.ts b/plugin/controller/test/http/middleware.test.ts index e79b569c..42b482c5 100644 --- a/plugin/controller/test/http/middleware.test.ts +++ b/plugin/controller/test/http/middleware.test.ts @@ -48,4 +48,11 @@ describe('test/middleware.test.ts', () => { assert(res.body.log === 'use middleware'); }); }); + + it('method middleware call module should work', async () => { + app.mockCsrf(); + await app.httpRequest() + .get('/middleware/methodCallModule') + .expect(200); + }); }); diff --git a/plugin/controller/tsconfig.json b/plugin/controller/tsconfig.json index 74935f4b..8205bd19 100644 --- a/plugin/controller/tsconfig.json +++ b/plugin/controller/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": "./" }, "exclude": [ - "node_modules" + "node_modules", + "test" ] } diff --git a/plugin/eventbus/CHANGELOG.md b/plugin/eventbus/CHANGELOG.md index 1f8c34ec..8ddabc3c 100644 --- a/plugin/eventbus/CHANGELOG.md +++ b/plugin/eventbus/CHANGELOG.md @@ -3,6 +3,363 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + + +### Bug Fixes + +* after call mockModuleContext, hasMockModuleContext should be true ([#134](https://github.com/eggjs/tegg/issues/134)) ([88b3caa](https://github.com/eggjs/tegg/commit/88b3caadd24f08221b8098c42733e26376338cae)) + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + + +### Bug Fixes + +* don't check eventbus plugin name ([#113](https://github.com/eggjs/tegg/issues/113)) ([2a94a57](https://github.com/eggjs/tegg/commit/2a94a57c58e4fd971400966c15597aace4bb1ecc)) + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + + +### Bug Fixes + +* eventbus cork should support reentry ([#98](https://github.com/eggjs/tegg/issues/98)) ([077044c](https://github.com/eggjs/tegg/commit/077044c040f8423572605eb2980e3cc6da8c038e)) + + + + + +## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + + +### Features + +* use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + + +### Bug Fixes + +* cork/uncork should can be called multi times in same ctx ([#78](https://github.com/eggjs/tegg/issues/78)) ([269cda6](https://github.com/eggjs/tegg/commit/269cda6327122111c230e6f69abb525ce4ab5be1)) + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) + + +### Features + +* impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-eventbus-plugin + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + ## [1.3.9](https://github.com/eggjs/tegg/compare/@eggjs/tegg-eventbus-plugin@1.3.8...@eggjs/tegg-eventbus-plugin@1.3.9) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-eventbus-plugin diff --git a/plugin/eventbus/lib/EggContextEventBus.ts b/plugin/eventbus/lib/EggContextEventBus.ts index f970ffed..4282ad99 100644 --- a/plugin/eventbus/lib/EggContextEventBus.ts +++ b/plugin/eventbus/lib/EggContextEventBus.ts @@ -1,20 +1,38 @@ +import assert from 'assert'; import { Context } from 'egg'; -import { EventBus, Events, PrototypeUtil } from '@eggjs/tegg'; +import { Events, PrototypeUtil, CORK_ID, ContextEventBus } from '@eggjs/tegg'; import { SingletonEventBus } from '@eggjs/tegg-eventbus-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; -import { EggContext } from '@eggjs/tegg-runtime'; +import { ContextHandler, EggContext } from '@eggjs/tegg-runtime'; -export class EggContextEventBus implements EventBus { +export class EggContextEventBus implements ContextEventBus { private readonly eventBus: SingletonEventBus; private readonly context: EggContext; + private corkId?: string; constructor(ctx: Context) { - this.context = ctx.teggContext; const proto = PrototypeUtil.getClazzProto(SingletonEventBus) as EggPrototype; - const eggObject = ctx.app.eggContainerFactory.getEggObject(proto, proto.name, this.context); + const eggObject = ctx.app.eggContainerFactory.getEggObject(proto, proto.name); + this.context = ContextHandler.getContext()!; this.eventBus = eggObject.obj as SingletonEventBus; } + cork() { + if (!this.corkId) { + this.corkId = this.eventBus.generateCorkId(); + this.context.set(CORK_ID, this.corkId); + } + this.eventBus.cork(this.corkId); + } + + uncork() { + assert(this.corkId, 'eventbus uncork without cork'); + if (this.eventBus.uncork(this.corkId)) { + this.context.set(CORK_ID, null); + this.corkId = undefined; + } + } + emit(event: E, ...args: any): boolean { return this.eventBus.emitWithContext(this.context, event, args); } diff --git a/plugin/eventbus/lib/EventbusLoadUnitHook.ts b/plugin/eventbus/lib/EventbusLoadUnitHook.ts index 8ae98d3b..f66bab9b 100644 --- a/plugin/eventbus/lib/EventbusLoadUnitHook.ts +++ b/plugin/eventbus/lib/EventbusLoadUnitHook.ts @@ -1,4 +1,4 @@ -import { LifecycleHook } from '@eggjs/tegg'; +import { EggQualifierAttribute, EggType, LifecycleHook, QualifierUtil } from '@eggjs/tegg'; import { EggLoadUnitType, EggPrototypeCreatorFactory, @@ -14,12 +14,17 @@ const REGISTER_CLAZZ = [ SingletonEventBus, ]; +// EggQualifier only for egg plugin +QualifierUtil.addProperQualifier(SingletonEventBus, 'logger', EggQualifierAttribute, EggType.APP); + export class EventbusLoadUnitHook implements LifecycleHook { async postCreate(_ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { if (loadUnit.type === EggLoadUnitType.APP) { for (const clazz of REGISTER_CLAZZ) { - const proto = await EggPrototypeCreatorFactory.createProto(clazz, loadUnit); - EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); + const protos = await EggPrototypeCreatorFactory.createProto(clazz, loadUnit); + for (const proto of protos) { + EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); + } } } } diff --git a/plugin/eventbus/package.json b/plugin/eventbus/package.json index 2ede4440..5992db36 100644 --- a/plugin/eventbus/package.json +++ b/plugin/eventbus/package.json @@ -1,8 +1,9 @@ { "name": "@eggjs/tegg-eventbus-plugin", - "version": "1.4.0", + "version": "3.23.0", "eggPlugin": { "name": "eventbusModule", + "strict": false, "dependencies": [ "tegg" ] @@ -28,11 +29,11 @@ ], "types": "typings/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -47,19 +48,26 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/egg-module-common": "^1.0.0", - "@eggjs/tegg": "^1.5.0", - "@eggjs/tegg-eventbus-runtime": "^1.5.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0", - "egg-tracer": "^1.1.0" + "@eggjs/egg-module-common": "^3.23.0", + "@eggjs/tegg": "^3.23.0", + "@eggjs/tegg-eventbus-runtime": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0" }, "devDependencies": { - "@eggjs/tegg-config": "^1.3.0", - "@eggjs/tegg-plugin": "^1.4.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-config": "^3.23.0", + "@eggjs/tegg-plugin": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", "await-event": "^2.1.0", - "egg": "^2.29.4", - "egg-mock": "^4.0.1" + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "egg-mock": "^5.5.0", + "egg-tracer": "^2.0.0", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/plugin/eventbus/test/eventbus.test.ts b/plugin/eventbus/test/eventbus.test.ts index 60502a41..73046948 100644 --- a/plugin/eventbus/test/eventbus.test.ts +++ b/plugin/eventbus/test/eventbus.test.ts @@ -1,15 +1,14 @@ import assert from 'assert'; import path from 'path'; -import mm from 'egg-mock'; +import mm, { MockApplication } from 'egg-mock'; +import { TimerUtil } from '@eggjs/tegg-common-util'; import { HelloService } from './fixtures/apps/event-app/app/event-module/HelloService'; import { HelloLogger } from './fixtures/apps/event-app/app/event-module/HelloLogger'; describe('test/eventbus.test.ts', () => { - let app; - let ctx; + let app: MockApplication; afterEach(async () => { - await app.destroyModuleContext(ctx); mm.restore(); }); @@ -30,17 +29,114 @@ describe('test/eventbus.test.ts', () => { }); it('msg should work', async () => { - ctx = await app.mockModuleContext(); - const helloService = await ctx.getEggObject(HelloService); - let msg: string | undefined; + await app.mockModuleContextScope(async ctx => { + const helloService = await ctx.getEggObject(HelloService); + let msg: string | undefined; + // helloLogger is in child context + mm(HelloLogger.prototype, 'handle', m => { + msg = m; + }); + const eventWaiter = await app.getEventWaiter(); + const helloEvent = eventWaiter.await('helloEgg'); + helloService.hello(); + await helloEvent; + assert(msg === '01'); + }); + }); + + it('cork/uncork should work', async () => { + await app.mockModuleContextScope(async ctx => { + const helloService = await ctx.getEggObject(HelloService); + let helloTime = 0; + // helloLogger is in child context + mm(HelloLogger.prototype, 'handle', () => { + helloTime = Date.now(); + }); + helloService.cork(); + const triggerTime = Date.now(); + helloService.hello(); + + await TimerUtil.sleep(100); + helloService.uncork(); + + const eventWaiter = await app.getEventWaiter(); + const helloEvent = eventWaiter.await('helloEgg'); + await helloEvent; + assert(helloTime >= triggerTime + 100); + }); + }); + + it('can call cork/uncork multi times', async () => { + await app.mockModuleContextScope(async ctx => { + const helloService = await ctx.getEggObject(HelloService); + const eventWaiter = await app.getEventWaiter(); + + let helloCalled = 0; + // helloLogger is in child context + mm(HelloLogger.prototype, 'handle', () => { + helloCalled++; + }); + helloService.cork(); + helloService.hello(); + helloService.uncork(); + await eventWaiter.await('helloEgg'); + + helloService.cork(); + helloService.hello(); + helloService.uncork(); + await eventWaiter.await('helloEgg'); + + assert(helloCalled === 2); + }); + }); + + it('reentry cork/uncork should work', async () => { + await app.mockModuleContextScope(async ctx => { + const helloService = await ctx.getEggObject(HelloService); + const eventWaiter = await app.getEventWaiter(); + + let helloCalled = 0; + // helloLogger is in child context + mm(HelloLogger.prototype, 'handle', () => { + helloCalled++; + }); + helloService.cork(); + helloService.cork(); + helloService.hello(); + helloService.uncork(); + helloService.uncork(); + await eventWaiter.await('helloEgg'); + + assert(helloCalled === 1); + }); + }); + + it('concurrent cork/uncork should work', async () => { + let helloCalled = 0; // helloLogger is in child context - mm(HelloLogger.prototype, 'handle', m => { - msg = m; + mm(HelloLogger.prototype, 'handle', () => { + helloCalled++; }); - const eventWaiter = await app.getEventWaiter(); - const helloEvent = eventWaiter.await('helloEgg'); - helloService.hello(); - await helloEvent; - assert(msg === '01'); + await Promise.all([ + await app.mockModuleContextScope(async ctx => { + const helloService = await ctx.getEggObject(HelloService); + const eventWaiter = await app.getEventWaiter(); + helloService.cork(); + helloService.hello(); + await TimerUtil.sleep(100); + helloService.uncork(); + await eventWaiter.await('helloEgg'); + }), + await app.mockModuleContextScope(async ctx => { + const helloService = await ctx.getEggObject(HelloService); + const eventWaiter = await app.getEventWaiter(); + helloService.cork(); + helloService.hello(); + await TimerUtil.sleep(100); + helloService.uncork(); + await eventWaiter.await('helloEgg'); + }), + ]); + assert(helloCalled === 2); }); }); diff --git a/plugin/eventbus/test/fixtures/apps/event-app/app/event-module/HelloService.ts b/plugin/eventbus/test/fixtures/apps/event-app/app/event-module/HelloService.ts index 77d4e070..ec0de28b 100644 --- a/plugin/eventbus/test/fixtures/apps/event-app/app/event-module/HelloService.ts +++ b/plugin/eventbus/test/fixtures/apps/event-app/app/event-module/HelloService.ts @@ -1,4 +1,4 @@ -import { AccessLevel, ContextProto, Inject, EventBus } from '@eggjs/tegg'; +import { AccessLevel, ContextProto, Inject, ContextEventBus } from '@eggjs/tegg'; declare module '@eggjs/tegg' { interface Events { @@ -12,7 +12,15 @@ declare module '@eggjs/tegg' { }) export class HelloService { @Inject() - private readonly eventBus: EventBus; + private readonly eventBus: ContextEventBus; + + cork() { + this.eventBus.cork(); + } + + uncork() { + this.eventBus.uncork(); + } hello() { this.eventBus.emit('helloEgg', '01'); diff --git a/plugin/eventbus/tsconfig.json b/plugin/eventbus/tsconfig.json index 74935f4b..8205bd19 100644 --- a/plugin/eventbus/tsconfig.json +++ b/plugin/eventbus/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": "./" }, "exclude": [ - "node_modules" + "node_modules", + "test" ] } diff --git a/plugin/orm/CHANGELOG.md b/plugin/orm/CHANGELOG.md index 9e3d4374..6b6aa692 100644 --- a/plugin/orm/CHANGELOG.md +++ b/plugin/orm/CHANGELOG.md @@ -3,6 +3,351 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + + +### Features + +* use singleton model insteadof context ([#89](https://github.com/eggjs/tegg/issues/89)) ([cfdfc05](https://github.com/eggjs/tegg/commit/cfdfc05f13048806274de1a35b1207c073a8519d)) + + + + + +## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + + +### Features + +* export singleton orm client ([#82](https://github.com/eggjs/tegg/issues/82)) ([5320af7](https://github.com/eggjs/tegg/commit/5320af77d7e7c5c73b80560a576f2ce01fc21fff)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) + + +### Features + +* impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-orm-plugin + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Inject Model ([#43](https://github.com/eggjs/tegg/issues/43)) ([ced2ce2](https://github.com/eggjs/tegg/commit/ced2ce2134964dcb410410c0192a34f77507c42d)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) + + + + + ## [2.2.1](https://github.com/eggjs/tegg/compare/@eggjs/tegg-orm-plugin@2.2.0...@eggjs/tegg-orm-plugin@2.2.1) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-orm-plugin diff --git a/plugin/orm/app.ts b/plugin/orm/app.ts index c0adf767..ac1017fd 100644 --- a/plugin/orm/app.ts +++ b/plugin/orm/app.ts @@ -4,8 +4,9 @@ import { LeoricRegister } from './lib/LeoricRegister'; import { ModelProtoManager } from './lib/ModelProtoManager'; import { ModelProtoHook } from './lib/ModelProtoHook'; import { MODEL_PROTO_IMPL_TYPE } from '@eggjs/tegg-orm-decorator'; -import ContextModelProto from './lib/ContextModelProto'; -import { ContextModeObject } from './lib/ContextModeObject'; +import SingletonModelProto from './lib/SingletonModelProto'; +import { SingletonModelObject } from './lib/SingletonModelObject'; +import { ORMLoadUnitHook } from './lib/ORMLoadUnitHook'; export default class OrmAppBootHook { private readonly app: Application; @@ -13,6 +14,7 @@ export default class OrmAppBootHook { private readonly leoricRegister: LeoricRegister; private readonly modelProtoManager: ModelProtoManager; private readonly modelProtoHook: ModelProtoHook; + private readonly ormLoadUnitHook: ORMLoadUnitHook; constructor(app) { this.app = app; @@ -20,13 +22,15 @@ export default class OrmAppBootHook { this.modelProtoManager = new ModelProtoManager(); this.leoricRegister = new LeoricRegister(this.modelProtoManager, this.dataSourceManager); this.modelProtoHook = new ModelProtoHook(this.modelProtoManager); - this.app.eggPrototypeCreatorFactory.registerPrototypeCreator(MODEL_PROTO_IMPL_TYPE, ContextModelProto.createProto); + this.app.eggPrototypeCreatorFactory.registerPrototypeCreator(MODEL_PROTO_IMPL_TYPE, SingletonModelProto.createProto); this.app.leoricRegister = this.leoricRegister; + this.ormLoadUnitHook = new ORMLoadUnitHook(); } configWillLoad() { this.app.eggPrototypeLifecycleUtil.registerLifecycle(this.modelProtoHook); - this.app.eggObjectFactory.registerEggObjectCreateMethod(ContextModelProto, ContextModeObject.createObject); + this.app.eggObjectFactory.registerEggObjectCreateMethod(SingletonModelProto, SingletonModelObject.createObject); + this.app.loadUnitLifecycleUtil.registerLifecycle(this.ormLoadUnitHook); } configDidLoad() { diff --git a/plugin/orm/lib/LeoricRegister.ts b/plugin/orm/lib/LeoricRegister.ts index 680407a5..4b7d884f 100644 --- a/plugin/orm/lib/LeoricRegister.ts +++ b/plugin/orm/lib/LeoricRegister.ts @@ -17,22 +17,35 @@ export class LeoricRegister extends Base { this.realmMap = new Map(); } - getOrCreateRealm(datasource: string | undefined): any { + getConfig(datasource?: string) { let config: OrmConfig | undefined; if (!datasource) { config = this.dataSourceManager.getDefaultConfig(); } else { config = this.dataSourceManager.getConfig(datasource); } - if (!config) { - throw new Error(`not found datasource for ${datasource}`); + return config; + } + + getRealm(config: OrmConfig | undefined): Realm | undefined { + if (!config?.database) { + return undefined; } - let realm = this.realmMap.get(config.database); - if (realm) { - return realm; + const realm = this.realmMap.get(config.database); + return realm; + } + + getOrCreateRealm(datasource: string | undefined): any { + const config = this.getConfig(datasource); + let realm: Realm | undefined; + if (config) { + realm = this.getRealm(config); + if (realm) { + return realm; + } } realm = new (Realm as any)({ ...config }); - this.realmMap.set(config.database, realm); + this.realmMap.set(config!.database, realm); return realm; } diff --git a/plugin/orm/lib/ORMLoadUnitHook.ts b/plugin/orm/lib/ORMLoadUnitHook.ts new file mode 100644 index 00000000..ba6c0ed0 --- /dev/null +++ b/plugin/orm/lib/ORMLoadUnitHook.ts @@ -0,0 +1,26 @@ +import { LifecycleHook } from '@eggjs/tegg'; +import { + EggLoadUnitType, + EggPrototypeCreatorFactory, + EggPrototypeFactory, + LoadUnit, + LoadUnitLifecycleContext, +} from '@eggjs/tegg-metadata'; +import { Orm } from './SingletonORM'; + +const REGISTER_CLAZZ = [ + Orm, +]; + +export class ORMLoadUnitHook implements LifecycleHook { + async postCreate(_ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { + if (loadUnit.type === EggLoadUnitType.APP) { + for (const clazz of REGISTER_CLAZZ) { + const protos = await EggPrototypeCreatorFactory.createProto(clazz, loadUnit); + for (const proto of protos) { + EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); + } + } + } + } +} diff --git a/plugin/orm/lib/ContextModeObject.ts b/plugin/orm/lib/SingletonModelObject.ts similarity index 57% rename from plugin/orm/lib/ContextModeObject.ts rename to plugin/orm/lib/SingletonModelObject.ts index e2e1877a..d0f5f4ac 100644 --- a/plugin/orm/lib/ContextModeObject.ts +++ b/plugin/orm/lib/SingletonModelObject.ts @@ -1,34 +1,29 @@ -import assert from 'assert'; import { - EggContext, + ContextHandler, EggObject, - EggObjectLifeCycleContext, EggObjectStatus, } from '@eggjs/tegg-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { EggPrototypeName, EggObjectName } from '@eggjs/tegg'; import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { Bone } from 'leoric'; -import ContextModelProto from './ContextModelProto'; +import SingletonModelProto from './SingletonModelProto'; import { EGG_CONTEXT } from '@eggjs/egg-module-common'; -export class ContextModeObject implements EggObject { +export class SingletonModelObject implements EggObject { private status: EggObjectStatus = EggObjectStatus.PENDING; id: Id; readonly name: EggPrototypeName; private _obj: typeof Bone; - readonly proto: ContextModelProto; - readonly ctx: EggContext; + readonly proto: SingletonModelProto; - constructor(name: EggObjectName, proto: ContextModelProto, ctx: EggContext) { + constructor(name: EggObjectName, proto: SingletonModelProto) { this.name = name; this.proto = proto; - this.ctx = ctx; - this.id = IdenticalUtil.createObjectId(this.proto.id, this.ctx.id); + this.id = IdenticalUtil.createObjectId(this.proto.id); } async init() { - const ctx = this.ctx; const clazz = class ContextModelClass extends this.proto.model { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -37,13 +32,16 @@ export class ContextModeObject implements EggObject { } static get ctx() { - return ctx.get(EGG_CONTEXT); + const ctx = ContextHandler.getContext(); + if (ctx) { + return ctx.get(EGG_CONTEXT); + } } - // custom setter always execute before define [CTX] when new Instance(super(opts) calling), if custom setter requires ctx, it should not be undefined get ctx() { - return ctx.get(EGG_CONTEXT); + return ContextModelClass.ctx; } + }; this._obj = clazz; this.status = EggObjectStatus.READY; @@ -61,9 +59,8 @@ export class ContextModeObject implements EggObject { return this._obj; } - static async createObject(name: EggObjectName, proto: EggPrototype, _: EggObjectLifeCycleContext, ctx?: EggContext): Promise { - assert(ctx, 'ctx must be defined for ContextModelObject'); - const modelObject = new ContextModeObject(name, proto as ContextModelProto, ctx); + static async createObject(name: EggObjectName, proto: EggPrototype): Promise { + const modelObject = new SingletonModelObject(name, proto as SingletonModelProto); await modelObject.init(); return modelObject; } diff --git a/plugin/orm/lib/ContextModelProto.ts b/plugin/orm/lib/SingletonModelProto.ts similarity index 76% rename from plugin/orm/lib/ContextModelProto.ts rename to plugin/orm/lib/SingletonModelProto.ts index a989b0d1..65d853b9 100644 --- a/plugin/orm/lib/ContextModelProto.ts +++ b/plugin/orm/lib/SingletonModelProto.ts @@ -6,16 +6,16 @@ import { QualifierInfo, QualifierUtil, MetadataUtil, - MetaDataKey, + MetaDataKey, QualifierAttribute, QualifierValue, } from '@eggjs/tegg'; import { Id, IdenticalUtil } from '@eggjs/tegg-lifecycle'; import { Bone } from 'leoric'; -export default class ContextModelProto implements EggPrototype { +export default class SingletonModelProto implements EggPrototype { private readonly qualifiers: QualifierInfo[]; readonly accessLevel = AccessLevel.PUBLIC; id: Id; - readonly initType = ObjectInitType.CONTEXT; + readonly initType = ObjectInitType.SINGLETON; readonly injectObjects = []; readonly loadUnitId: string; readonly moduleName: string; @@ -53,7 +53,11 @@ export default class ContextModelProto implements EggPrototype { return true; } - static createProto(ctx: EggPrototypeLifecycleContext): ContextModelProto { - return new ContextModelProto(ctx.loadUnit, ctx.clazz as typeof Bone); + getQualifier(attribute: QualifierAttribute): QualifierValue | undefined { + return this.qualifiers.find(t => t.attribute === attribute)?.value; + } + + static createProto(ctx: EggPrototypeLifecycleContext): SingletonModelProto { + return new SingletonModelProto(ctx.loadUnit, ctx.clazz as typeof Bone); } } diff --git a/plugin/orm/lib/SingletonORM.ts b/plugin/orm/lib/SingletonORM.ts new file mode 100644 index 00000000..f4ac304d --- /dev/null +++ b/plugin/orm/lib/SingletonORM.ts @@ -0,0 +1,30 @@ +import { + AccessLevel, + Inject, + SingletonProto, +} from '@eggjs/tegg'; +import { LeoricRegister } from './LeoricRegister'; +import Realm from 'leoric'; + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class Orm { + @Inject() + private leoricRegister: LeoricRegister; + + // default dataSource + get client(): Realm { + const defaultConfig = this.leoricRegister.getConfig(); + return this.leoricRegister.getRealm(defaultConfig)!; + } + + getClient(datasource: string): Realm { + const config = this.leoricRegister.getConfig(datasource); + if (!config) { + throw new Error(`not found ${datasource} datasource`); + } + return this.leoricRegister.getOrCreateRealm(config.database)!; + } + +} diff --git a/plugin/orm/package.json b/plugin/orm/package.json index 6c8d2819..dcb15340 100644 --- a/plugin/orm/package.json +++ b/plugin/orm/package.json @@ -6,7 +6,7 @@ "tegg" ] }, - "version": "2.3.0", + "version": "3.23.0", "description": "orm decorator for egg", "keywords": [ "egg", @@ -28,11 +28,11 @@ ], "types": "typings/index.d.ts", "scripts": { + "test": "npm run prepare-test && cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", "prepublishOnly": "npm run tsc:pub", - "autod": "autod", "prepare-test": "node ./test/fixtures/prepare.js" }, "homepage": "https://github.com/eggjs/tegg", @@ -48,28 +48,35 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/egg-module-common": "^1.0.0", - "@eggjs/tegg": "^1.5.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-orm-decorator": "^1.5.0", - "@eggjs/tegg-runtime": "^1.4.0", + "@eggjs/egg-module-common": "^3.23.0", + "@eggjs/tegg": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-orm-decorator": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", "@types/koa-router": "^7.0.40", "koa-compose": "^3.2.1", - "leoric": "^2.6.1" + "leoric": "^2.6.1", + "sdk-base": "^4.2.0" }, "devDependencies": { - "@eggjs/module-test-util": "^1.4.0", + "@eggjs/module-test-util": "^3.23.0", "@eggjs/router": "^2.0.0", - "@eggjs/tegg-config": "^1.3.0", - "@eggjs/tegg-plugin": "^1.4.0", - "egg": "^2.26.0", - "egg-mock": "^3.25.1", - "egg-tracer": "^1.1.0", + "@eggjs/tegg-config": "^3.23.0", + "@eggjs/tegg-plugin": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "egg-mock": "^5.5.0", + "egg-tracer": "^2.0.0", "koa-router": "^8.0.8", - "mysql": "^2.18.1" + "mocha": "^10.2.0", + "mysql": "^2.18.1", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/plugin/orm/test/fixtures/apps/orm-app/app.ts b/plugin/orm/test/fixtures/apps/orm-app/app.ts index 81813226..abf4b5e7 100644 --- a/plugin/orm/test/fixtures/apps/orm-app/app.ts +++ b/plugin/orm/test/fixtures/apps/orm-app/app.ts @@ -1,4 +1,5 @@ import { Application } from 'egg'; +// @ts-expect-error: the library definition is wrong import { Logger } from 'leoric'; export default class OrmAppHook { diff --git a/plugin/orm/test/fixtures/apps/orm-app/config/config.default.js b/plugin/orm/test/fixtures/apps/orm-app/config/config.default.js index 71959acc..f5ada8e5 100644 --- a/plugin/orm/test/fixtures/apps/orm-app/config/config.default.js +++ b/plugin/orm/test/fixtures/apps/orm-app/config/config.default.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = function() { +module.exports = function () { const config = { keys: 'test key', security: { @@ -9,19 +9,53 @@ module.exports = function() { }, }, orm: { - client: 'mysql', - database: 'test', - host: '127.0.0.1', - port: 3306, - user: 'root', + datasources: [ + { + client: 'mysql', + database: 'test', + host: '127.0.0.1', + port: 3306, + user: 'root', - delegate: 'model', - baseDir: 'model', - migrations: 'database', + delegate: 'model', + baseDir: 'model', + migrations: 'database', - define: { - underscored: true, - }, + define: { + underscored: true, + }, + }, + { + client: 'mysql', + database: 'apple', + host: '127.0.0.1', + port: 3306, + user: 'root', + + delegate: 'model', + baseDir: 'model', + migrations: 'database', + + define: { + underscored: true, + }, + }, + { + client: 'mysql', + database: 'banana', + host: '127.0.0.1', + port: 3306, + user: 'root', + + delegate: 'model', + baseDir: 'model', + migrations: 'database', + + define: { + underscored: true, + }, + }, + ], }, }; return config; diff --git a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/AppService.ts b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/AppService.ts index 347036de..b820a9b8 100644 --- a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/AppService.ts +++ b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/AppService.ts @@ -1,11 +1,15 @@ -import { ContextProto, Inject } from '@eggjs/tegg'; +import { Inject, SingletonProto } from '@eggjs/tegg'; +import { Orm } from '@eggjs/tegg-orm-plugin/lib/SingletonORM'; import { App } from './model/App'; -@ContextProto() +@SingletonProto() export class AppService { @Inject() App: typeof App; + @Inject() + private readonly orm: Orm; + async createApp(data: { name: string; desc: string; @@ -18,4 +22,17 @@ export class AppService { const app = await this.App.findOne({ name }); return app as App; } + + async rawQuery(dataSource: string, sql: string, values?: any[]) { + return await this.orm.getClient(dataSource).query(sql, values); + } + + async getClient(name: string) { + return this.orm.getClient(name); + } + + async getDefaultClient() { + return this.orm.client; + } + } diff --git a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/CtxService.ts b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/CtxService.ts new file mode 100644 index 00000000..0f32d4d7 --- /dev/null +++ b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/CtxService.ts @@ -0,0 +1,21 @@ +import { ContextProto, Inject } from '@eggjs/tegg'; +import { Pkg } from './model/Pkg'; + +@ContextProto() +export class CtxService { + @Inject() + Pkg: typeof Pkg; + + async createCtxPkg(data: { + name: string; + desc: string; + }): Promise { + const bone = await this.Pkg.create(data as any); + return bone as Pkg; + } + + async findCtxPkg(name: string): Promise { + const app = await this.Pkg.findOne({ name }); + return app as Pkg; + } +} diff --git a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/PkgService.ts b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/PkgService.ts index b342f5d1..37c20007 100644 --- a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/PkgService.ts +++ b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/PkgService.ts @@ -1,7 +1,7 @@ -import { ContextProto, Inject } from '@eggjs/tegg'; +import { Inject, SingletonProto } from '@eggjs/tegg'; import { Pkg } from './model/Pkg'; -@ContextProto() +@SingletonProto() export class PkgService { @Inject() Pkg: typeof Pkg; diff --git a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/App.ts b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/App.ts index 7d109b8f..d7955f86 100644 --- a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/App.ts +++ b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/App.ts @@ -1,7 +1,9 @@ import { Attribute, Model } from '@eggjs/tegg-orm-decorator'; import { DataTypes, Bone } from 'leoric'; -@Model() +@Model({ + dataSource: 'test', +}) export class App extends Bone { @Attribute(DataTypes.STRING) name: string; diff --git a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/Pkg.ts b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/Pkg.ts index 3b2ab21e..2155f879 100644 --- a/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/Pkg.ts +++ b/plugin/orm/test/fixtures/apps/orm-app/modules/orm-module/model/Pkg.ts @@ -1,7 +1,9 @@ import { Attribute, Model } from '@eggjs/tegg-orm-decorator'; import { DataTypes, Bone } from 'leoric'; -@Model() +@Model({ + dataSource: 'test', +}) export class Pkg extends Bone { @Attribute(DataTypes.STRING) name: string; diff --git a/plugin/orm/test/fixtures/prepare.js b/plugin/orm/test/fixtures/prepare.js index 002b60ad..501c3085 100644 --- a/plugin/orm/test/fixtures/prepare.js +++ b/plugin/orm/test/fixtures/prepare.js @@ -33,6 +33,10 @@ async function query(sql) { async function init() { await query('DROP DATABASE IF EXISTS `test`;'); await query('CREATE DATABASE test;'); + await query('DROP DATABASE IF EXISTS `apple`;'); + await query('CREATE DATABASE apple;'); + await query('DROP DATABASE IF EXISTS `banana`;'); + await query('CREATE DATABASE banana;'); await query('use test;'); await query('CREATE TABLE `apps` (\n' + ' `id` bigint unsigned NOT NULL AUTO_INCREMENT,\n' + diff --git a/plugin/orm/test/index.test.ts b/plugin/orm/test/index.test.ts index 559f76f1..c029ec28 100644 --- a/plugin/orm/test/index.test.ts +++ b/plugin/orm/test/index.test.ts @@ -1,11 +1,15 @@ import assert from 'assert'; import path from 'path'; -import mm from 'egg-mock'; +// @ts-expect-error: the library definition is wrong +import { Logger } from 'leoric'; +import mm, { MockApplication } from 'egg-mock'; import { AppService } from './fixtures/apps/orm-app/modules/orm-module/AppService'; import os from 'os'; import { PkgService } from './fixtures/apps/orm-app/modules/orm-module/PkgService'; import { Pkg } from './fixtures/apps/orm-app/modules/orm-module/model/Pkg'; import { App } from './fixtures/apps/orm-app/modules/orm-module/model/App'; +import { CtxService } from './fixtures/apps/orm-app/modules/orm-module/CtxService'; +import { EggContext } from '@eggjs/tegg'; describe('test/orm.test.ts', () => { // TODO win32 ci not support mysql @@ -13,11 +17,10 @@ describe('test/orm.test.ts', () => { return; } - let app; - let ctx; + let app: MockApplication; + let appService: AppService; afterEach(async () => { - await app.destroyModuleContext(ctx); await Promise.all([ Pkg.truncate(), App.truncate(), @@ -42,8 +45,7 @@ describe('test/orm.test.ts', () => { }); it('bone should work', async () => { - ctx = await app.mockModuleContext(); - const appService = await ctx.getEggObject(AppService); + const appService = await app.getEggObject(AppService); const appModel = await appService.createApp({ name: 'egg', desc: 'the framework', @@ -59,8 +61,7 @@ describe('test/orm.test.ts', () => { }); it('hook should work', async () => { - ctx = await app.mockModuleContext(); - const pkgService = await ctx.getEggObject(PkgService); + const pkgService = await app.getEggObject(PkgService); const pkgModel = await pkgService.createPkg({ name: 'egg', desc: 'the framework', @@ -76,10 +77,120 @@ describe('test/orm.test.ts', () => { }); it('ctx should inject with Model', async () => { - ctx = await app.mockModuleContext(); - const appService = await ctx.getEggObject(AppService); + const appService = await app.getEggObject(AppService); app.mockLog(); await appService.findApp('egg'); - app.expectLog(/sql: SELECT \* FROM `apps` WHERE `name` = 'egg' LIMIT 1 path: \//); + app.expectLog(/sql: SELECT \* FROM `apps` WHERE `name` = 'egg' LIMIT 1/); + // Model.ctx should be undefined in Singleton Service + app.expectLog(/path: undefined/); + }); + + it('singleton ORM client', async () => { + appService = await app.getEggObject(AppService); + + describe('raw query', () => { + before(async () => { + const appModel = await appService.createApp({ + name: 'egg', + desc: 'the framework', + }); + assert(appModel); + assert(appModel.name === 'egg'); + assert(appModel.desc === 'the framework'); + }); + + it('query success', async () => { + const res = await appService.rawQuery('test', 'select * from apps where name = "egg"'); + assert(res.rows.length === 1); + assert(res.rows[0].name === 'egg'); + }); + + it('query success for args', async () => { + const res = await appService.rawQuery('test', 'select * from apps where name = ?', [ 'egg' ]); + assert(res.rows.length === 1); + assert(res.rows[0].name === 'egg'); + }); + }); + + describe('multi db', () => { + + it('should work for multi database', async () => { + const appleClient = await appService.getClient('apple'); + const bananaClient = await appService.getClient('banana'); + assert(appleClient.options.database === 'apple'); + assert(appleClient.options.database === 'apple'); + assert(bananaClient.options.database === 'banana'); + assert(bananaClient.options.database === 'banana'); + }); + + it('should throw when invalid database', async () => { + await assert.rejects(async () => { + await appService.getClient('orange'); + }, /not found orange datasource/); + }); + + it('should return undefined when get default client', async () => { + const defaultClient = await appService.getDefaultClient(); + assert(defaultClient === undefined); + }); + }); + + }); + + describe('context proto', () => { + let ctx: EggContext; + let ctxService: CtxService; + beforeEach(async () => { + ctx = await app.mockModuleContext(); + ctxService = await ctx.getEggObject(CtxService); + }); + afterEach(async () => { + await app.destroyModuleContext(ctx); + }); + + it('should work for ContextProto service', async () => { + const ctxPkg = await ctxService.createCtxPkg({ + name: 'egg', + desc: 'the framework', + }); + assert(ctxPkg.name === 'egg_before_create_hook'); + }); + + it('should query work', async () => { + app.mockLog(); + await ctxService.createCtxPkg({ + name: 'egg', + desc: 'the framework', + }); + const ctxPkg = await ctxService.findCtxPkg('egg_before_create_hook'); + assert(ctxPkg); + assert(ctxPkg.name === 'egg_before_create_hook'); + app.expectLog(/sql: SELECT \* FROM `pkgs` WHERE `name` = 'egg_before_create_hook' LIMIT 1 path: \//); + + }); + + it('should tracer ctx set', async () => { + let ctx; + await (app as any).leoricRegister.ready(); + for (const realm of app.leoricRegister.realmMap.values()) { + realm.driver.logger = new Logger({ + // eslint-disable-next-line no-loop-func + logQuery(_: any, __: any, options: { Model: { ctx: any; }; }) { + if (options.Model) { + ctx = options.Model.ctx; + } + }, + }); + } + await ctxService.createCtxPkg({ + name: 'egg', + desc: 'the framework', + }); + + assert(ctx.originalUrl === '/'); + assert(ctx.tracer.traceId); + + }); + }); }); diff --git a/plugin/orm/tsconfig.json b/plugin/orm/tsconfig.json index 74935f4b..8205bd19 100644 --- a/plugin/orm/tsconfig.json +++ b/plugin/orm/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": "./" }, "exclude": [ - "node_modules" + "node_modules", + "test" ] } diff --git a/plugin/orm/typings/index.d.ts b/plugin/orm/typings/index.d.ts index 001a6f91..270903b6 100644 --- a/plugin/orm/typings/index.d.ts +++ b/plugin/orm/typings/index.d.ts @@ -4,6 +4,7 @@ import '@eggjs/tegg-plugin'; import { DataType } from 'leoric'; import { AttributeOptions } from '@eggjs/tegg-orm-decorator'; import { LeoricRegister } from '../lib/LeoricRegister'; +import { Orm } from '../lib/SingletonORM'; declare module '@eggjs/tegg-orm-decorator' { export declare function Attribute(dataType: DataType, options?: AttributeOptions): (target: any, propertyKey: PropertyKey) => void; @@ -12,6 +13,7 @@ declare module '@eggjs/tegg-orm-decorator' { declare module 'egg' { export interface TeggOrmApplication { leoricRegister: LeoricRegister; + orm: Orm; } interface Application extends TeggOrmApplication { diff --git a/plugin/schedule/CHANGELOG.md b/plugin/schedule/CHANGELOG.md index b5123bb5..41c6735e 100644 --- a/plugin/schedule/CHANGELOG.md +++ b/plugin/schedule/CHANGELOG.md @@ -3,6 +3,354 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) + + +### Features + +* impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) +* fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-schedule-plugin + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* fix miss agent file ([#56](https://github.com/eggjs/tegg/issues/56)) ([cfb4dcc](https://github.com/eggjs/tegg/commit/cfb4dcc006ee1253733c7122f885a05da94f80b5)) +* fix schedule import ([1fb5481](https://github.com/eggjs/tegg/commit/1fb54816fb3240c641824c2bc2b464c35652b655)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* impl Schedule decorator ([#52](https://github.com/eggjs/tegg/issues/52)) ([7f95005](https://github.com/eggjs/tegg/commit/7f950050b548ca542addbd7b466675da4e81ce3f)) +* implement cork/uncork for eventbus ([#60](https://github.com/eggjs/tegg/issues/60)) ([38114bd](https://github.com/eggjs/tegg/commit/38114bd7ea3b46cc4a79556a005ef18b2ae11ec2)) + + + + + ## [2.2.3](https://github.com/eggjs/tegg/compare/@eggjs/tegg-schedule-plugin@2.2.2...@eggjs/tegg-schedule-plugin@2.2.3) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-schedule-plugin diff --git a/plugin/schedule/lib/EggScheduleAdapter.ts b/plugin/schedule/lib/EggScheduleAdapter.ts index 2257a9cc..b2262ba9 100644 --- a/plugin/schedule/lib/EggScheduleAdapter.ts +++ b/plugin/schedule/lib/EggScheduleAdapter.ts @@ -11,7 +11,7 @@ export function eggScheduleAdapterFactory(proto: EggPrototype, metaData: Schedul (ctx as any)[ROOT_PROTO] = proto; await ctx.beginModuleScope(async () => { if (metaData.disable) return; - const eggObject = await EggContainerFactory.getOrCreateEggObject(proto, proto.name, ctx.teggContext); + const eggObject = await EggContainerFactory.getOrCreateEggObject(proto, proto.name); const subscriber = eggObject.obj as ScheduleSubscriber; await subscriber.subscribe(data); }); diff --git a/plugin/schedule/lib/ScheduleSubscriberRegister.ts b/plugin/schedule/lib/ScheduleSubscriberRegister.ts index 2c9c1d5f..541a1aa7 100644 --- a/plugin/schedule/lib/ScheduleSubscriberRegister.ts +++ b/plugin/schedule/lib/ScheduleSubscriberRegister.ts @@ -1,6 +1,5 @@ import { Application, EggLogger } from 'egg'; -import { PrototypeUtil } from '@eggjs/tegg'; -import { EggProtoImplClass } from '@eggjs/tegg'; +import { PrototypeUtil, EggProtoImplClass } from '@eggjs/tegg'; import { ScheduleMetadata } from '@eggjs/tegg-schedule-decorator'; import { EggScheduleMetadataConvertor } from './EggScheduleMetadataConvertor'; diff --git a/plugin/schedule/package.json b/plugin/schedule/package.json index 365e3ae4..a32324b0 100644 --- a/plugin/schedule/package.json +++ b/plugin/schedule/package.json @@ -7,7 +7,7 @@ "schedule" ] }, - "version": "2.3.0", + "version": "3.23.0", "description": "schedule decorator for egg", "keywords": [ "egg", @@ -30,11 +30,11 @@ ], "types": "typings/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -49,22 +49,28 @@ "node": ">=14.0.0" }, "dependencies": { - "@eggjs/egg-module-common": "^1.0.0", - "@eggjs/tegg": "^1.5.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0", - "@eggjs/tegg-schedule-decorator": "^1.6.0" + "@eggjs/egg-module-common": "^3.23.0", + "@eggjs/tegg": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", + "@eggjs/tegg-schedule-decorator": "^3.23.0" }, "devDependencies": { - "@eggjs/module-test-util": "^1.4.0", - "@eggjs/tegg-config": "^1.3.0", - "@eggjs/tegg-plugin": "^1.4.0", - "egg": "^2.26.0", - "egg-mock": "^3.25.1", - "mz-modules": "^2.1.0" + "@eggjs/module-test-util": "^3.23.0", + "@eggjs/tegg-config": "^3.23.0", + "@eggjs/tegg-plugin": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "egg-mock": "^5.5.0", + "egg-schedule": "^4.0.0", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/plugin/schedule/test/schedule.test.ts b/plugin/schedule/test/schedule.test.ts index 5f03577f..4cbe7577 100644 --- a/plugin/schedule/test/schedule.test.ts +++ b/plugin/schedule/test/schedule.test.ts @@ -1,8 +1,8 @@ import path from 'path'; -import mm from 'egg-mock'; import fs from 'fs/promises'; import assert from 'assert'; -import sleep from 'mz-modules/sleep'; +import mm from 'egg-mock'; +import { TimerUtil } from '@eggjs/tegg-common-util'; describe('test/schedule.test.ts', () => { let app; @@ -32,8 +32,8 @@ describe('test/schedule.test.ts', () => { return app.close(); }); - it('msg should work', async () => { - await sleep(1000); + it('schedule should work', async () => { + await TimerUtil.sleep(1000); const scheduleLog = await getScheduleLogContent('schedule-app'); assert(/schedule called/.test(scheduleLog)); }); diff --git a/plugin/schedule/tsconfig.json b/plugin/schedule/tsconfig.json index 74935f4b..8205bd19 100644 --- a/plugin/schedule/tsconfig.json +++ b/plugin/schedule/tsconfig.json @@ -4,6 +4,7 @@ "baseUrl": "./" }, "exclude": [ - "node_modules" + "node_modules", + "test" ] } diff --git a/plugin/tegg/.autod.conf.js b/plugin/tegg/.autod.conf.js deleted file mode 100644 index f4c9046f..00000000 --- a/plugin/tegg/.autod.conf.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -module.exports = { - write: true, - prefix: '^', - dep: [], - devdep: [], - semver: [], - exclude: [], -}; diff --git a/plugin/tegg/CHANGELOG.md b/plugin/tegg/CHANGELOG.md index df294cc9..38576afc 100644 --- a/plugin/tegg/CHANGELOG.md +++ b/plugin/tegg/CHANGELOG.md @@ -3,6 +3,407 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + + +### Features + +* add LoadUnitMultiInstanceProtoHook for tegg plugin ([#150](https://github.com/eggjs/tegg/issues/150)) ([b938580](https://github.com/eggjs/tegg/commit/b9385803383dceedfc26bd990e5d752cd33f0f67)) + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + + +### Bug Fixes + +* fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + + +### Features + +* implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + + +### Bug Fixes + +* ensure ContextInitiator be called after ctx ready ([#138](https://github.com/eggjs/tegg/issues/138)) ([79e16da](https://github.com/eggjs/tegg/commit/79e16dae913b6114ac8d13bde8de60164d57dab3)) + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + + +### Bug Fixes + +* after call mockModuleContext, hasMockModuleContext should be true ([#134](https://github.com/eggjs/tegg/issues/134)) ([88b3caa](https://github.com/eggjs/tegg/commit/88b3caadd24f08221b8098c42733e26376338cae)) + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +## [3.6.2](https://github.com/eggjs/tegg/compare/v3.6.1...v3.6.2) (2023-02-16) + + +### Bug Fixes + +* should not cache ctx object ([#103](https://github.com/eggjs/tegg/issues/103)) ([be54083](https://github.com/eggjs/tegg/commit/be5408375261d98b60fbc97e18de9232581a9547)) + + + + + +## [3.6.1](https://github.com/eggjs/tegg/compare/v3.6.0...v3.6.1) (2023-02-14) + + +### Bug Fixes + +* get app/ctx properties when load unit beforeCreate ([#102](https://github.com/eggjs/tegg/issues/102)) ([76ef679](https://github.com/eggjs/tegg/commit/76ef679d745deb235db9dcc3fa34984b511bd5c6)) + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + + +### Bug Fixes + +* egg qualifier should register after all file loaded ([#100](https://github.com/eggjs/tegg/issues/100)) ([5033b51](https://github.com/eggjs/tegg/commit/5033b51796b8a3329bd79884a8d8f18226193a1b)) + + +### Features + +* add backgroundTask.timeout config ([#101](https://github.com/eggjs/tegg/issues/101)) ([0b1eee0](https://github.com/eggjs/tegg/commit/0b1eee00d6feb9c6d4509023dffe85c0ada749c2)) + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + + +### Bug Fixes + +* not create ctx logger proto ([#97](https://github.com/eggjs/tegg/issues/97)) ([100886b](https://github.com/eggjs/tegg/commit/100886ba90bdc7cccd07fa2f390defb5b0c53e22)) + + + + + +## [3.5.1](https://github.com/eggjs/tegg/compare/v3.5.0...v3.5.1) (2023-02-10) + + +### Bug Fixes + +* remove useless init singleton proto ([#96](https://github.com/eggjs/tegg/issues/96)) ([097ac58](https://github.com/eggjs/tegg/commit/097ac58c675d43088c8785a12cf224b5d6adea17)) + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + + +### Features + +* append call stack for runInBackground ([#91](https://github.com/eggjs/tegg/issues/91)) ([ec7bc2c](https://github.com/eggjs/tegg/commit/ec7bc2c60ffb49b4a51feec82e391b1f6a88549a)) +* remove context egg object factory ([#93](https://github.com/eggjs/tegg/issues/93)) ([e14bdb2](https://github.com/eggjs/tegg/commit/e14bdb257eaebc0b0a4c37c6073a5c3237718718)) +* use SingletonProto for egg ctx object ([#92](https://github.com/eggjs/tegg/issues/92)) ([3385d57](https://github.com/eggjs/tegg/commit/3385d571b076d3148978f252188f29d9cf2c6781)) + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + + +### Bug Fixes + +* BackgroundTaskHelper should support recursively call ([#90](https://github.com/eggjs/tegg/issues/90)) ([368ac03](https://github.com/eggjs/tegg/commit/368ac0343d0d4e96b3768e7fd169b721551d0e4b)) + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +## [3.3.4](https://github.com/eggjs/tegg/compare/v3.3.3...v3.3.4) (2023-01-29) + + +### Bug Fixes + +* should not notify backgroundTaskHelper if teggContext not exists ([#88](https://github.com/eggjs/tegg/issues/88)) ([4cab68b](https://github.com/eggjs/tegg/commit/4cab68bfc08a3786bde9a67cd8687f152829d9a0)) + + + + + +## [3.3.3](https://github.com/eggjs/tegg/compare/v3.3.2...v3.3.3) (2023-01-29) + + +### Bug Fixes + +* wait egg background task done before destroy tegg ctx ([#87](https://github.com/eggjs/tegg/issues/87)) ([deea4d8](https://github.com/eggjs/tegg/commit/deea4d8d75c43347c6ee09e0e97f5fa80dd68dd9)) + + + + + +## [3.3.2](https://github.com/eggjs/tegg/compare/v3.3.1...v3.3.2) (2023-01-29) + + +### Bug Fixes + +* beginModuleScope should be reentrant ([#86](https://github.com/eggjs/tegg/issues/86)) ([648aeaf](https://github.com/eggjs/tegg/commit/648aeaf1f20ff5bc217bf6f16fac9d9181eb8447)) + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + + +### Features + +* add app.eggContextHandler ([#84](https://github.com/eggjs/tegg/issues/84)) ([2772624](https://github.com/eggjs/tegg/commit/277262418143956b2e75bd1db5f2e7dd9b75eb8b)) + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.2.0](https://github.com/eggjs/tegg/compare/v3.1.0...v3.2.0) (2022-12-28) + + +### Features + +* impl mockModuleContextScope ([#73](https://github.com/eggjs/tegg/issues/73)) ([041881c](https://github.com/eggjs/tegg/commit/041881ca317ad81366172a35ac56b7b2dc0a0488)) + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Bug Fixes + +* inject context proto to singleton proto ([#72](https://github.com/eggjs/tegg/issues/72)) ([fcc0b2b](https://github.com/eggjs/tegg/commit/fcc0b2b48fc9bce580c1f2bcfcc38039ae909951)) +* optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-plugin + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Bug Fixes + +* optimize backgroud output ([#47](https://github.com/eggjs/tegg/issues/47)) ([6d978c5](https://github.com/eggjs/tegg/commit/6d978c5d7c339c78a90b00d2c2622f0be85ab3ce)) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) + + + + + ## [1.3.8](https://github.com/eggjs/tegg/compare/@eggjs/tegg-plugin@1.3.7...@eggjs/tegg-plugin@1.3.8) (2022-09-05) **Note:** Version bump only for package @eggjs/tegg-plugin diff --git a/plugin/tegg/app.ts b/plugin/tegg/app.ts index c635c048..2d849684 100644 --- a/plugin/tegg/app.ts +++ b/plugin/tegg/app.ts @@ -5,10 +5,17 @@ import { Application } from 'egg'; import { EggContextCompatibleHook } from './lib/EggContextCompatibleHook'; import { CompatibleUtil } from './lib/CompatibleUtil'; import { ModuleHandler } from './lib/ModuleHandler'; +import { EggContextHandler } from './lib/EggContextHandler'; +import { hijackRunInBackground } from './lib/run_in_background'; +import { EggQualifierProtoHook } from './lib/EggQualifierProtoHook'; +import { LoadUnitMultiInstanceProtoHook } from '@eggjs/tegg-metadata'; export default class App { private readonly app: Application; private compatibleHook?: EggContextCompatibleHook; + private eggContextHandler: EggContextHandler; + private eggQualifierProtoHook: EggQualifierProtoHook; + private loadUnitMultiInstanceProtoHook: LoadUnitMultiInstanceProtoHook; constructor(app: Application) { this.app = app; @@ -19,11 +26,22 @@ export default class App { } configDidLoad() { + this.eggContextHandler = new EggContextHandler(this.app); + this.app.eggContextHandler = this.eggContextHandler; + this.eggContextHandler.register(); this.app.moduleHandler = new ModuleHandler(this.app); } async didLoad() { - await this.app.moduleHandler.ready(); + hijackRunInBackground(this.app); + this.loadUnitMultiInstanceProtoHook = new LoadUnitMultiInstanceProtoHook(); + this.app.loadUnitLifecycleUtil.registerLifecycle(this.loadUnitMultiInstanceProtoHook); + + // wait all file loaded, so app/ctx has all properties + this.eggQualifierProtoHook = new EggQualifierProtoHook(this.app); + this.app.loadUnitLifecycleUtil.registerLifecycle(this.eggQualifierProtoHook); + // start load tegg objects + await this.app.moduleHandler.init(); this.compatibleHook = new EggContextCompatibleHook(this.app.moduleHandler); this.app.eggContextLifecycleUtil.registerLifecycle(this.compatibleHook); } @@ -34,5 +52,8 @@ export default class App { if (this.compatibleHook) { this.app.eggContextLifecycleUtil.deleteLifecycle(this.compatibleHook); } + if (this.eggQualifierProtoHook) { + this.app.loadUnitLifecycleUtil.deleteLifecycle(this.eggQualifierProtoHook); + } } } diff --git a/plugin/tegg/app/extend/application.ts b/plugin/tegg/app/extend/application.ts index 90d10c41..9e4c7667 100644 --- a/plugin/tegg/app/extend/application.ts +++ b/plugin/tegg/app/extend/application.ts @@ -16,7 +16,8 @@ import { LoadUnitInstanceLifecycleUtil, } from '@eggjs/tegg-runtime'; import { LoaderFactory } from '@eggjs/tegg-loader'; -import { EggProtoImplClass, PrototypeUtil, IdenticalUtil } from '@eggjs/tegg'; +import { EggProtoImplClass, PrototypeUtil, IdenticalUtil, RuntimeConfig } from '@eggjs/tegg'; +import type { Application } from 'egg'; export default { // @eggjs/tegg-metadata should not depend by other egg plugins. @@ -78,6 +79,16 @@ export default { return IdenticalUtil; }, + get runtimeConfig(): RuntimeConfig { + const app = this as unknown as Application; + const config = app.config; + return { + baseDir: config.baseDir, + env: config.env, + name: config.name, + }; + }, + async getEggObject(clazz: EggProtoImplClass) { const proto = PrototypeUtil.getClazzProto(clazz); if (!proto) { diff --git a/plugin/tegg/app/extend/application.unittest.ts b/plugin/tegg/app/extend/application.unittest.ts index ac551296..c9d49954 100644 --- a/plugin/tegg/app/extend/application.unittest.ts +++ b/plugin/tegg/app/extend/application.unittest.ts @@ -1,29 +1,33 @@ import { MockApplication } from 'egg-mock'; import { Context } from 'egg'; -import { TEggPluginContext } from './context'; import { EggContextImpl } from '../../lib/EggContextImpl'; import { EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; -import { TEGG_CONTEXT, EGG_CONTEXT } from '@eggjs/egg-module-common'; const TEGG_LIFECYCLE_CACHE: Map = new Map(); +let hasMockModuleContext = false; + export default { async mockModuleContext(this: MockApplication, data?: any): Promise { - const ctx = this.mockContext(data) as TEggPluginContext; - const teggCtx = ctx[TEGG_CONTEXT] = new EggContextImpl(ctx); - ctx[TEGG_CONTEXT] = teggCtx; - teggCtx.set(EGG_CONTEXT, ctx); + this.deprecate('app.mockModuleContext is deprecated, use mockModuleContextScope.'); + if (hasMockModuleContext) { + throw new Error('should not call mockModuleContext twice.'); + } + const ctx = this.mockContext(data); + const teggCtx = new EggContextImpl(ctx); const lifecycle = {}; TEGG_LIFECYCLE_CACHE.set(teggCtx, lifecycle); if (teggCtx.init) { await teggCtx.init(lifecycle); } + hasMockModuleContext = true; return ctx; }, async destroyModuleContext(ctx: Context) { - const teggPluginCtx = ctx as TEggPluginContext; - const teggCtx = teggPluginCtx[TEGG_CONTEXT]; + hasMockModuleContext = false; + + const teggCtx = ctx.teggContext; if (!teggCtx) { return; } @@ -32,4 +36,22 @@ export default { await teggCtx.destroy(lifecycle); } }, + + async mockModuleContextScope(this: MockApplication, fn: (ctx: Context) => Promise, data?: any): Promise { + if (hasMockModuleContext) { + throw new Error('mockModuleContextScope can not use with mockModuleContext, should use mockModuleContextScope only.'); + } + return this.mockContextScope(async ctx => { + const teggCtx = new EggContextImpl(ctx); + const lifecycle = {}; + if (teggCtx.init) { + await teggCtx.init(lifecycle); + } + try { + return await fn(ctx); + } finally { + await teggCtx.destroy(lifecycle); + } + }, data); + }, }; diff --git a/plugin/tegg/app/extend/context.ts b/plugin/tegg/app/extend/context.ts index ce9e1480..633e8472 100644 --- a/plugin/tegg/app/extend/context.ts +++ b/plugin/tegg/app/extend/context.ts @@ -27,7 +27,7 @@ export default { throw new Error(`can not get proto for clazz ${clazz.name}`); } const proto = protoObj as EggPrototype; - const eggObject = await this.app.eggContainerFactory.getOrCreateEggObject(proto, proto.name, this.teggContext); + const eggObject = await this.app.eggContainerFactory.getOrCreateEggObject(proto, proto.name); return eggObject.obj; }, }; diff --git a/plugin/tegg/lib/AppGraph.ts b/plugin/tegg/lib/AppGraph.ts index 4cf327d7..8a344f79 100644 --- a/plugin/tegg/lib/AppGraph.ts +++ b/plugin/tegg/lib/AppGraph.ts @@ -49,7 +49,9 @@ export class ModuleNode implements GraphNodeObj { } getPublicClazzList(): readonly EggProtoImplClass[] { - return this.clazzList.filter(clazz => PrototypeUtil.getProperty(clazz)?.accessLevel === AccessLevel.PUBLIC); + return this.clazzList.filter(clazz => PrototypeUtil.getAccessLevel(clazz, { + unitPath: this.moduleConfig.path, + }) === AccessLevel.PUBLIC); } toString() { @@ -78,7 +80,9 @@ export class ModuleNode implements GraphNodeObj { } const clazzList = innerFind ? this.clazzList : this.getPublicClazzList(); const implList = clazzList - .filter(clazz => PrototypeUtil.getProperty(clazz)?.name === objName) + .filter(clazz => PrototypeUtil.getObjNames(clazz, { + unitPath: this.moduleConfig.path, + }).includes(objName)) .filter(clazz => this.verifyQualifiers(clazz, qualifiers)); if (implList.length === 1) { return implList; diff --git a/plugin/tegg/lib/AppLoadUnit.ts b/plugin/tegg/lib/AppLoadUnit.ts index 60d8d257..f724b5be 100644 --- a/plugin/tegg/lib/AppLoadUnit.ts +++ b/plugin/tegg/lib/AppLoadUnit.ts @@ -38,10 +38,11 @@ export class AppLoadUnit implements LoadUnit { const clazzList = this.loader.load(); for (const clazz of clazzList) { // TODO duplicate code, same in ModuleLoadUnit - const property = PrototypeUtil.getProperty(clazz)!; const defaultQualifier = [{ attribute: InitTypeQualifierAttribute, - value: property.initType, + value: PrototypeUtil.getInitType(clazz, { + unitPath: this.unitPath, + })!, }, { attribute: LoadUnitNameQualifierAttribute, value: this.name, @@ -49,8 +50,10 @@ export class AppLoadUnit implements LoadUnit { defaultQualifier.forEach(qualifier => { QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value); }); - const proto = await EggPrototypeCreatorFactory.createProto(clazz, this); - EggPrototypeFactory.instance.registerPrototype(proto, this); + const protos = await EggPrototypeCreatorFactory.createProto(clazz, this); + for (const proto of protos) { + EggPrototypeFactory.instance.registerPrototype(proto, this); + } } } diff --git a/plugin/tegg/lib/AppLoadUnitInstance.ts b/plugin/tegg/lib/AppLoadUnitInstance.ts index a23baad5..ad2923ea 100644 --- a/plugin/tegg/lib/AppLoadUnitInstance.ts +++ b/plugin/tegg/lib/AppLoadUnitInstance.ts @@ -47,8 +47,7 @@ export class AppLoadUnitInstance implements LoadUnitInstance { } } - async destroy(ctx: LoadUnitInstanceLifecycleContext): Promise { - await LoadUnitInstanceLifecycleUtil.objectPreDestroy(ctx, this); + async destroy(): Promise { const objs: EggObject[] = []; for (const protoObjMap of this.eggObjectMap.values()) { for (const protoObj of protoObjMap.values()) { diff --git a/plugin/tegg/lib/CompatibleUtil.ts b/plugin/tegg/lib/CompatibleUtil.ts index 18ea01bb..4088839f 100644 --- a/plugin/tegg/lib/CompatibleUtil.ts +++ b/plugin/tegg/lib/CompatibleUtil.ts @@ -30,35 +30,39 @@ export class CompatibleUtil { return this.requestProtoCache.get(name)!; } - private static singletonModuleProxyFactory(app: Application, loadUnitInstance: LoadUnitInstance, deprecate: boolean) { + private static singletonModuleProxyFactory(app: Application, loadUnitInstance: LoadUnitInstance) { + let deprecated = false; return function(_, p: PropertyKey) { const proto = CompatibleUtil.getSingletonProto(p); const eggObj = EggContainerFactory.getEggObject(proto); - if (deprecate) { + if (!deprecated) { + deprecated = true; app.deprecate( - `[egg/module] Please use app.module.${loadUnitInstance.name}.${String(p)} instead of app.${loadUnitInstance.name}.${String(p)}`); + `[egg/module] Please use await app.getEggObject(clazzName) instead of app.${loadUnitInstance.name}.${String(p)}`); } return eggObj.obj; }; } static appCompatible(app: Application, loadUnitInstance: LoadUnitInstance) { - const moduleLoadUnitProxy = ProxyUtil.safeProxy(loadUnitInstance, CompatibleUtil.singletonModuleProxyFactory(app, loadUnitInstance, false)); + const moduleLoadUnitProxy = ProxyUtil.safeProxy(loadUnitInstance, CompatibleUtil.singletonModuleProxyFactory(app, loadUnitInstance)); Reflect.defineProperty(app.module, loadUnitInstance.name, { configurable: true, value: moduleLoadUnitProxy, }); } - static contextModuleProxyFactory(holder: object, ctx: Context, loadUnitInstance: LoadUnitInstance, deprecate: boolean) { + static contextModuleProxyFactory(holder: object, ctx: Context, loadUnitInstance: LoadUnitInstance) { const cacheKey = `_${loadUnitInstance.name}Proxy`; if (!holder[cacheKey]) { + let deprecated = false; const getter = function(_, p: PropertyKey) { const proto = CompatibleUtil.getRequestProto(p); - const eggObj = EggContainerFactory.getEggObject(proto, p, ctx.teggContext); - if (deprecate) { + const eggObj = EggContainerFactory.getEggObject(proto, p); + if (!deprecated) { + deprecated = true; ctx.app.deprecate( - `[egg/module] Please use ctx.module.${loadUnitInstance.name}.${String(p)} instead of ctx.${loadUnitInstance.name}.${String(p)}`); + `[egg/module] Please use await ctx.getEggObject(clazzName) instead of ctx.${loadUnitInstance.name}.${String(p)}`); } return eggObj.obj; }; @@ -86,7 +90,7 @@ export class CompatibleUtil { if (!loadUnitInstance) { return; } - return CompatibleUtil.contextModuleProxyFactory(ctxModule, ctx, loadUnitInstance, false); + return CompatibleUtil.contextModuleProxyFactory(ctxModule, ctx, loadUnitInstance); }); } return this._moduleProxy; diff --git a/plugin/tegg/lib/EggAppLoader.ts b/plugin/tegg/lib/EggAppLoader.ts index 1de19ddf..9b3c5171 100644 --- a/plugin/tegg/lib/EggAppLoader.ts +++ b/plugin/tegg/lib/EggAppLoader.ts @@ -2,7 +2,11 @@ import { Application } from 'egg'; import { Loader, TeggError } from '@eggjs/tegg-metadata'; import { AccessLevel, - EggProtoImplClass, InitTypeQualifierAttribute, LoadUnitNameQualifierAttribute, + EggProtoImplClass, + EggQualifierAttribute, + EggType, + InitTypeQualifierAttribute, + LoadUnitNameQualifierAttribute, ObjectInitType, PrototypeUtil, QualifierUtil, @@ -10,11 +14,16 @@ import { import { ObjectUtils } from '@eggjs/tegg-common-util'; import { COMPATIBLE_PROTO_IMPLE_TYPE } from './EggCompatibleProtoImpl'; import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; -import { EggContextObjectFactory, EggSingletonObjectFactory } from '@eggjs/tegg-dynamic-inject-runtime'; +import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject-runtime'; -const APP_CLAZZ_BLACK_LIST = [ 'eggObjectFactory' ]; -const DEFAULT_APP_CLAZZ = []; -const DEFAULT_CONTEXT_CLAZZ = [ +export const APP_CLAZZ_BLACK_LIST = [ 'eggObjectFactory' ]; + +export const CONTEXT_CLAZZ_BLACK_LIST = [ + // just use the app.logger, ctx logger is deprecated. + 'logger', +]; +export const DEFAULT_APP_CLAZZ: string[] = []; +export const DEFAULT_CONTEXT_CLAZZ = [ 'user', ]; @@ -25,46 +34,41 @@ export class EggAppLoader implements Loader { this.app = app; } - private buildAppClazz(name: string): EggProtoImplClass { + private buildClazz(name: string, eggType: EggType): EggProtoImplClass { const app = this.app; - const func: EggProtoImplClass = function() { - return app[name]; - } as any; - PrototypeUtil.setIsEggPrototype(func); - PrototypeUtil.setFilePath(func, 'mock_file_path'); - PrototypeUtil.setProperty(func, { - name, - initType: ObjectInitType.SINGLETON, - accessLevel: AccessLevel.PUBLIC, - protoImplType: COMPATIBLE_PROTO_IMPLE_TYPE, - }); - QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); - QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.SINGLETON); - return func; - } - - private buildCtxClazz(name: string): EggProtoImplClass { - const temp = { - [name]: function(ctx) { + let func: EggProtoImplClass; + if (eggType === EggType.APP) { + func = function() { + return app[name]; + } as any; + } else { + func = function() { + const ctx = app.currentContext; if (!ctx) { // ctx has been destroyed, throw humanize error info throw TeggError.create(`Can not read property \`${name}\` because egg ctx has been destroyed`, 'read_after_ctx_destroyed'); } return ctx[name]; - } as any, - }; - const func = temp[name]; + } as any; + } + Object.defineProperty(func, 'name', { + value: name, + writable: false, + enumerable: false, + configurable: true, + }); PrototypeUtil.setIsEggPrototype(func); PrototypeUtil.setFilePath(func, 'mock_file_path'); PrototypeUtil.setProperty(func, { name, - initType: ObjectInitType.CONTEXT, + initType: ObjectInitType.SINGLETON, accessLevel: AccessLevel.PUBLIC, protoImplType: COMPATIBLE_PROTO_IMPLE_TYPE, }); QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); - QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.CONTEXT); + QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.SINGLETON); + QualifierUtil.addProtoQualifier(func, EggQualifierAttribute, eggType); return func; } @@ -73,6 +77,12 @@ export class EggAppLoader implements Loader { const func: EggProtoImplClass = function() { return app.getLogger(name); } as any; + Object.defineProperty(func, 'name', { + value: name, + writable: false, + enumerable: false, + configurable: true, + }); PrototypeUtil.setIsEggPrototype(func); PrototypeUtil.setFilePath(func, 'mock_file_path'); PrototypeUtil.setProperty(func, { @@ -83,33 +93,14 @@ export class EggAppLoader implements Loader { }); QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.SINGLETON); + QualifierUtil.addProtoQualifier(func, EggQualifierAttribute, EggType.APP); return func; } - private buildCtxLoggerClazz(name: string): EggProtoImplClass { - const temp = { - [name]: function(ctx) { - return ctx.getLogger(name); - } as any, - }; - const func = temp[name]; - PrototypeUtil.setIsEggPrototype(func); - PrototypeUtil.setFilePath(func, 'mock_file_path'); - PrototypeUtil.setProperty(func, { - name, - initType: ObjectInitType.CONTEXT, - accessLevel: AccessLevel.PUBLIC, - protoImplType: COMPATIBLE_PROTO_IMPLE_TYPE, - }); - QualifierUtil.addProtoQualifier(func, LoadUnitNameQualifierAttribute, 'app'); - QualifierUtil.addProtoQualifier(func, InitTypeQualifierAttribute, ObjectInitType.CONTEXT); - return func; - } - - private getLoggerNames(ctxClazzNames: string[]): string[] { + private getLoggerNames(ctxClazzNames: string[], singletonClazzNames: string[]): string[] { const loggerNames = Array.from(this.app.loggers.keys()); // filter logger/coreLogger - return loggerNames.filter(t => !ctxClazzNames.includes(t)); + return loggerNames.filter(t => !ctxClazzNames.includes(t) && !singletonClazzNames.includes(t)); } load(): EggProtoImplClass[] { @@ -123,27 +114,26 @@ export class EggAppLoader implements Loader { ]); APP_CLAZZ_BLACK_LIST.forEach(t => allSingletonClazzNameSet.delete(t)); const allSingletonClazzNames = Array.from(allSingletonClazzNameSet); - const allContextClazzNames = Array.from(new Set([ + const allContextClazzNamesSet = new Set([ ...contextProperties, ...DEFAULT_CONTEXT_CLAZZ, - ])); - const loggerNames = this.getLoggerNames(allContextClazzNames); - const allSingletonClazzs = allSingletonClazzNames.map(name => this.buildAppClazz(name)); - const allContextClazzs = allContextClazzNames.map(name => this.buildCtxClazz(name)); + ]); + CONTEXT_CLAZZ_BLACK_LIST.forEach(t => allContextClazzNamesSet.delete(t)); + const allContextClazzNames = Array.from(allContextClazzNamesSet); + const loggerNames = this.getLoggerNames(allContextClazzNames, allSingletonClazzNames); + const allSingletonClazzs = allSingletonClazzNames.map(name => this.buildClazz(name, EggType.APP)); + const allContextClazzs = allContextClazzNames.map(name => this.buildClazz(name, EggType.CONTEXT)); const appLoggerClazzs = loggerNames.map(name => this.buildAppLoggerClazz(name)); - const ctxLoggerClazzs = loggerNames.map(name => this.buildCtxLoggerClazz(name)); return [ ...allSingletonClazzs, ...allContextClazzs, ...appLoggerClazzs, - ...ctxLoggerClazzs, // inner helper class list // TODO: should auto the inner class BackgroundTaskHelper, - EggContextObjectFactory, - EggSingletonObjectFactory, + EggObjectFactory, ]; } } diff --git a/plugin/tegg/lib/EggCompatibleObject.ts b/plugin/tegg/lib/EggCompatibleObject.ts index c55be7e2..0f0d9354 100644 --- a/plugin/tegg/lib/EggCompatibleObject.ts +++ b/plugin/tegg/lib/EggCompatibleObject.ts @@ -1,6 +1,9 @@ import { EggCompatibleProtoImpl } from './EggCompatibleProtoImpl'; -import { EggContext, EggObject, EggObjectFactory, EggObjectLifeCycleContext } from '@eggjs/tegg-runtime'; -import { IdenticalUtil, EggObjectName } from '@eggjs/tegg'; +import { + EggObject, + EggObjectFactory, +} from '@eggjs/tegg-runtime'; +import { IdenticalUtil, EggObjectName, EggType, EggQualifierAttribute } from '@eggjs/tegg'; import { EggPrototype } from '@eggjs/tegg-metadata'; const OBJ = Symbol('EggCompatibleObject#obj'); @@ -10,22 +13,28 @@ export class EggCompatibleObject implements EggObject { private [OBJ]: object; readonly proto: EggCompatibleProtoImpl; readonly name: EggObjectName; - readonly ctx?: EggContext; readonly id: string; + readonly isContext: boolean; - constructor(name: EggObjectName, proto: EggCompatibleProtoImpl, ctx?: EggContext) { + constructor(name: EggObjectName, proto: EggCompatibleProtoImpl) { this.proto = proto; this.name = name; - this.ctx = ctx; - this.id = IdenticalUtil.createObjectId(this.proto.id, this.ctx?.id); + this.id = IdenticalUtil.createObjectId(this.proto.id); + this.isContext = this.proto.verifyQualifier({ + value: EggType.CONTEXT, + attribute: EggQualifierAttribute, + }); } // If the egg object is a getter, // access may have side effect. // So access egg object lazy. get obj() { + if (this.isContext) { + return this.proto.constructEggObject(); + } if (!this[OBJ]) { - this[OBJ] = this.proto.constructorEggCompatibleObject(this.ctx); + this[OBJ] = this.proto.constructEggObject(); } return this[OBJ]; } @@ -34,8 +43,8 @@ export class EggCompatibleObject implements EggObject { return; } - static async createObject(name: EggObjectName, proto: EggPrototype, _: EggObjectLifeCycleContext, ctx?: EggContext): Promise { - return new EggCompatibleObject(name, proto as EggCompatibleProtoImpl, ctx); + static async createObject(name: EggObjectName, proto: EggPrototype): Promise { + return new EggCompatibleObject(name, proto as EggCompatibleProtoImpl); } } diff --git a/plugin/tegg/lib/EggCompatibleProtoImpl.ts b/plugin/tegg/lib/EggCompatibleProtoImpl.ts index 4f796ead..3ec25aba 100644 --- a/plugin/tegg/lib/EggCompatibleProtoImpl.ts +++ b/plugin/tegg/lib/EggCompatibleProtoImpl.ts @@ -5,18 +5,16 @@ import { MetadataUtil, ObjectInitTypeLike, QualifierInfo, - PrototypeUtil, QualifierUtil, Id, IdenticalUtil, + QualifierValue, } from '@eggjs/tegg'; import { EggPrototype, InjectObjectProto, EggPrototypeLifecycleContext, } from '@eggjs/tegg-metadata'; -import { EggContext } from '@eggjs/tegg-runtime'; -import { EGG_CONTEXT } from '@eggjs/egg-module-common'; export const COMPATIBLE_PROTO_IMPLE_TYPE = 'EGG_COMPATIBLE'; @@ -64,12 +62,12 @@ export class EggCompatibleProtoImpl implements EggPrototype { return selfQualifiers?.value === qualifier.value; } - constructEggObject(): object { - return {}; + getQualifier(attribute: string): QualifierValue | undefined { + return this.qualifiers.find(t => t.attribute === attribute)?.value; } - constructorEggCompatibleObject(ctx?: EggContext) { - return Reflect.apply(this.clazz, null, [ ctx?.get(EGG_CONTEXT) ]); + constructEggObject(): object { + return Reflect.apply(this.clazz, null, []); } getMetaData(metadataKey: MetaDataKey): T | undefined { @@ -78,11 +76,13 @@ export class EggCompatibleProtoImpl implements EggPrototype { static create(ctx: EggPrototypeLifecycleContext): EggPrototype { const { clazz, loadUnit } = ctx; - const property = PrototypeUtil.getProperty(clazz)!; - const name = property.name; + const name = ctx.prototypeInfo.name; const id = IdenticalUtil.createProtoId(loadUnit.id, name); const proto = new EggCompatibleProtoImpl( - id, name, clazz, property.initType, loadUnit.id, QualifierUtil.getProtoQualifiers(clazz), + id, name, clazz, ctx.prototypeInfo.initType, loadUnit.id, [ + ...QualifierUtil.getProtoQualifiers(clazz), + ...(ctx.prototypeInfo.qualifiers ?? []), + ], ); return proto; } diff --git a/plugin/tegg/lib/EggContextCompatibleHook.ts b/plugin/tegg/lib/EggContextCompatibleHook.ts index fdfba4b7..34ae55c3 100644 --- a/plugin/tegg/lib/EggContextCompatibleHook.ts +++ b/plugin/tegg/lib/EggContextCompatibleHook.ts @@ -1,9 +1,8 @@ -import { LifecycleHook, ObjectInitType } from '@eggjs/tegg'; -import { EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; +import { BackgroundTaskHelper, LifecycleHook, ObjectInitType, PrototypeUtil } from '@eggjs/tegg'; +import { EggContainerFactory, EggContext, EggContextLifecycleContext } from '@eggjs/tegg-runtime'; import { EggPrototype } from '@eggjs/tegg-metadata'; import { ModuleHandler } from './ModuleHandler'; import { ROOT_PROTO } from '@eggjs/egg-module-common'; -import { EggCompatibleProtoImpl } from './EggCompatibleProtoImpl'; export class EggContextCompatibleHook implements LifecycleHook { private readonly moduleHandler: ModuleHandler; @@ -15,11 +14,6 @@ export class EggContextCompatibleHook implements LifecycleHook { + const rootProto = ctx.get(ROOT_PROTO); + if (rootProto) { + // Ensure ContextInitiator be called. + await EggContainerFactory.getOrCreateEggObject(rootProto as EggPrototype); } } } diff --git a/plugin/tegg/lib/EggContextHandler.ts b/plugin/tegg/lib/EggContextHandler.ts new file mode 100644 index 00000000..88b01dbe --- /dev/null +++ b/plugin/tegg/lib/EggContextHandler.ts @@ -0,0 +1,30 @@ +import { Application } from 'egg'; +import { ContextHandler, EggContext } from '@eggjs/tegg-runtime'; +import { EGG_CONTEXT } from '@eggjs/egg-module-common'; + +export class EggContextHandler { + private readonly app: Application; + + constructor(app: Application) { + this.app = app; + } + + getContextCallback(): EggContext { + const ctx = this.app.currentContext; + return ctx && ctx.teggContext; + } + + async run(eggContext: EggContext, fn: () => Promise): Promise { + const ctx = eggContext.get(EGG_CONTEXT); + return await this.app.ctxStorage.run(ctx, fn); + } + + register() { + ContextHandler.getContextCallback = () => { + return this.getContextCallback(); + }; + ContextHandler.runInContextCallback = async (context: EggContext, fn: () => Promise) => { + return await this.run(context, fn); + }; + } +} diff --git a/plugin/tegg/lib/EggModuleLoader.ts b/plugin/tegg/lib/EggModuleLoader.ts index d57ea64f..afee5f5c 100644 --- a/plugin/tegg/lib/EggModuleLoader.ts +++ b/plugin/tegg/lib/EggModuleLoader.ts @@ -1,15 +1,7 @@ -import { EggLoadUnitType, Loader, LoadUnitFactory } from '@eggjs/tegg-metadata'; +import { EggLoadUnitType, Loader, LoadUnitFactory, AppGraph, ModuleNode } from '@eggjs/tegg-metadata'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { EggAppLoader } from './EggAppLoader'; import { Application } from 'egg'; -import { AppGraph, ModuleNode } from './AppGraph'; -import { - InitTypeQualifierAttribute, - LoadUnitNameQualifierAttribute, - PrototypeUtil, - QualifierUtil, -} from '@eggjs/tegg'; -import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; export class EggModuleLoader { app: Application; @@ -33,19 +25,6 @@ export class EggModuleLoader { loaderCache.set(modulePath, loader); const clazzList = loader.load(); for (const clazz of clazzList) { - // TODO copy from ModuleLoadUnit, duplicate code - const moduleName = ModuleConfigUtil.readModuleNameSync(modulePath); - const property = PrototypeUtil.getProperty(clazz)!; - const defaultQualifier = [{ - attribute: InitTypeQualifierAttribute, - value: property.initType, - }, { - attribute: LoadUnitNameQualifierAttribute, - value: moduleName, - }]; - defaultQualifier.forEach(qualifier => { - QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value); - }); moduleNode.addClazz(clazz); } appGraph.addNode(moduleNode); diff --git a/plugin/tegg/lib/EggQualifierProtoHook.ts b/plugin/tegg/lib/EggQualifierProtoHook.ts new file mode 100644 index 00000000..8f7c5231 --- /dev/null +++ b/plugin/tegg/lib/EggQualifierProtoHook.ts @@ -0,0 +1,66 @@ +import { LoadUnitLifecycleContext, LoadUnit } from '@eggjs/tegg-metadata'; +import { + LifecycleHook, + PrototypeUtil, + QualifierUtil, + EggQualifierAttribute, + EggType, +} from '@eggjs/tegg'; +import { Application } from 'egg'; +import { + APP_CLAZZ_BLACK_LIST, + CONTEXT_CLAZZ_BLACK_LIST, + DEFAULT_APP_CLAZZ, + DEFAULT_CONTEXT_CLAZZ, +} from './EggAppLoader'; +import { ObjectUtils } from '@eggjs/tegg-common-util'; + +export class EggQualifierProtoHook implements LifecycleHook { + private readonly app: Application; + + constructor(app: Application) { + this.app = app; + } + + async preCreate(ctx: LoadUnitLifecycleContext): Promise { + const clazzList = ctx.loader.load(); + const appProperties = ObjectUtils.getProperties(this.app); + const ctxProperties = ObjectUtils.getProperties(this.app.context); + for (const clazz of clazzList) { + for (const injectObject of PrototypeUtil.getInjectObjects(clazz) || []) { + const propertyQualifiers = QualifierUtil.getProperQualifiers(clazz, injectObject.refName); + const hasEggQualifier = propertyQualifiers.find(t => t.attribute === EggQualifierAttribute); + if (hasEggQualifier) { + continue; + } + if (this.isCtxObject(injectObject.objName, ctxProperties)) { + QualifierUtil.addProperQualifier(clazz, injectObject.refName, EggQualifierAttribute, EggType.CONTEXT); + } else if (this.isAppObject(injectObject.objName, appProperties)) { + QualifierUtil.addProperQualifier(clazz, injectObject.refName, EggQualifierAttribute, EggType.APP); + } + } + } + } + + private isAppObject(name: PropertyKey, appProperties: string[]) { + name = String(name); + if (APP_CLAZZ_BLACK_LIST.includes(name)) { + return false; + } + if (DEFAULT_APP_CLAZZ.includes(name)) { + return true; + } + return appProperties.includes(name); + } + + private isCtxObject(name: PropertyKey, ctxProperties: string[]) { + name = String(name); + if (CONTEXT_CLAZZ_BLACK_LIST.includes(name)) { + return false; + } + if (DEFAULT_CONTEXT_CLAZZ.includes(name)) { + return true; + } + return ctxProperties.includes(name); + } +} diff --git a/plugin/tegg/lib/ModuleHandler.ts b/plugin/tegg/lib/ModuleHandler.ts index 2377c4b9..e5661f02 100644 --- a/plugin/tegg/lib/ModuleHandler.ts +++ b/plugin/tegg/lib/ModuleHandler.ts @@ -18,29 +18,36 @@ export class ModuleHandler extends Base { private readonly app: Application; constructor(app: Application) { - super({ initMethod: '_init' }); + super(); this.app = app; this.loadUnitLoader = new EggModuleLoader(this.app); } - async _init() { - this.app.eggPrototypeCreatorFactory.registerPrototypeCreator( - COMPATIBLE_PROTO_IMPLE_TYPE, EggCompatibleProtoImpl.create); + async init() { + try { + this.app.eggPrototypeCreatorFactory.registerPrototypeCreator( + COMPATIBLE_PROTO_IMPLE_TYPE, EggCompatibleProtoImpl.create); - await this.loadUnitLoader.load(); - const instances: LoadUnitInstance[] = []; - // TODO fixtures dts broken the module defintion - (this.app as any).module = {}; + await this.loadUnitLoader.load(); + const instances: LoadUnitInstance[] = []; + // TODO fixtures dts broken the module defintion + (this.app as any).module = {}; - for (const loadUnit of this.loadUnits) { - const instance = await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); - if (instance.loadUnit.type !== EggLoadUnitType.APP) { - CompatibleUtil.appCompatible(this.app, instance); + for (const loadUnit of this.loadUnits) { + const instance = await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); + if (instance.loadUnit.type !== EggLoadUnitType.APP) { + CompatibleUtil.appCompatible(this.app, instance); + } + instances.push(instance); } - instances.push(instance); + CompatibleUtil.contextModuleCompatible((this.app as any).context as Context, instances); + this.loadUnitInstances = instances; + this.ready(true); + } catch (e) { + this.ready(e); + throw e; } - CompatibleUtil.contextModuleCompatible((this.app as any).context as Context, instances); - this.loadUnitInstances = instances; + } async destroy() { diff --git a/plugin/tegg/lib/Utils.ts b/plugin/tegg/lib/Utils.ts new file mode 100644 index 00000000..4d879fad --- /dev/null +++ b/plugin/tegg/lib/Utils.ts @@ -0,0 +1,38 @@ +function prepareObjectStackTrace(_, stack) { + return stack; +} + +export function getCalleeFromStack(withLine: boolean, stackIndex?: number) { + stackIndex = stackIndex === undefined ? 2 : stackIndex; + const limit = Error.stackTraceLimit; + const prep = Error.prepareStackTrace; + + Error.prepareStackTrace = prepareObjectStackTrace; + Error.stackTraceLimit = 5; + + // capture the stack + const obj: any = {}; + Error.captureStackTrace(obj); + let callSite = obj.stack[stackIndex]; + let fileName; + /* istanbul ignore else */ + if (callSite) { + // egg-mock will create a proxy + // https://github.com/eggjs/egg-mock/blob/master/lib/app.js#L174 + fileName = callSite.getFileName(); + /* istanbul ignore if */ + if (fileName && fileName.endsWith('egg-mock/lib/app.js')) { + // TODO: add test + callSite = obj.stack[stackIndex + 1]; + fileName = callSite.getFileName(); + } + } + + Error.prepareStackTrace = prep; + Error.stackTraceLimit = limit; + + /* istanbul ignore if */ + if (!callSite || !fileName) return ''; + if (!withLine) return fileName; + return `${fileName}:${callSite.getLineNumber()}:${callSite.getColumnNumber()}`; +} diff --git a/plugin/tegg/lib/ctx_lifecycle_middleware.ts b/plugin/tegg/lib/ctx_lifecycle_middleware.ts index 2c1caa77..4171e2f6 100644 --- a/plugin/tegg/lib/ctx_lifecycle_middleware.ts +++ b/plugin/tegg/lib/ctx_lifecycle_middleware.ts @@ -1,8 +1,14 @@ import { TEggPluginContext } from '../app/extend/context'; import { EggContextImpl } from './EggContextImpl'; -import { ROOT_PROTO } from '@eggjs/egg-module-common'; +import { ROOT_PROTO, TEGG_CONTEXT } from '@eggjs/egg-module-common'; export default async function ctxLifecycleMiddleware(ctx: TEggPluginContext, next) { + // should not recreate teggContext + if (ctx[TEGG_CONTEXT]) { + await next(); + return; + } + const lifecycle = {}; const teggCtx = new EggContextImpl(ctx); diff --git a/plugin/tegg/lib/run_in_background.ts b/plugin/tegg/lib/run_in_background.ts new file mode 100644 index 00000000..b696eb64 --- /dev/null +++ b/plugin/tegg/lib/run_in_background.ts @@ -0,0 +1,59 @@ +import { Application, Context } from 'egg'; +import { BackgroundTaskHelper, PrototypeUtil } from '@eggjs/tegg'; +import { EggPrototype } from '@eggjs/tegg-metadata'; +import { TEGG_CONTEXT } from '@eggjs/egg-module-common'; +import { TEggPluginContext } from '../app/extend/context'; +import { getCalleeFromStack } from './Utils'; + +export const LONG_STACK_DELIMITER = '\n --------------------\n'; + +function addLongStackTrace(err: Error, causeError: Error) { + const callSiteStack = causeError.stack; + if (!callSiteStack || typeof callSiteStack !== 'string') { + return; + } + const index = callSiteStack.indexOf('\n'); + if (index !== -1) { + err.stack += LONG_STACK_DELIMITER + callSiteStack.substring(index + 1); + } +} + +export function hijackRunInBackground(app: Application) { + const eggRunInBackground = app.context.runInBackground; + app.context.runInBackground = function runInBackground(this: TEggPluginContext, scope: (ctx: Context) => Promise) { + if (!this[TEGG_CONTEXT]) { + return Reflect.apply(eggRunInBackground, this, [ scope ]); + } + const caseError = new Error('cause'); + let resolveBackgroundTask; + const backgroundTaskPromise = new Promise(resolve => { + resolveBackgroundTask = resolve; + }); + const newScope = async () => { + try { + await scope(this); + } catch (e) { + addLongStackTrace(e, caseError); + throw e; + } finally { + resolveBackgroundTask(); + } + }; + const taskName = (scope as any)._name || scope.name || getCalleeFromStack(true, 2); + (scope as any)._name = taskName; + Object.defineProperty(newScope, 'name', { + value: taskName, + enumerable: false, + configurable: true, + writable: false, + }); + Reflect.apply(eggRunInBackground, this, [ newScope ]); + + const proto = PrototypeUtil.getClazzProto(BackgroundTaskHelper); + const eggObject = app.eggContainerFactory.getEggObject(proto as EggPrototype); + const backgroundTaskHelper = eggObject.obj as BackgroundTaskHelper; + backgroundTaskHelper.run(async () => { + await backgroundTaskPromise; + }); + }; +} diff --git a/plugin/tegg/package.json b/plugin/tegg/package.json index 95243224..f1d8cc4d 100644 --- a/plugin/tegg/package.json +++ b/plugin/tegg/package.json @@ -6,7 +6,7 @@ "teggConfig" ] }, - "version": "1.4.0", + "version": "3.23.0", "description": "module plugin for egg", "keywords": [ "egg", @@ -26,11 +26,11 @@ ], "types": "typings/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -39,28 +39,35 @@ "repository": { "type": "git", "url": "git@github.com:eggjs/tegg.git", - "directory": "plugin/eventbus" + "directory": "plugin/tegg" }, "engines": { "node": ">=14.0.0" }, "dependencies": { - "@eggjs/egg-module-common": "^1.0.0", - "@eggjs/tegg": "^1.5.0", - "@eggjs/tegg-background-task": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-dynamic-inject-runtime": "^1.4.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0", - "sdk-base": "^3.6.0" + "@eggjs/egg-module-common": "^3.23.0", + "@eggjs/tegg": "^3.23.0", + "@eggjs/tegg-background-task": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-dynamic-inject-runtime": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0", + "sdk-base": "^4.2.0" }, "devDependencies": { - "@eggjs/tegg-config": "^1.3.0", - "egg": "^2.26.1", - "egg-mock": "^3.25.1", - "egg-tracer": "^1.1.0", - "mz-modules": "^2.1.0" + "@eggjs/tegg-config": "^3.23.0", + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "egg": "^3.9.1", + "egg-logger": "^3.0.1", + "egg-mock": "^5.5.0", + "egg-schedule": "^4.0.0", + "egg-tracer": "^2.0.0", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" }, "publishConfig": { "access": "public" diff --git a/plugin/tegg/test/AccessLevelCheck.test.ts b/plugin/tegg/test/AccessLevelCheck.test.ts index a388bfea..4a5790ae 100644 --- a/plugin/tegg/test/AccessLevelCheck.test.ts +++ b/plugin/tegg/test/AccessLevelCheck.test.ts @@ -37,16 +37,18 @@ describe('test/AccessLevelCheck.test.ts', () => { }); it('should work: private has some name', async () => { - const ctx = await app.mockModuleContext(); - const mainService: MainService = await ctx.getEggObject(MainService); - assert(mainService); - assert(mainService.invokeFoo() === 'moduleMain-FooService-Method'); + await app.mockModuleContextScope(async ctx => { + const mainService: MainService = await ctx.getEggObject(MainService); + assert(mainService); + assert(mainService.invokeFoo() === 'moduleMain-FooService-Method'); + }); }); it('should work: public/private has some name', async () => { - const ctx = await app.mockModuleContext(); - const mainService: MainService = await ctx.getEggObject(MainService); - assert(mainService); - assert(mainService.invokeBar() === 'moduleMain-BarService-Method'); + await app.mockModuleContextScope(async ctx => { + const mainService: MainService = await ctx.getEggObject(MainService); + assert(mainService); + assert(mainService.invokeBar() === 'moduleMain-BarService-Method'); + }); }); }); diff --git a/plugin/tegg/test/BackgroundTask.test.ts b/plugin/tegg/test/BackgroundTask.test.ts index 93966c17..6ac6d48c 100644 --- a/plugin/tegg/test/BackgroundTask.test.ts +++ b/plugin/tegg/test/BackgroundTask.test.ts @@ -1,9 +1,12 @@ -import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; -import { CountService } from './fixtures/apps/background-app/modules/multi-module-background/CountService'; -import sleep from 'mz-modules/sleep'; import fs from 'fs'; +import mm from 'egg-mock'; +import { TimerUtil } from '@eggjs/tegg-common-util'; +import { TEGG_CONTEXT } from '@eggjs/egg-module-common'; +import { BackgroundTaskHelper } from '@eggjs/tegg'; +import { EggContext, EggContextLifecycleUtil } from '@eggjs/tegg-runtime'; +import { CountService } from './fixtures/apps/background-app/modules/multi-module-background/CountService'; describe('test/BackgroundTask.test.ts', () => { const appDir = path.join(__dirname, 'fixtures/apps/background-app'); @@ -37,7 +40,7 @@ describe('test/BackgroundTask.test.ts', () => { const countService = await app.getEggObject(CountService); assert(countService.count === 0); - await sleep(1000); + await TimerUtil.sleep(1000); assert(countService.count === 1); }); @@ -47,8 +50,28 @@ describe('test/BackgroundTask.test.ts', () => { .get('/backgroudTimeout') .expect(200); - await sleep(7000); + await TimerUtil.sleep(7000); const errorLog = fs.readFileSync(path.resolve(appDir, 'logs/egg-app/common-error.log'), 'utf-8'); assert(errorLog.includes('Can not read property `testObj` because egg ctx has been destroyed [')); }); + + it('should release', async () => { + let teggCtx: EggContext; + await app.mockModuleContextScope(async ctx => { + teggCtx = ctx[TEGG_CONTEXT]; + const backgroundTaskHelper = await ctx.getEggObject(BackgroundTaskHelper); + backgroundTaskHelper.run(async () => { + // do nothing + }); + }); + const lifecycleList = EggContextLifecycleUtil.getObjectLifecycleList(teggCtx!); + assert(lifecycleList.length === 0); + }); + + it('config should work', async () => { + await app.mockModuleContextScope(async ctx => { + const backgroundTaskHelper = await ctx.getEggObject(BackgroundTaskHelper); + assert(backgroundTaskHelper.timeout === Infinity); + }); + }); }); diff --git a/plugin/tegg/test/EggCompatible.test.ts b/plugin/tegg/test/EggCompatible.test.ts index 807a5b48..f3bbf359 100644 --- a/plugin/tegg/test/EggCompatible.test.ts +++ b/plugin/tegg/test/EggCompatible.test.ts @@ -1,6 +1,8 @@ import mm from 'egg-mock'; import assert from 'assert'; import path from 'path'; +import EggTypeService from './fixtures/apps/egg-app/modules/multi-module-service/EggTypeService'; +import TraceService from './fixtures/apps/egg-app/modules/multi-module-service/TraceService'; describe('test/EggCompatible.test.ts', () => { let app; @@ -76,24 +78,35 @@ describe('test/EggCompatible.test.ts', () => { }); it('inject config should work', async () => { - const ctx = await app.mockModuleContext(); - const baseDir = ctx.module.multiModuleService.configService.getBaseDir(); - assert(baseDir); - await app.destroyModuleContext(ctx); + await app.mockModuleContextScope(async () => { + const baseDir = app.module.multiModuleService.configService.getBaseDir(); + assert(baseDir); + + const runtimeConfig = app.module.multiModuleService.configService.getRuntimeConfig(); + assert.deepEqual(runtimeConfig, { + baseDir: path.join(__dirname, 'fixtures/apps/egg-app'), + env: 'unittest', + name: 'egg-app', + }); + }); }); it('inject user should work', async () => { - app.mockUser(); - const ctx = await app.mockModuleContext(); - const userName = ctx.module.multiModuleService.configService.getCurrentUserName(); - assert(userName); - await app.destroyModuleContext(ctx); + await app.mockModuleContextScope(async () => { + const userName = app.module.multiModuleService.configService.getCurrentUserName(); + assert(userName); + }, { + user: { + userName: 'mock_user', + }, + }); }); it('custom logger should work', async () => { - const ctx = await app.mockModuleContext(); - await ctx.module.multiModuleService.customLoggerService.printLog(); - await app.destroyModuleContext(ctx); + await app.mockModuleContextScope(async ctx => { + await app.module.multiModuleService.customLoggerService.printLog(); + await app.destroyModuleContext(ctx); + }); }); it('use singleton proto should work', async () => { @@ -109,15 +122,43 @@ describe('test/EggCompatible.test.ts', () => { }); it('module proxy cache should work', async () => { - const ctx = await app.mockModuleContext(); - const moduleMultiModuleService1 = ctx.module.multiModuleService; - const moduleMultiModuleService2 = ctx.module.multiModuleService; - assert(moduleMultiModuleService1 === moduleMultiModuleService2); + await app.mockModuleContextScope(async () => { + const moduleMultiModuleService1 = app.module.multiModuleService; + const moduleMultiModuleService2 = app.module.multiModuleService; + assert(moduleMultiModuleService1 === moduleMultiModuleService2); + }); }); it('should load egg object with no side effect', async () => { - const ctx = await app.mockModuleContext(); - assert(ctx.counter === 0); - assert(ctx.counter === 1); + await app.mockModuleContextScope(async ctx => { + assert(ctx.counter === 0); + assert(ctx.counter === 1); + }); + }); + + it('should support EggQualifier', async () => { + await app.mockModuleContextScope(async () => { + const eggTypeService = await app.getEggObject(EggTypeService); + const result = eggTypeService.testInject(); + assert.deepStrictEqual(result, { app: { from: 'app' }, ctx: { from: 'ctx' } }); + }); + }); + + it('should support context property', async () => { + mm(app.context, 'tracer', { + traceId: 'mockTraceId', + }); + await app.mockModuleContextScope(async () => { + const traceService: TraceService = await app.getEggObject(TraceService); + assert(traceService.getTraceId() === 'mockTraceId'); + }); + mm(app.context, 'tracer', { + traceId: 'mockTraceId2', + }); + await app.mockModuleContextScope(async () => { + const traceService: TraceService = await app.getEggObject(TraceService); + console.log('id: ', traceService.getTraceId()); + assert(traceService.getTraceId() === 'mockTraceId2'); + }); }); }); diff --git a/plugin/tegg/test/SameProtoName.test.ts b/plugin/tegg/test/SameProtoName.test.ts index 76f7b48a..6507a035 100644 --- a/plugin/tegg/test/SameProtoName.test.ts +++ b/plugin/tegg/test/SameProtoName.test.ts @@ -28,9 +28,10 @@ describe('test/SameProtoName.test.ts', () => { }); it('should work', async () => { - const ctx = await app.mockModuleContext(); - const barService = await ctx.getEggObject(BarService); - assert(barService); - assert(barService.fooService); + await app.mockModuleContextScope(async ctx => { + const barService = await ctx.getEggObject(BarService); + assert(barService); + assert(barService.fooService); + }); }); }); diff --git a/plugin/tegg/test/app/extend/application.test.ts b/plugin/tegg/test/app/extend/application.test.ts index 49646739..40931113 100644 --- a/plugin/tegg/test/app/extend/application.test.ts +++ b/plugin/tegg/test/app/extend/application.test.ts @@ -5,7 +5,7 @@ import { Application } from 'egg'; import AppService from '../../fixtures/apps/egg-app/modules/multi-module-service/AppService'; import PersistenceService from '../../fixtures/apps/egg-app/modules/multi-module-repo/PersistenceService'; -describe('test/app/extend/context.test.ts', () => { +describe('test/app/extend/application.test.ts', () => { let app: Application; after(async () => { diff --git a/plugin/tegg/test/app/extend/application.unittest.test.ts b/plugin/tegg/test/app/extend/application.unittest.test.ts index 00d2fae1..2e1376e0 100644 --- a/plugin/tegg/test/app/extend/application.unittest.test.ts +++ b/plugin/tegg/test/app/extend/application.unittest.test.ts @@ -26,9 +26,21 @@ describe('test/app/extend/application.unittest.test.ts', () => { }); it('should work', async function() { + await app.mockModuleContextScope(async () => { + const traceId = await app.module.multiModuleService.traceService.getTraceId(); + assert(traceId); + }); + }); + + it('should not call mockModuleContext twice', async () => { const ctx = await app.mockModuleContext(); - const traceId = await ctx.module.multiModuleService.traceService.getTraceId(); - assert(traceId); - await app.destroyModuleContext(ctx); + try { + await assert.rejects( + app.mockModuleContext(), + /should not call mockModuleContext twice./, + ); + } finally { + await app.destroyModuleContext(ctx); + } }); }); diff --git a/plugin/tegg/test/app/extend/context.test.ts b/plugin/tegg/test/app/extend/context.test.ts index 177bfa3f..6d7f680a 100644 --- a/plugin/tegg/test/app/extend/context.test.ts +++ b/plugin/tegg/test/app/extend/context.test.ts @@ -2,8 +2,10 @@ import assert from 'assert'; import path from 'path'; import mm from 'egg-mock'; import { Application } from 'egg'; +import { TimerUtil } from '@eggjs/tegg-common-util'; import AppService from '../../fixtures/apps/egg-app/modules/multi-module-service/AppService'; import PersistenceService from '../../fixtures/apps/egg-app/modules/multi-module-repo/PersistenceService'; +import { LONG_STACK_DELIMITER } from '../../../lib/run_in_background'; describe('test/app/extend/context.test.ts', () => { let app: Application; @@ -30,14 +32,77 @@ describe('test/app/extend/context.test.ts', () => { describe('getEggObject', () => { it('should work', async () => { - const ctx = await app.mockModuleContext(); - const appService = await ctx.getEggObject(AppService); - assert(appService instanceof AppService); + await app.mockModuleContextScope(async ctx => { + const appService = await ctx.getEggObject(AppService); + assert(appService instanceof AppService); - const persistenceService = await ctx.getEggObject(PersistenceService); - assert(persistenceService instanceof PersistenceService); + const persistenceService = await ctx.getEggObject(PersistenceService); + assert(persistenceService instanceof PersistenceService); + }); + }); + }); + + describe('beginModuleScope', () => { + it('should be reentrant', async () => { + await app.mockModuleContextScope(async ctx => { + await ctx.beginModuleScope(async () => { + // ...do nothing + }); + assert(ctx.teggContext.destroyed === false); + }); + }); + }); + + describe('runInBackground', () => { + it('should notify background task helper', async () => { + let backgroundIsDone = false; + await app.mockModuleContextScope(async ctx => { + ctx.runInBackground(async () => { + await TimerUtil.sleep(100); + backgroundIsDone = true; + }); + }); + assert(backgroundIsDone); + }); + + it('recursive runInBackground should work', async () => { + let backgroundIsDone = false; + await app.mockModuleContextScope(async ctx => { + ctx.runInBackground(async () => { + await TimerUtil.sleep(100); + ctx.runInBackground(async () => { + await TimerUtil.sleep(100); + backgroundIsDone = true; + }); + }); + }); + assert(backgroundIsDone); + }); - await app.destroyModuleContext(ctx); + it('stack should be continuous', async () => { + let backgroundError; + app.on('error', e => { + backgroundError = e; + }); + await app.mockModuleContextScope(async ctx => { + ctx.runInBackground(async () => { + throw new Error('background'); + }); + await TimerUtil.sleep(1000); + }); + const stack: string = backgroundError.stack; + // background + // at ~/plugin/tegg/test/app/extend/context.test.ts:88:17 + // at ~/plugin/tegg/test/app/extend/context.test.ts:82:21 (~/plugin/tegg/lib/run_in_background.ts:34:15) + // at ~/node_modules/egg/app/extend/context.js:232:49 + // -------------------- + // at Object.runInBackground (~/plugin/tegg/lib/run_in_background.ts:27:23) + // at ~/plugin/tegg/test/app/extend/context.test.ts:87:13 + // at ~/plugin/tegg/app/extend/application.unittest.ts:49:22 + // at async Proxy.mockContextScope (~/node_modules/egg-mock/app/extend/application.js:81:12) + // at async Context. (~/plugin/tegg/test/app/extend/context.test.ts:86:7) + assert(stack.includes(__filename)); + assert(stack.includes(LONG_STACK_DELIMITER)); }); }); }); diff --git a/plugin/tegg/test/fixtures/apps/access-level-check/app/controller/app.ts b/plugin/tegg/test/fixtures/apps/access-level-check/app/controller/app.ts index 5d733b68..185543e3 100644 --- a/plugin/tegg/test/fixtures/apps/access-level-check/app/controller/app.ts +++ b/plugin/tegg/test/fixtures/apps/access-level-check/app/controller/app.ts @@ -2,7 +2,7 @@ import { Controller } from 'egg'; export default class App extends Controller { async invokeFoo() { - const ret = await this.ctx.module.moduleMain.mainService.invokeFoo(); + const ret = await this.ctx.app.module.moduleMain.mainService.invokeFoo(); this.ctx.body = { ret, }; diff --git a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/BarService.ts b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/BarService.ts index cf757bb5..2ee1bb43 100644 --- a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/BarService.ts +++ b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/BarService.ts @@ -1,6 +1,6 @@ -import { AccessLevel, ContextProto } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto } from '@eggjs/tegg'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class BarService { diff --git a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/FooService.ts b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/FooService.ts index b7fa6127..ace7e841 100644 --- a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/FooService.ts +++ b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-a/FooService.ts @@ -1,7 +1,7 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import BarService from './BarService'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PRIVATE, }) export default class FooService { diff --git a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/BarService.ts b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/BarService.ts index f5692591..45ca81d5 100644 --- a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/BarService.ts +++ b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/BarService.ts @@ -1,6 +1,6 @@ -import { AccessLevel, ContextProto } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto } from '@eggjs/tegg'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PRIVATE, }) export default class BarService { diff --git a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/FooService.ts b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/FooService.ts index ba93a9bc..e88224bd 100644 --- a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/FooService.ts +++ b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/FooService.ts @@ -1,7 +1,7 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import BarService from './BarService'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PRIVATE, }) export default class FooService { diff --git a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/MainService.ts b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/MainService.ts index 58f6b51b..0f34e632 100644 --- a/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/MainService.ts +++ b/plugin/tegg/test/fixtures/apps/access-level-check/modules/module-main/MainService.ts @@ -1,9 +1,9 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import BarService from './BarService'; import FooService from './FooService'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class MainService { diff --git a/plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/controller/app.ts b/plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/controller/app.ts index 737df1fc..ba436209 100644 --- a/plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/controller/app.ts +++ b/plugin/tegg/test/fixtures/apps/app-with-no-module-json/app/controller/app.ts @@ -2,7 +2,7 @@ import { Controller } from 'egg'; export default class App extends Controller { async baseDir() { - const baseDir = await this.ctx.module.config.configService.getBaseDir(); + const baseDir = await this.ctx.app.module.config.configService.getBaseDir(); this.ctx.body = { baseDir, }; diff --git a/plugin/tegg/test/fixtures/apps/app-with-no-module-json/modules/config-module/ConfigService.ts b/plugin/tegg/test/fixtures/apps/app-with-no-module-json/modules/config-module/ConfigService.ts index a22d7c69..47ee9c21 100644 --- a/plugin/tegg/test/fixtures/apps/app-with-no-module-json/modules/config-module/ConfigService.ts +++ b/plugin/tegg/test/fixtures/apps/app-with-no-module-json/modules/config-module/ConfigService.ts @@ -1,11 +1,11 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import { EggAppConfig } from 'egg'; interface XSessionUser { userName: string; } -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class ConfigService { diff --git a/plugin/tegg/test/fixtures/apps/background-app/app/controller/app.ts b/plugin/tegg/test/fixtures/apps/background-app/app/controller/app.ts index 5633a020..9f6db1ae 100644 --- a/plugin/tegg/test/fixtures/apps/background-app/app/controller/app.ts +++ b/plugin/tegg/test/fixtures/apps/background-app/app/controller/app.ts @@ -3,14 +3,14 @@ import BackgroundService from '../../modules/multi-module-background/BackgroundS export default class App extends Controller { async background() { - const backgroundService = await this.ctx.getEggObject(BackgroundService); + const backgroundService = await this.ctx.app.getEggObject(BackgroundService); await backgroundService.backgroundAdd(); this.ctx.status = 200; this.ctx.body = 'done'; } async backgroudTimeout() { - const backgroundService = await this.ctx.getEggObject(BackgroundService); + const backgroundService = await this.ctx.app.getEggObject(BackgroundService); await backgroundService.backgroundAdd(6000); this.ctx.status = 200; this.ctx.body = 'done'; diff --git a/plugin/tegg/test/fixtures/apps/background-app/app/extend/context.ts b/plugin/tegg/test/fixtures/apps/background-app/app/extend/context.ts deleted file mode 100644 index d7abb6f6..00000000 --- a/plugin/tegg/test/fixtures/apps/background-app/app/extend/context.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - testObj: { - ok: true, - }, -}; diff --git a/plugin/tegg/test/fixtures/apps/background-app/config/config.default.js b/plugin/tegg/test/fixtures/apps/background-app/config/config.default.js index 6d1b8de5..faedd054 100644 --- a/plugin/tegg/test/fixtures/apps/background-app/config/config.default.js +++ b/plugin/tegg/test/fixtures/apps/background-app/config/config.default.js @@ -15,6 +15,9 @@ module.exports = function(appInfo) { ignoreJSON: false, } }, + backgroundTask: { + timeout: Infinity, + }, }; return config; }; diff --git a/plugin/tegg/test/fixtures/apps/background-app/modules/multi-module-background/BackgroundService.ts b/plugin/tegg/test/fixtures/apps/background-app/modules/multi-module-background/BackgroundService.ts index a6225f39..fa85e4de 100644 --- a/plugin/tegg/test/fixtures/apps/background-app/modules/multi-module-background/BackgroundService.ts +++ b/plugin/tegg/test/fixtures/apps/background-app/modules/multi-module-background/BackgroundService.ts @@ -1,10 +1,15 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import assert from 'assert'; +import { AccessLevel, SingletonProto, Inject, ContextProto } from '@eggjs/tegg'; import { BackgroundTaskHelper } from '@eggjs/tegg-background-task'; +import { TimerUtil } from '@eggjs/tegg-common-util'; import { CountService } from './CountService'; -import sleep from 'mz-modules/sleep'; -import assert from 'assert'; -@ContextProto({ +@ContextProto() +export class TestObj { + ok = true; +} + +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class BackgroundService { @@ -12,14 +17,15 @@ export default class BackgroundService { private readonly backgroundTaskHelper:BackgroundTaskHelper; @Inject() - testObj: any; + testObj: TestObj; @Inject() private readonly countService: CountService; async backgroundAdd(delay = 1000) { + this.backgroundTaskHelper.timeout = 5000; this.backgroundTaskHelper.run(async () => { - await sleep(delay); + await TimerUtil.sleep(delay); assert(this.testObj.ok); this.countService.count += 1; }); diff --git a/plugin/tegg/test/fixtures/apps/egg-app/app/controller/app.ts b/plugin/tegg/test/fixtures/apps/egg-app/app/controller/app.ts index 90af23ab..0f026eeb 100644 --- a/plugin/tegg/test/fixtures/apps/egg-app/app/controller/app.ts +++ b/plugin/tegg/test/fixtures/apps/egg-app/app/controller/app.ts @@ -2,7 +2,7 @@ import { Controller } from 'egg'; export default class App extends Controller { async find() { - const traceId = await this.ctx.module.multiModuleService.traceService.getTraceId(); + const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); const app = await this.ctx.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, @@ -11,7 +11,7 @@ export default class App extends Controller { } async find2() { - const traceId = await this.ctx.module.multiModuleService.traceService.getTraceId(); + const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); const app = await this.ctx.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, @@ -21,7 +21,7 @@ export default class App extends Controller { async save() { const app = this.ctx.request.body; - const traceId = await this.ctx.module.multiModuleService.traceService.getTraceId(); + const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); await this.ctx.module.multiModuleService.appService.save(app); this.ctx.body = { success: true, diff --git a/plugin/tegg/test/fixtures/apps/egg-app/app/extend/application.ts b/plugin/tegg/test/fixtures/apps/egg-app/app/extend/application.ts new file mode 100644 index 00000000..a77e8354 --- /dev/null +++ b/plugin/tegg/test/fixtures/apps/egg-app/app/extend/application.ts @@ -0,0 +1,7 @@ +export default { + get appDefineObject() { + return { + from: 'app', + }; + }, +}; diff --git a/plugin/tegg/test/fixtures/apps/egg-app/app/extend/context.ts b/plugin/tegg/test/fixtures/apps/egg-app/app/extend/context.ts index 934ed2a2..a794e5dd 100644 --- a/plugin/tegg/test/fixtures/apps/egg-app/app/extend/context.ts +++ b/plugin/tegg/test/fixtures/apps/egg-app/app/extend/context.ts @@ -11,4 +11,10 @@ export default { get user() { return {}; }, + + get appDefineObject() { + return { + from: 'ctx', + }; + }, }; diff --git a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/AppRepo.ts b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/AppRepo.ts index e8dbd506..dbb4091b 100644 --- a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/AppRepo.ts +++ b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-repo/AppRepo.ts @@ -1,8 +1,8 @@ import PersistenceService from './PersistenceService'; -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import App from '../multi-module-common/model/App'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { diff --git a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/ConfigService.ts b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/ConfigService.ts index a22d7c69..df55c324 100644 --- a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/ConfigService.ts +++ b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/ConfigService.ts @@ -1,19 +1,22 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject, RuntimeConfig } from '@eggjs/tegg'; import { EggAppConfig } from 'egg'; interface XSessionUser { userName: string; } -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class ConfigService { @Inject() - user: XSessionUser; + private user: XSessionUser; @Inject() - config: EggAppConfig; + private config: EggAppConfig; + + @Inject() + private runtimeConfig: RuntimeConfig; getBaseDir(): string { return this.config.baseDir; @@ -22,4 +25,9 @@ export default class ConfigService { async getCurrentUserName(): Promise { return this.user.userName; } + + getRuntimeConfig(): RuntimeConfig { + return this.runtimeConfig; + } + } diff --git a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/CustomLoggerService.ts b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/CustomLoggerService.ts index 6018df63..76f28c04 100644 --- a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/CustomLoggerService.ts +++ b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/CustomLoggerService.ts @@ -1,11 +1,11 @@ import { AccessLevel, Inject, - ContextProto, + SingletonProto, } from '@eggjs/tegg'; import { EggLogger } from 'egg-logger'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class CustomLoggerService { diff --git a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/EggTypeService.ts b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/EggTypeService.ts new file mode 100644 index 00000000..2921b15b --- /dev/null +++ b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/EggTypeService.ts @@ -0,0 +1,34 @@ +import { AccessLevel, EggQualifier, EggType, Inject, SingletonProto } from '@eggjs/tegg'; +import { EggLogger } from 'egg-logger'; + +interface AppDefObj { + from: string; +} + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export default class EggTypeService { + @Inject({ + name: 'appDefineObject', + }) + @EggQualifier(EggType.APP) + appAppDefineObject: AppDefObj; + + @Inject({ + name: 'appDefineObject', + }) + @EggQualifier(EggType.CONTEXT) + ctxAppDefineObject: AppDefObj; + + @Inject() + @EggQualifier(EggType.APP) + logger: EggLogger; + + testInject() { + return { + app: this.appAppDefineObject, + ctx: this.ctxAppDefineObject, + }; + } +} diff --git a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/SingletonFooService.ts b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/SingletonFooService.ts index 84b32ddb..751e6bea 100644 --- a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/SingletonFooService.ts +++ b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/SingletonFooService.ts @@ -1,10 +1,4 @@ -import { - AccessLevel, - InitTypeQualifier, - Inject, - ObjectInitType, - SingletonProto, -} from '@eggjs/tegg'; +import { AccessLevel, EggQualifier, EggType, Inject, SingletonProto } from '@eggjs/tegg'; import { EggLogger } from 'egg-logger'; @SingletonProto({ @@ -12,7 +6,7 @@ import { EggLogger } from 'egg-logger'; }) export default class SingletonFooService { @Inject() - @InitTypeQualifier(ObjectInitType.SINGLETON) + @EggQualifier(EggType.APP) logger: EggLogger; async printLog(): Promise { diff --git a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/TraceService.ts b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/TraceService.ts index 738e4a7c..d4400c4a 100644 --- a/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/TraceService.ts +++ b/plugin/tegg/test/fixtures/apps/egg-app/modules/multi-module-service/TraceService.ts @@ -1,10 +1,10 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; interface Tracer { traceId: string; } -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class TraceService { diff --git a/plugin/tegg/test/fixtures/apps/recursive-module-app/app/controller/app.ts b/plugin/tegg/test/fixtures/apps/recursive-module-app/app/controller/app.ts index 2092e00f..b697f50e 100644 --- a/plugin/tegg/test/fixtures/apps/recursive-module-app/app/controller/app.ts +++ b/plugin/tegg/test/fixtures/apps/recursive-module-app/app/controller/app.ts @@ -2,8 +2,8 @@ import { Controller } from 'egg'; export default class App extends Controller { async find() { - const traceId = await this.ctx.module.multiModuleService.traceService.getTraceId(); - const app = await this.ctx.module.multiModuleService.appService.findApp(this.ctx.query.name); + const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); + const app = await this.ctx.app.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, app, @@ -12,8 +12,8 @@ export default class App extends Controller { async save() { const app = this.ctx.request.body; - const traceId = await this.ctx.module.multiModuleService.traceService.getTraceId(); - await this.ctx.module.multiModuleService.appService.save(app); + const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); + await this.ctx.app.module.multiModuleService.appService.save(app); this.ctx.body = { success: true, traceId, diff --git a/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-repo/AppRepo.ts b/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-repo/AppRepo.ts index 1975b814..1c4d438f 100644 --- a/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-repo/AppRepo.ts +++ b/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-repo/AppRepo.ts @@ -1,7 +1,7 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppService from '../multi-module-service/AppService'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { diff --git a/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-service/AppService.ts b/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-service/AppService.ts index 6cce0fbb..7b5561cd 100644 --- a/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-service/AppService.ts +++ b/plugin/tegg/test/fixtures/apps/recursive-module-app/modules/multi-module-service/AppService.ts @@ -1,7 +1,7 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { diff --git a/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-a/BarService.ts b/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-a/BarService.ts index 6f73b5e0..b6c85d36 100644 --- a/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-a/BarService.ts +++ b/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-a/BarService.ts @@ -1,7 +1,7 @@ -import { ContextProto, Inject, ModuleQualifier } from '@eggjs/tegg'; +import { SingletonProto, Inject, ModuleQualifier } from '@eggjs/tegg'; import { FooService } from '../module-foo/FooService'; -@ContextProto() +@SingletonProto() export class BarService { @Inject() @ModuleQualifier('foo') diff --git a/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-bar/FooService.ts b/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-bar/FooService.ts index 9ca6f576..003da01d 100644 --- a/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-bar/FooService.ts +++ b/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-bar/FooService.ts @@ -1,6 +1,6 @@ -import { AccessLevel, ContextProto } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto } from '@eggjs/tegg'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class FooService { diff --git a/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-foo/FooService.ts b/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-foo/FooService.ts index 9ca6f576..003da01d 100644 --- a/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-foo/FooService.ts +++ b/plugin/tegg/test/fixtures/apps/same-name-protos/app/modules/module-foo/FooService.ts @@ -1,6 +1,6 @@ -import { AccessLevel, ContextProto } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto } from '@eggjs/tegg'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export class FooService { diff --git a/plugin/tegg/test/fixtures/apps/schedule-app/app/schedule/foo.ts b/plugin/tegg/test/fixtures/apps/schedule-app/app/schedule/foo.ts index 4748b2c9..d335b35f 100644 --- a/plugin/tegg/test/fixtures/apps/schedule-app/app/schedule/foo.ts +++ b/plugin/tegg/test/fixtures/apps/schedule-app/app/schedule/foo.ts @@ -10,7 +10,7 @@ export default class Foo extends Subscription { async subscribe() { await this.ctx.beginModuleScope(async () => { - await this.ctx.module.multiModuleService.appService.findApp(); + await this.ctx.app.module.multiModuleService.appService.findApp(); }); } } diff --git a/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-repo/AppRepo.ts b/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-repo/AppRepo.ts index 929e9d8f..37070f38 100644 --- a/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-repo/AppRepo.ts +++ b/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-repo/AppRepo.ts @@ -1,6 +1,6 @@ -import { AccessLevel, ContextProto } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto } from '@eggjs/tegg'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { diff --git a/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-service/AppService.ts b/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-service/AppService.ts index 6cce0fbb..7b5561cd 100644 --- a/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-service/AppService.ts +++ b/plugin/tegg/test/fixtures/apps/schedule-app/modules/multi-module-service/AppService.ts @@ -1,7 +1,7 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { diff --git a/plugin/tegg/test/fixtures/apps/wrong-order-app/app/controller/app.ts b/plugin/tegg/test/fixtures/apps/wrong-order-app/app/controller/app.ts index 2092e00f..b697f50e 100644 --- a/plugin/tegg/test/fixtures/apps/wrong-order-app/app/controller/app.ts +++ b/plugin/tegg/test/fixtures/apps/wrong-order-app/app/controller/app.ts @@ -2,8 +2,8 @@ import { Controller } from 'egg'; export default class App extends Controller { async find() { - const traceId = await this.ctx.module.multiModuleService.traceService.getTraceId(); - const app = await this.ctx.module.multiModuleService.appService.findApp(this.ctx.query.name); + const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); + const app = await this.ctx.app.module.multiModuleService.appService.findApp(this.ctx.query.name); this.ctx.body = { traceId, app, @@ -12,8 +12,8 @@ export default class App extends Controller { async save() { const app = this.ctx.request.body; - const traceId = await this.ctx.module.multiModuleService.traceService.getTraceId(); - await this.ctx.module.multiModuleService.appService.save(app); + const traceId = await this.ctx.app.module.multiModuleService.traceService.getTraceId(); + await this.ctx.app.module.multiModuleService.appService.save(app); this.ctx.body = { success: true, traceId, diff --git a/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-repo/AppRepo.ts b/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-repo/AppRepo.ts index 929e9d8f..37070f38 100644 --- a/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-repo/AppRepo.ts +++ b/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-repo/AppRepo.ts @@ -1,6 +1,6 @@ -import { AccessLevel, ContextProto } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto } from '@eggjs/tegg'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppRepo { diff --git a/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-service/AppService.ts b/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-service/AppService.ts index 6cce0fbb..7b5561cd 100644 --- a/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-service/AppService.ts +++ b/plugin/tegg/test/fixtures/apps/wrong-order-app/modules/multi-module-service/AppService.ts @@ -1,7 +1,7 @@ -import { AccessLevel, ContextProto, Inject } from '@eggjs/tegg'; +import { AccessLevel, SingletonProto, Inject } from '@eggjs/tegg'; import AppRepo from '../multi-module-repo/AppRepo'; -@ContextProto({ +@SingletonProto({ accessLevel: AccessLevel.PUBLIC, }) export default class AppService { diff --git a/plugin/tegg/typings/index.d.ts b/plugin/tegg/typings/index.d.ts index a0d7ac7c..be54fe34 100644 --- a/plugin/tegg/typings/index.d.ts +++ b/plugin/tegg/typings/index.d.ts @@ -1,6 +1,6 @@ -import { Application, Context } from 'egg'; +import { AsyncLocalStorage } from 'async_hooks'; +import { Context } from 'egg'; import '@eggjs/tegg-config'; -import { ModuleHandler } from '../lib/ModuleHandler'; import { EggPrototypeCreatorFactory } from '@eggjs/tegg-metadata'; import { EggPrototypeFactory, @@ -16,9 +16,12 @@ import { EggObjectLifecycleUtil, AbstractEggContext, EggObjectFactory, + EggContext, } from '@eggjs/tegg-runtime'; import { LoaderFactory } from '@eggjs/tegg-loader'; import { IdenticalUtil, EggProtoImplClass } from '@eggjs/tegg'; +import { ModuleHandler } from '../lib/ModuleHandler'; +import { EggContextHandler } from '../lib/EggContextHandler'; declare module 'egg' { export interface EggModule { @@ -46,10 +49,12 @@ declare module 'egg' { eggPrototypeLifecycleUtil: typeof EggPrototypeLifecycleUtil; eggContextLifecycleUtil: typeof EggContextLifecycleUtil; eggObjectLifecycleUtil: typeof EggObjectLifecycleUtil; - + teggContext: EggContext; moduleHandler: ModuleHandler; + eggContextHandler: EggContextHandler; mockModuleContext(data?: any): Promise; + mockModuleContextScope(fn: (ctx: Context) => Promise, data?: any): Promise; destroyModuleContext(context: Context): Promise; // 兼容现有 module 的定义 module: EggModule & EggApplicationModule; diff --git a/standalone/standalone/CHANGELOG.md b/standalone/standalone/CHANGELOG.md index d717bd80..cefe7332 100644 --- a/standalone/standalone/CHANGELOG.md +++ b/standalone/standalone/CHANGELOG.md @@ -3,6 +3,309 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [3.23.0](https://github.com/eggjs/tegg/compare/v3.22.0...v3.23.0) (2023-09-20) + + +### Features + +* add className property to EggPrototypeInfo ([#158](https://github.com/eggjs/tegg/issues/158)) ([bddac97](https://github.com/eggjs/tegg/commit/bddac97a9f575c9f13b794246a7e8346c58d1a09)) + + + + + +# [3.20.0](https://github.com/eggjs/tegg/compare/v3.19.0...v3.20.0) (2023-09-07) + + +### Features + +* support load module config with env ([#151](https://github.com/eggjs/tegg/issues/151)) ([c087226](https://github.com/eggjs/tegg/commit/c087226bd7764242fadce5622fccd9e9fee56322)) + + + + + +# [3.19.0](https://github.com/eggjs/tegg/compare/v3.18.1...v3.19.0) (2023-08-31) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.18.1](https://github.com/eggjs/tegg/compare/v3.18.0...v3.18.1) (2023-08-29) + + +### Bug Fixes + +* fix use MultiInstanceProto from other modules ([#147](https://github.com/eggjs/tegg/issues/147)) ([b71af60](https://github.com/eggjs/tegg/commit/b71af60ce6d1da0d778f5e712633b8c15052bd70)) + + + + + +# [3.18.0](https://github.com/eggjs/tegg/compare/v3.17.0...v3.18.0) (2023-08-29) + + +### Features + +* add aop runtime/dynamic inject runtime for standalone ([#149](https://github.com/eggjs/tegg/issues/149)) ([6091fc6](https://github.com/eggjs/tegg/commit/6091fc6be885976d72a6920d37ec685927b63d5d)) + + + + + +# [3.17.0](https://github.com/eggjs/tegg/compare/v3.16.0...v3.17.0) (2023-08-24) + + +### Features + +* impl MultiInstanceProto ([#145](https://github.com/eggjs/tegg/issues/145)) ([12fd5cf](https://github.com/eggjs/tegg/commit/12fd5cff4004578bcc737dcdf4f7e9d1159f5633)) + + + + + +# [3.16.0](https://github.com/eggjs/tegg/compare/v3.15.0...v3.16.0) (2023-08-24) + + +### Features + +* export EggModuleLoader in standalone ([#146](https://github.com/eggjs/tegg/issues/146)) ([9d1da9a](https://github.com/eggjs/tegg/commit/9d1da9a87dbd486930adc50cd43020c2fb478230)) +* implement RuntimeConfig ([#144](https://github.com/eggjs/tegg/issues/144)) ([0862655](https://github.com/eggjs/tegg/commit/0862655846f6765349d406ee697c036cec2a37bd)) + + + + + +## [3.14.3](https://github.com/eggjs/tegg/compare/v3.14.2...v3.14.3) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.14.2](https://github.com/eggjs/tegg/compare/v3.14.1...v3.14.2) (2023-08-14) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.14.1](https://github.com/eggjs/tegg/compare/v3.14.0...v3.14.1) (2023-08-11) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.14.0](https://github.com/eggjs/tegg/compare/v3.13.0...v3.14.0) (2023-07-31) + + +### Features + +* impl ModuleConfigs for standalone ([#136](https://github.com/eggjs/tegg/issues/136)) ([7227492](https://github.com/eggjs/tegg/commit/7227492295b9c84e3660bfc006ca96e7a9652a25)) + + + + + +# [3.13.0](https://github.com/eggjs/tegg/compare/v3.12.0...v3.13.0) (2023-07-25) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.12.0](https://github.com/eggjs/tegg/compare/v3.11.1...v3.12.0) (2023-07-13) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.11.1](https://github.com/eggjs/tegg/compare/v3.11.0...v3.11.1) (2023-06-29) + + +### Bug Fixes + +* export StandaloneInnerObject ([#131](https://github.com/eggjs/tegg/issues/131)) ([e4b87e0](https://github.com/eggjs/tegg/commit/e4b87e0a48e3232adaf43bad75f44d0ae775c984)) + + + + + +# [3.11.0](https://github.com/eggjs/tegg/compare/v3.10.0...v3.11.0) (2023-06-29) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.10.0](https://github.com/eggjs/tegg/compare/v3.9.0...v3.10.0) (2023-06-28) + + +### Features + +* standalone Runner run support ctx ([#126](https://github.com/eggjs/tegg/issues/126)) ([0788c7d](https://github.com/eggjs/tegg/commit/0788c7dfb57f96c55e94cc6692c0b6e9ac1ee03c)) + + + + + +# [3.9.0](https://github.com/eggjs/tegg/compare/v3.8.0...v3.9.0) (2023-06-20) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.8.0](https://github.com/eggjs/tegg/compare/v3.7.0...v3.8.0) (2023-05-30) + + +### Features + +* impl EggObjectLifecycle hook in decorator ([#119](https://github.com/eggjs/tegg/issues/119)) ([cced8a2](https://github.com/eggjs/tegg/commit/cced8a2e009c33d5040fa21d00409fddef471b0e)) + + + + + +# [3.7.0](https://github.com/eggjs/tegg/compare/v3.6.3...v3.7.0) (2023-04-03) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.6.3](https://github.com/eggjs/tegg/compare/v3.6.2...v3.6.3) (2023-03-02) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.6.0](https://github.com/eggjs/tegg/compare/v3.5.2...v3.6.0) (2023-02-13) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.5.2](https://github.com/eggjs/tegg/compare/v3.5.1...v3.5.2) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.5.0](https://github.com/eggjs/tegg/compare/v3.4.1...v3.5.0) (2023-02-10) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.4.1](https://github.com/eggjs/tegg/compare/v3.4.0...v3.4.1) (2023-02-02) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.4.0](https://github.com/eggjs/tegg/compare/v3.3.4...v3.4.0) (2023-02-01) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.3.1](https://github.com/eggjs/tegg/compare/v3.3.0...v3.3.1) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.3.0](https://github.com/eggjs/tegg/compare/v3.2.4...v3.3.0) (2023-01-28) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.2.3](https://github.com/eggjs/tegg/compare/v3.2.2...v3.2.3) (2023-01-16) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.2.2](https://github.com/eggjs/tegg/compare/v3.2.1...v3.2.2) (2023-01-06) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +## [3.2.1](https://github.com/eggjs/tegg/compare/v3.2.0...v3.2.1) (2022-12-28) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.1.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.1.0) (2022-12-27) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) + + + + + +# [3.0.0](https://github.com/eggjs/tegg/compare/v3.0.0-alpha.0...v3.0.0) (2022-12-26) + +**Note:** Version bump only for package @eggjs/tegg-standalone + + + + + +# [3.0.0-alpha.0](https://github.com/eggjs/tegg/compare/v1.3.0...v3.0.0-alpha.0) (2022-12-22) + + +### Features + +* **break:** use async local storage ([#69](https://github.com/eggjs/tegg/issues/69)) ([772aeb9](https://github.com/eggjs/tegg/commit/772aeb9412c6d7cd23560230b441161ba28ffa0e)) +* standalone support context ([#65](https://github.com/eggjs/tegg/issues/65)) ([b35dc2d](https://github.com/eggjs/tegg/commit/b35dc2d40fff1331145abd3f04917dc64f80010b)) + + + + + ## [1.3.6](https://github.com/eggjs/tegg/compare/@eggjs/tegg-standalone@1.3.5...@eggjs/tegg-standalone@1.3.6) (2022-09-04) **Note:** Version bump only for package @eggjs/tegg-standalone diff --git a/standalone/standalone/README.md b/standalone/standalone/README.md index 98ece65c..5e9c9ed3 100644 --- a/standalone/standalone/README.md +++ b/standalone/standalone/README.md @@ -42,3 +42,44 @@ await main(cwd, { }, }); ``` + +### 配置 + +module 支持通过 module.yml 来定义配置,在代码中可以通过注入 moduleConfigs 获取全局配置,通过注入 moduleConfig 来获取单 module 的配置。 + +```yaml +# module.yml +# module 根目录中 + +features: + dynamic: + foo: 'bar' +``` + +```ts +@ContextProto() +export class Foo { + // 获取全局配置, 通过 get 方法来获取特定 module 的配置 + @Inject() + moduleConfigs: ModuleConfigs; + + // 注入当前 module 的配置 + @Inject() + moduleConfig: ModuleConfig; + + // 注入 "bar" module 的配置 + @Inject({ + name: 'moduleConfig', + }) + @ConfigSourceQualifier('bar') + barModuleConfig: ModuleConfig; + + async main() { + return { + configs: this.moduleConfigs, + foo: this.moduleConfig, + bar: this.barModuleConfig, + }; + } +} +``` diff --git a/standalone/standalone/index.ts b/standalone/standalone/index.ts index 07ac1976..0ff5948c 100644 --- a/standalone/standalone/index.ts +++ b/standalone/standalone/index.ts @@ -1,2 +1,6 @@ +export * from './src/EggModuleLoader'; export * from './src/Runner'; export * from './src/main'; +export * from './src/StandaloneInnerObjectProto'; +export * from './src/StandaloneContext'; +export * from './src/StandaloneInnerObject'; diff --git a/standalone/standalone/package.json b/standalone/standalone/package.json index a8c95769..6ef727dd 100644 --- a/standalone/standalone/package.json +++ b/standalone/standalone/package.json @@ -1,7 +1,7 @@ { "name": "@eggjs/tegg-standalone", "description": "tegg standalone", - "version": "1.4.0", + "version": "3.23.0", "keywords": [ "egg", "typescript", @@ -17,11 +17,11 @@ ], "typings": "dist/index.d.ts", "scripts": { + "test": "cross-env NODE_ENV=test NODE_OPTIONS='--no-deprecation' mocha", "clean": "tsc -b --clean", "tsc": "npm run clean && tsc -p ./tsconfig.json", "tsc:pub": "npm run clean && tsc -p ./tsconfig.pub.json", - "prepublishOnly": "npm run tsc:pub", - "autod": "autod" + "prepublishOnly": "npm run tsc:pub" }, "homepage": "https://github.com/eggjs/tegg", "bugs": { @@ -38,17 +38,26 @@ "author": "killagu ", "license": "MIT", "dependencies": { - "@eggjs/egg-module-common": "^1.0.0", - "@eggjs/tegg": "^1.5.0", - "@eggjs/tegg-background-task": "^1.4.0", - "@eggjs/tegg-common-util": "^1.2.0", - "@eggjs/tegg-dynamic-inject-runtime": "^1.4.0", - "@eggjs/tegg-lifecycle": "^1.0.0", - "@eggjs/tegg-loader": "^1.4.0", - "@eggjs/tegg-metadata": "^1.4.0", - "@eggjs/tegg-runtime": "^1.4.0" + "@eggjs/egg-module-common": "^3.23.0", + "@eggjs/tegg": "^3.23.0", + "@eggjs/tegg-aop-runtime": "^3.23.0", + "@eggjs/tegg-background-task": "^3.23.0", + "@eggjs/tegg-common-util": "^3.23.0", + "@eggjs/tegg-dynamic-inject-runtime": "^3.23.0", + "@eggjs/tegg-lifecycle": "^3.23.0", + "@eggjs/tegg-loader": "^3.23.0", + "@eggjs/tegg-metadata": "^3.23.0", + "@eggjs/tegg-runtime": "^3.23.0" }, "publishConfig": { "access": "public" + }, + "devDependencies": { + "@types/mocha": "^10.0.1", + "@types/node": "^20.2.4", + "cross-env": "^7.0.3", + "mocha": "^10.2.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" } } diff --git a/standalone/standalone/src/ConfigSource.ts b/standalone/standalone/src/ConfigSource.ts new file mode 100644 index 00000000..b09481cf --- /dev/null +++ b/standalone/standalone/src/ConfigSource.ts @@ -0,0 +1,9 @@ +import { QualifierUtil, EggProtoImplClass } from '@eggjs/tegg'; + +export const ConfigSourceQualifierAttribute = Symbol.for('Qualifier.ConfigSource'); + +export function ConfigSourceQualifier(moduleName: string) { + return function(target: any, propertyKey: PropertyKey) { + QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, ConfigSourceQualifierAttribute, moduleName); + }; +} diff --git a/standalone/standalone/src/ConfigSourceLoadUnitHook.ts b/standalone/standalone/src/ConfigSourceLoadUnitHook.ts new file mode 100644 index 00000000..ca83049f --- /dev/null +++ b/standalone/standalone/src/ConfigSourceLoadUnitHook.ts @@ -0,0 +1,24 @@ +import { LoadUnit, LoadUnitLifecycleContext } from '@eggjs/tegg-metadata'; +import { + LifecycleHook, + PrototypeUtil, QualifierUtil, +} from '@eggjs/tegg'; +import { ConfigSourceQualifier, ConfigSourceQualifierAttribute } from './ConfigSource'; + +/** + * Hook for inject moduleConfig. + * Add default qualifier value is current module name. + */ +export class ConfigSourceLoadUnitHook implements LifecycleHook { + async preCreate(ctx: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { + const classList = ctx.loader.load(); + for (const clazz of classList) { + const injectObjects = PrototypeUtil.getInjectObjects(clazz); + const moduleConfigObject = injectObjects.find(t => t.objName === 'moduleConfig'); + const configSourceQualifier = QualifierUtil.getProperQualifier(clazz, 'moduleConfig', ConfigSourceQualifierAttribute); + if (moduleConfigObject && !configSourceQualifier) { + ConfigSourceQualifier(loadUnit.name)(clazz.prototype, moduleConfigObject.refName); + } + } + } +} diff --git a/standalone/standalone/src/EggModuleLoader.ts b/standalone/standalone/src/EggModuleLoader.ts index 29e71121..c35b8b88 100644 --- a/standalone/standalone/src/EggModuleLoader.ts +++ b/standalone/standalone/src/EggModuleLoader.ts @@ -1,13 +1,7 @@ import { EggLoadUnitType, Loader, LoadUnit, LoadUnitFactory } from '@eggjs/tegg-metadata'; import { LoaderFactory } from '@eggjs/tegg-loader'; -import { StandaloneGraph, ModuleNode } from './StandaloneGraph'; -import { - InitTypeQualifierAttribute, - LoadUnitNameQualifierAttribute, - PrototypeUtil, - QualifierUtil, -} from '@eggjs/tegg'; -import { ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util'; +import { AppGraph, ModuleNode } from '@eggjs/tegg/helper'; +import { ModuleReference } from '@eggjs/tegg-common-util'; export class EggModuleLoader { private moduleReferences: readonly ModuleReference[]; @@ -17,7 +11,7 @@ export class EggModuleLoader { } private buildAppGraph(loaderCache: Map) { - const appGraph = new StandaloneGraph(); + const appGraph = new AppGraph(); for (const moduleConfig of this.moduleReferences) { const modulePath = moduleConfig.path; const moduleNode = new ModuleNode(moduleConfig); @@ -25,19 +19,6 @@ export class EggModuleLoader { loaderCache.set(modulePath, loader); const clazzList = loader.load(); for (const clazz of clazzList) { - // TODO copy from ModuleLoadUnit, duplicate code - const moduleName = ModuleConfigUtil.readModuleNameSync(modulePath); - const property = PrototypeUtil.getProperty(clazz)!; - const defaultQualifier = [{ - attribute: InitTypeQualifierAttribute, - value: property.initType, - }, { - attribute: LoadUnitNameQualifierAttribute, - value: moduleName, - }]; - defaultQualifier.forEach(qualifier => { - QualifierUtil.addProtoQualifier(clazz, qualifier.attribute, qualifier.value); - }); moduleNode.addClazz(clazz); } appGraph.addNode(moduleNode); diff --git a/standalone/standalone/src/LoadUnitInnerClassHook.ts b/standalone/standalone/src/LoadUnitInnerClassHook.ts new file mode 100644 index 00000000..6fddd4a5 --- /dev/null +++ b/standalone/standalone/src/LoadUnitInnerClassHook.ts @@ -0,0 +1,26 @@ +import { LifecycleHook } from '@eggjs/tegg-lifecycle'; +import { + EggPrototypeFactory, + LoadUnit, + LoadUnitLifecycleContext, + EggPrototypeCreatorFactory, +} from '@eggjs/tegg-metadata'; +import { EggProtoImplClass } from '@eggjs/tegg'; +import { EggObjectFactory } from '@eggjs/tegg-dynamic-inject-runtime'; + +const INNER_CLASS_LIST = [ + EggObjectFactory, +]; + +export class LoadUnitInnerClassHook implements LifecycleHook { + async postCreate(_: LoadUnitLifecycleContext, loadUnit: LoadUnit): Promise { + if (loadUnit.type === 'StandaloneLoadUnitType') { + for (const clazz of INNER_CLASS_LIST) { + const protos = await EggPrototypeCreatorFactory.createProto(clazz as EggProtoImplClass, loadUnit); + for (const proto of protos) { + EggPrototypeFactory.instance.registerPrototype(proto, loadUnit); + } + } + } + } +} diff --git a/standalone/standalone/src/ModuleConfig.ts b/standalone/standalone/src/ModuleConfig.ts new file mode 100644 index 00000000..be97dc10 --- /dev/null +++ b/standalone/standalone/src/ModuleConfig.ts @@ -0,0 +1,8 @@ +import 'egg'; + +// for declare merging +declare module 'egg' { + export interface ModuleConfig { + // ... + } +} diff --git a/standalone/standalone/src/ModuleConfigs.ts b/standalone/standalone/src/ModuleConfigs.ts new file mode 100644 index 00000000..61693055 --- /dev/null +++ b/standalone/standalone/src/ModuleConfigs.ts @@ -0,0 +1,17 @@ +import { ModuleConfig } from 'egg'; +import { ModuleReference } from '@eggjs/tegg-common-util'; + +export interface ModuleConfigHolder { + name: string; + config: ModuleConfig; + reference: ModuleReference; +} + +export class ModuleConfigs { + constructor(readonly inner: Record) { + } + + get(moduleName: string): ModuleConfig | undefined { + return this.inner[moduleName]?.config; + } +} diff --git a/standalone/standalone/src/Runner.ts b/standalone/standalone/src/Runner.ts index b8c11539..0707eafd 100644 --- a/standalone/standalone/src/Runner.ts +++ b/standalone/standalone/src/Runner.ts @@ -1,77 +1,171 @@ -import { ModuleConfig, ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util'; -import { EggPrototype, LoadUnit, LoadUnitFactory } from '@eggjs/tegg-metadata'; -import { EggContainerFactory, LoadUnitInstance, LoadUnitInstanceFactory, ModuleLoadUnitInstance } from '@eggjs/tegg-runtime'; -import { EggModuleLoader } from './EggModuleLoader'; +import { ModuleConfigUtil, ModuleReference, ReadModuleReferenceOptions, RuntimeConfig } from '@eggjs/tegg-common-util'; +import { + EggPrototype, EggPrototypeLifecycleUtil, + LoadUnit, + LoadUnitFactory, + LoadUnitLifecycleUtil, LoadUnitMultiInstanceProtoHook, +} from '@eggjs/tegg-metadata'; +import { + ContextHandler, + EggContainerFactory, EggContext, EggObjectLifecycleUtil, + LoadUnitInstance, + LoadUnitInstanceFactory, + ModuleLoadUnitInstance, +} from '@eggjs/tegg-runtime'; import { EggProtoImplClass, PrototypeUtil } from '@eggjs/tegg'; import { StandaloneUtil, MainRunner } from '@eggjs/tegg/standalone'; -import { StandaloneLoadUnit, StandaloneLoadUnitType } from './StandaloneLoadUnit'; +import { CrosscutAdviceFactory } from '@eggjs/tegg/aop'; +import { EggObjectAopHook, EggPrototypeCrossCutHook, LoadUnitAopHook } from '@eggjs/tegg-aop-runtime'; + +import { EggModuleLoader } from './EggModuleLoader'; +import { InnerObject, StandaloneLoadUnit, StandaloneLoadUnitType } from './StandaloneLoadUnit'; +import { StandaloneContext } from './StandaloneContext'; +import { StandaloneContextHandler } from './StandaloneContextHandler'; +import { ModuleConfigHolder, ModuleConfigs } from './ModuleConfigs'; +import { ConfigSourceQualifierAttribute } from './ConfigSource'; +import { ConfigSourceLoadUnitHook } from './ConfigSourceLoadUnitHook'; +import { LoadUnitInnerClassHook } from './LoadUnitInnerClassHook'; -export interface ModuleConfigHolder { - name: string; - config: ModuleConfig; - reference: ModuleReference; +export interface ModuleDependency extends ReadModuleReferenceOptions { + baseDir: string; } export interface RunnerOptions { - innerObjects: Record; + /** + * @deprecated + * use inner object handlers instead + */ + innerObjects?: Record; + env?: string; + name?: string; + innerObjectHandlers?: Record; + dependencies?: (string | ModuleDependency)[]; } export class Runner { readonly cwd: string; readonly moduleReferences: readonly ModuleReference[]; readonly moduleConfigs: Record; + readonly env?: string; + readonly name?: string; private loadUnitLoader: EggModuleLoader; + private runnerProto: EggPrototype; + private configSourceEggPrototypeHook: ConfigSourceLoadUnitHook; + private loadUnitMultiInstanceProtoHook: LoadUnitMultiInstanceProtoHook; + + private readonly loadUnitInnerClassHook: LoadUnitInnerClassHook; + private readonly crosscutAdviceFactory: CrosscutAdviceFactory; + private readonly loadUnitAopHook: LoadUnitAopHook; + private readonly eggPrototypeCrossCutHook: EggPrototypeCrossCutHook; + private readonly eggObjectAopHook: EggObjectAopHook; loadUnits: LoadUnit[] = []; loadUnitInstances: LoadUnitInstance[] = []; - innerObjects: Record | undefined; + innerObjects: Record; constructor(cwd: string, options?: RunnerOptions) { this.cwd = cwd; - this.innerObjects = options?.innerObjects; - this.moduleReferences = ModuleConfigUtil.readModuleReference(this.cwd); + this.env = options?.env; + this.name = options?.name; + const moduleDirs = (options?.dependencies || []).concat(this.cwd); + this.moduleReferences = moduleDirs.reduce((list, baseDir) => { + const module = typeof baseDir === 'string' ? { baseDir } : baseDir; + return list.concat(...ModuleConfigUtil.readModuleReference(module.baseDir, module)); + }, [] as readonly ModuleReference[]); this.moduleConfigs = {}; + this.innerObjects = { + moduleConfigs: [{ + obj: new ModuleConfigs(this.moduleConfigs), + }], + moduleConfig: [], + }; + + const runtimeConfig: Partial = { + baseDir: this.cwd, + name: this.name, + env: this.env, + }; + // Inject runtimeConfig + this.innerObjects.runtimeConfig = [{ + obj: runtimeConfig, + }]; + for (const reference of this.moduleReferences) { const absoluteRef = { path: ModuleConfigUtil.resolveModuleDir(reference.path, this.cwd), + name: reference.name, }; const moduleName = ModuleConfigUtil.readModuleNameSync(absoluteRef.path); this.moduleConfigs[moduleName] = { name: moduleName, reference: absoluteRef, - config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path) || {}, + config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path, undefined, this.env) || {}, }; } + for (const moduleConfig of Object.values(this.moduleConfigs)) { + this.innerObjects.moduleConfig.push({ + obj: moduleConfig.config, + qualifiers: [{ + attribute: ConfigSourceQualifierAttribute, + value: moduleConfig.name, + }], + }); + } + if (options?.innerObjects) { + for (const [ name, obj ] of Object.entries(options.innerObjects)) { + this.innerObjects[name] = [{ + obj, + }]; + } + } else if (options?.innerObjectHandlers) { + Object.assign(this.innerObjects, options.innerObjectHandlers); + } this.loadUnitLoader = new EggModuleLoader(this.moduleReferences); + const configSourceEggPrototypeHook = new ConfigSourceLoadUnitHook(); + LoadUnitLifecycleUtil.registerLifecycle(configSourceEggPrototypeHook); + + this.loadUnitInnerClassHook = new LoadUnitInnerClassHook(); + LoadUnitLifecycleUtil.registerLifecycle(this.loadUnitInnerClassHook); + + // TODO refactor with egg module + // aop runtime + this.crosscutAdviceFactory = new CrosscutAdviceFactory(); + this.loadUnitAopHook = new LoadUnitAopHook(this.crosscutAdviceFactory); + this.eggPrototypeCrossCutHook = new EggPrototypeCrossCutHook(this.crosscutAdviceFactory); + this.eggObjectAopHook = new EggObjectAopHook(); + + EggPrototypeLifecycleUtil.registerLifecycle(this.eggPrototypeCrossCutHook); + LoadUnitLifecycleUtil.registerLifecycle(this.loadUnitAopHook); + EggObjectLifecycleUtil.registerLifecycle(this.eggObjectAopHook); + + this.loadUnitMultiInstanceProtoHook = new LoadUnitMultiInstanceProtoHook(); + LoadUnitLifecycleUtil.registerLifecycle(this.loadUnitMultiInstanceProtoHook); } - async init() { - this.loadUnits = []; - if (this.innerObjects) { - LoadUnitFactory.registerLoadUnitCreator(StandaloneLoadUnitType, () => { - return new StandaloneLoadUnit(this.innerObjects!); - }); - LoadUnitInstanceFactory.registerLoadUnitInstanceClass(StandaloneLoadUnitType, ModuleLoadUnitInstance.createModuleLoadUnitInstance); - const standaloneLoadUnit = await LoadUnitFactory.createLoadUnit('MockStandaloneLoadUnitPath', StandaloneLoadUnitType, { - load(): EggProtoImplClass[] { - return []; - }, - }); - this.loadUnits.push(standaloneLoadUnit); - } + async load() { + StandaloneContextHandler.register(); + LoadUnitFactory.registerLoadUnitCreator(StandaloneLoadUnitType, () => { + return new StandaloneLoadUnit(this.innerObjects); + }); + LoadUnitInstanceFactory.registerLoadUnitInstanceClass(StandaloneLoadUnitType, ModuleLoadUnitInstance.createModuleLoadUnitInstance); + const standaloneLoadUnit = await LoadUnitFactory.createLoadUnit('MockStandaloneLoadUnitPath', StandaloneLoadUnitType, { + load(): EggProtoImplClass[] { + return []; + }, + }); const loadUnits = await this.loadUnitLoader.load(); - this.loadUnits = this.loadUnits.concat(loadUnits); + return [ standaloneLoadUnit, ...loadUnits ]; + } + async init() { + this.loadUnits = await this.load(); const instances: LoadUnitInstance[] = []; for (const loadUnit of this.loadUnits) { const instance = await LoadUnitInstanceFactory.createLoadUnitInstance(loadUnit); instances.push(instance); } this.loadUnitInstances = instances; - } - - async run() { const runnerClass = StandaloneUtil.getMainRunner(); if (!runnerClass) { throw new Error('not found runner class. Do you add @Runner decorator?'); @@ -80,9 +174,29 @@ export class Runner { if (!proto) { throw new Error(`can not get proto for clazz ${runnerClass.name}`); } - const eggObject = await EggContainerFactory.getOrCreateEggObject(proto as EggPrototype); - const runner = eggObject.obj as MainRunner; - return await runner.main(); + this.runnerProto = proto as EggPrototype; + } + + async run(aCtx?: EggContext) { + const lifecycle = {}; + const ctx = aCtx || new StandaloneContext(); + return await ContextHandler.run(ctx, async () => { + if (ctx.init) { + await ctx.init(lifecycle); + } + const eggObject = await EggContainerFactory.getOrCreateEggObject(this.runnerProto); + const runner = eggObject.obj as MainRunner; + try { + return await runner.main(); + } finally { + if (ctx.destroy) { + ctx.destroy(lifecycle).catch(e => { + e.message = `[tegg/standalone] destroy tegg context failed: ${e.message}`; + console.warn(e); + }); + } + } + }); } async destroy() { @@ -96,5 +210,26 @@ export class Runner { await LoadUnitFactory.destroyLoadUnit(loadUnit); } } + if (this.configSourceEggPrototypeHook) { + LoadUnitLifecycleUtil.deleteLifecycle(this.configSourceEggPrototypeHook); + } + + if (this.loadUnitInnerClassHook) { + LoadUnitLifecycleUtil.deleteLifecycle(this.loadUnitInnerClassHook); + } + + if (this.eggPrototypeCrossCutHook) { + EggPrototypeLifecycleUtil.deleteLifecycle(this.eggPrototypeCrossCutHook); + } + if (this.loadUnitAopHook) { + LoadUnitLifecycleUtil.deleteLifecycle(this.loadUnitAopHook); + } + if (this.eggObjectAopHook) { + EggObjectLifecycleUtil.deleteLifecycle(this.eggObjectAopHook); + } + + if (this.loadUnitMultiInstanceProtoHook) { + LoadUnitLifecycleUtil.deleteLifecycle(this.loadUnitMultiInstanceProtoHook); + } } } diff --git a/standalone/standalone/src/StandaloneContext.ts b/standalone/standalone/src/StandaloneContext.ts new file mode 100644 index 00000000..690d4ed5 --- /dev/null +++ b/standalone/standalone/src/StandaloneContext.ts @@ -0,0 +1,11 @@ +import { AbstractEggContext } from '@eggjs/tegg-runtime'; +import { IdenticalUtil } from '@eggjs/tegg-lifecycle'; + +export class StandaloneContext extends AbstractEggContext { + id: string; + + constructor() { + super(); + this.id = IdenticalUtil.createContextId(); + } +} diff --git a/standalone/standalone/src/StandaloneContextHandler.ts b/standalone/standalone/src/StandaloneContextHandler.ts new file mode 100644 index 00000000..bb257df9 --- /dev/null +++ b/standalone/standalone/src/StandaloneContextHandler.ts @@ -0,0 +1,16 @@ +import { AsyncLocalStorage } from 'async_hooks'; +import { ContextHandler, EggContext } from '@eggjs/tegg-runtime'; + +export class StandaloneContextHandler { + static storage = new AsyncLocalStorage(); + + static register() { + ContextHandler.getContextCallback = () => { + return StandaloneContextHandler.storage.getStore(); + }; + + ContextHandler.runInContextCallback = (context, fn) => { + return StandaloneContextHandler.storage.run(context, fn); + }; + } +} diff --git a/standalone/standalone/src/StandaloneGraph.ts b/standalone/standalone/src/StandaloneGraph.ts deleted file mode 100644 index be0f86d2..00000000 --- a/standalone/standalone/src/StandaloneGraph.ts +++ /dev/null @@ -1,149 +0,0 @@ -import assert from 'assert'; -import path from 'path'; -import { Graph, GraphNode, GraphNodeObj } from '@eggjs/tegg-common-util'; -import { - EggProtoImplClass, - EggPrototypeName, - INIT_TYPE_TRY_ORDER, - InitTypeQualifierAttribute, - LoadUnitNameQualifierAttribute, - PrototypeUtil, - QualifierInfo, - QualifierUtil, - AccessLevel, -} from '@eggjs/tegg'; - -export interface ModuleConfig { - path: string; -} - -export class ModuleNode implements GraphNodeObj { - readonly id: string; - readonly name: string; - readonly moduleConfig: ModuleConfig; - private clazzList: EggProtoImplClass[]; - - // TODO refactor to ModuleUtil - static readModuleName(modulePath: string): string { - const pkgPath = path.join(modulePath, 'package.json'); - // eslint-disable-next-line @typescript-eslint/no-var-requires - const pkg = require(pkgPath); - assert(pkg.eggModule, `module config not found in package ${pkgPath}`); - const { name } = pkg.eggModule; - return name; - } - - constructor(moduleConfig: ModuleConfig) { - this.moduleConfig = moduleConfig; - this.id = moduleConfig.path; - this.name = ModuleNode.readModuleName(moduleConfig.path); - this.clazzList = []; - } - - addClazz(clazz: EggProtoImplClass) { - this.clazzList.push(clazz); - } - - getClazzList(): readonly EggProtoImplClass[] { - return this.clazzList; - } - - getPublicClazzList(): readonly EggProtoImplClass[] { - return this.clazzList.filter(clazz => PrototypeUtil.getProperty(clazz)?.accessLevel === AccessLevel.PUBLIC); - } - - toString() { - return `${this.name}@${this.moduleConfig.path}`; - } - - verifyQualifiers(clazz: EggProtoImplClass, qualifiers: QualifierInfo[]): boolean { - const clazzQualifiers = QualifierUtil.getProtoQualifiers(clazz); - for (const qualifier of qualifiers) { - if (!this.verifyQualifier(clazzQualifiers, qualifier)) { - return false; - } - } - return true; - } - - verifyQualifier(clazzQualifiers: QualifierInfo[], qualifier: QualifierInfo) { - const selfQualifiers = clazzQualifiers.find(t => t.attribute === qualifier.attribute); - return selfQualifiers?.value === qualifier.value; - } - - findImplementClazzList(objName: EggPrototypeName, qualifiers: QualifierInfo[], innerFind: boolean): EggProtoImplClass[] { - const moduleQualifier = qualifiers.find(t => t.attribute === LoadUnitNameQualifierAttribute); - if (moduleQualifier && moduleQualifier.value !== this.name) { - return []; - } - const clazzList = innerFind ? this.clazzList : this.getPublicClazzList(); - const implList = clazzList - .filter(clazz => PrototypeUtil.getProperty(clazz)?.name === objName) - .filter(clazz => this.verifyQualifiers(clazz, qualifiers)); - if (implList.length === 1) { - return implList; - } - const initTypeQualifiers = INIT_TYPE_TRY_ORDER.map(type => ({ - attribute: InitTypeQualifierAttribute, - value: type, - })); - for (const initTypeQualifier of initTypeQualifiers) { - const initTypeList = implList.filter(clazz => this.verifyQualifiers(clazz, [ initTypeQualifier ])); - if (initTypeList.length === 1) { - return initTypeList; - } - } - return implList; - } -} - -export class StandaloneGraph { - private graph: Graph; - moduleConfigList: Array; - - constructor() { - this.graph = new Graph(); - } - - addNode(moduleNode: ModuleNode) { - if (!this.graph.addVertex(new GraphNode(moduleNode))) { - throw new Error(`duplicate module: ${moduleNode}`); - } - } - - private findDependencyModule(objName: EggPrototypeName, properqualifiers: QualifierInfo[], protoQualifiers: QualifierInfo[]): Array> { - const nodes: Array> = Array.from(this.graph.nodes.values()); - const hostModuleName = protoQualifiers.find(t => t.attribute === LoadUnitNameQualifierAttribute)?.value; - return nodes.filter(node => { - // private 的类只能在当前 module 中被找到 - const clazzList = node.val.findImplementClazzList(objName, properqualifiers, hostModuleName === node.val.name); - return clazzList.length; - }); - } - - build() { - for (const node of this.graph.nodes.values()) { - for (const clazz of node.val.getClazzList()) { - const injectObjects = PrototypeUtil.getInjectObjects(clazz); - for (const injectObject of injectObjects) { - const properqualifiers = QualifierUtil.getProperQualifiers(clazz, injectObject.refName); - const protoQualifiers = QualifierUtil.getProtoQualifiers(clazz); - const dependencyModules = this.findDependencyModule(injectObject.objName, properqualifiers, protoQualifiers); - for (const moduleNode of dependencyModules) { - if (node !== moduleNode) { - this.graph.addEdge(node, moduleNode); - } - } - } - } - } - } - - sort() { - const loopPath = this.graph.loopPath(); - if (loopPath) { - throw new Error('module has recursive deps: ' + loopPath); - } - this.moduleConfigList = this.graph.sort().map(t => t.val.moduleConfig); - } -} diff --git a/standalone/standalone/src/StandaloneInnerObjectProto.ts b/standalone/standalone/src/StandaloneInnerObjectProto.ts index 6403c473..fcfe48b6 100644 --- a/standalone/standalone/src/StandaloneInnerObjectProto.ts +++ b/standalone/standalone/src/StandaloneInnerObjectProto.ts @@ -5,10 +5,9 @@ import { MetadataUtil, ObjectInitTypeLike, QualifierInfo, - PrototypeUtil, QualifierUtil, Id, - IdenticalUtil, + IdenticalUtil, QualifierValue, } from '@eggjs/tegg'; import { EggPrototype, @@ -67,13 +66,16 @@ export class StandaloneInnerObjectProto implements EggPrototype { return MetadataUtil.getMetaData(metadataKey, this.clazz); } + getQualifier(attribute: string): QualifierValue | undefined { + return this.qualifiers.find(t => t.attribute === attribute)?.value; + } + static create(ctx: EggPrototypeLifecycleContext): EggPrototype { const { clazz, loadUnit } = ctx; - const property = PrototypeUtil.getProperty(clazz)!; - const name = property.name; + const name = ctx.prototypeInfo.name; const id = IdenticalUtil.createProtoId(loadUnit.id, name); const proto = new StandaloneInnerObjectProto( - id, name, clazz, property.initType, loadUnit.id, QualifierUtil.getProtoQualifiers(clazz), + id, name, clazz, ctx.prototypeInfo.initType, loadUnit.id, QualifierUtil.getProtoQualifiers(clazz), ); return proto; } diff --git a/standalone/standalone/src/StandaloneLoadUnit.ts b/standalone/standalone/src/StandaloneLoadUnit.ts index 277467cc..5e054b8a 100644 --- a/standalone/standalone/src/StandaloneLoadUnit.ts +++ b/standalone/standalone/src/StandaloneLoadUnit.ts @@ -6,34 +6,40 @@ import { StandaloneInnerObjectProto } from './StandaloneInnerObjectProto'; export const StandaloneLoadUnitType = 'StandaloneLoadUnitType'; +export interface InnerObject { + obj: object, + qualifiers?: QualifierInfo[], +} + export class StandaloneLoadUnit implements LoadUnit { readonly id: string = 'StandaloneLoadUnit'; readonly name: string = 'StandaloneLoadUnit'; readonly unitPath: string = 'MockStandaloneLoadUnitPath'; readonly type = StandaloneLoadUnitType; - private innerObject: Record; + private innerObject: Record; private protoMap: Map = new Map(); - constructor(innerObject: Record) { + constructor(innerObject: Record) { this.innerObject = innerObject; } async init() { - for (const [ name, obj ] of Object.entries(this.innerObject)) { - const proto = new StandaloneInnerObjectProto( - IdenticalUtil.createProtoId(this.id, name), - name, - (() => obj) as any, - ObjectInitType.SINGLETON, - this.id, - [], - ); - EggPrototypeFactory.instance.registerPrototype(proto, this); + for (const [ name, objs ] of Object.entries(this.innerObject)) { + for (const { obj, qualifiers } of objs) { + const proto = new StandaloneInnerObjectProto( + IdenticalUtil.createProtoId(this.id, name), + name, + (() => obj) as any, + ObjectInitType.SINGLETON, + this.id, + qualifiers || [], + ); + EggPrototypeFactory.instance.registerPrototype(proto, this); + } } } - containPrototype(proto: EggPrototype): boolean { return !!(this.protoMap.get(proto.name)?.find(t => t === proto)); } diff --git a/standalone/standalone/test/fixtures/aop-module/Hello.ts b/standalone/standalone/test/fixtures/aop-module/Hello.ts new file mode 100644 index 00000000..08d16d83 --- /dev/null +++ b/standalone/standalone/test/fixtures/aop-module/Hello.ts @@ -0,0 +1,182 @@ +import { + ContextProto, + Inject, + SingletonProto, +} from '@eggjs/tegg'; +import { + Advice, + AdviceContext, + Crosscut, + IAdvice, + Pointcut, + PointcutType, +} from '@eggjs/tegg/aop'; +import assert from 'assert'; + +export interface CallTraceMsg { + className: string; + methodName: string; + id: number; + name: string; + result?: string; + adviceParams?: any; +} + +@SingletonProto() +export class CallTrace { + msgs: Array = []; + + addMsg(msg: CallTraceMsg) { + this.msgs.push(msg); + } +} + +export const pointcutAdviceParams = { + point: Math.random() + .toString(), + cut: Math.random() + .toString(), +}; + +@Advice() +export class PointcutAdvice implements IAdvice { + @Inject() + callTrace: CallTrace; + + async beforeCall(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); + this.callTrace.addMsg({ + className: PointcutAdvice.name, + methodName: 'beforeCall', + id: ctx.that.id, + name: ctx.args[0], + adviceParams: ctx.adviceParams, + }); + } + + async afterReturn(ctx: AdviceContext, result: any): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); + this.callTrace.addMsg({ + className: PointcutAdvice.name, + methodName: 'afterReturn', + id: ctx.that.id, + name: ctx.args[0], + result, + adviceParams: ctx.adviceParams, + }); + } + + async afterThrow(ctx: AdviceContext, error: Error): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); + this.callTrace.addMsg({ + className: PointcutAdvice.name, + methodName: 'afterThrow', + id: ctx.that.id, + name: ctx.args[0], + result: error.message, + adviceParams: ctx.adviceParams, + }); + } + + async afterFinally(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); + this.callTrace.addMsg({ + className: PointcutAdvice.name, + methodName: 'afterFinally', + id: ctx.that.id, + name: ctx.args[0], + adviceParams: ctx.adviceParams, + }); + } + + async around(ctx: AdviceContext, next: () => Promise): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, pointcutAdviceParams); + ctx.args[0] = `withPointAroundParam(${ctx.args[0]})`; + const result = await next(); + return `withPointAroundResult(${result}${JSON.stringify(pointcutAdviceParams)})`; + } +} + +@ContextProto() +export class Hello { + id = 233; + + @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) + async hello(name: string) { + return `hello ${name}`; + } + + @Pointcut(PointcutAdvice, { adviceParams: pointcutAdviceParams }) + async helloWithException(name: string) { + throw new Error(`ops, exception for ${name}`); + } + +} + +export const crosscutAdviceParams = { + cross: Math.random() + .toString(), + cut: Math.random() + .toString(), +}; + +@Crosscut({ + type: PointcutType.CLASS, + clazz: Hello, + methodName: 'hello', +}, { adviceParams: crosscutAdviceParams }) +@Advice() +export class CrosscutAdvice implements IAdvice { + @Inject() + callTrace: CallTrace; + + async beforeCall(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); + this.callTrace.addMsg({ + className: CrosscutAdvice.name, + methodName: 'beforeCall', + id: ctx.that.id, + name: ctx.args[0], + adviceParams: ctx.adviceParams, + }); + } + + async afterReturn(ctx: AdviceContext, result: any): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); + this.callTrace.addMsg({ + className: CrosscutAdvice.name, + methodName: 'afterReturn', + id: ctx.that.id, + name: ctx.args[0], + result, + adviceParams: ctx.adviceParams, + }); + } + + async afterFinally(ctx: AdviceContext): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); + this.callTrace.addMsg({ + className: CrosscutAdvice.name, + methodName: 'afterFinally', + id: ctx.that.id, + name: ctx.args[0], + adviceParams: ctx.adviceParams, + }); + } + + async around(ctx: AdviceContext, next: () => Promise): Promise { + assert.ok(ctx.adviceParams); + assert.deepStrictEqual(ctx.adviceParams, crosscutAdviceParams); + ctx.args[0] = `withCrosscutAroundParam(${ctx.args[0]})`; + const result = await next(); + return `withCrossAroundResult(${result}${JSON.stringify(ctx.adviceParams)})`; + } +} diff --git a/standalone/standalone/test/fixtures/aop-module/main.ts b/standalone/standalone/test/fixtures/aop-module/main.ts new file mode 100644 index 00000000..11d6fc34 --- /dev/null +++ b/standalone/standalone/test/fixtures/aop-module/main.ts @@ -0,0 +1,15 @@ +import { ContextProto, Inject } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { Hello } from './Hello'; + + +@Runner() +@ContextProto() +export class Foo implements MainRunner { + @Inject() + hello: Hello; + + async main(): Promise { + return await this.hello.hello('aop'); + } +} diff --git a/standalone/standalone/test/fixtures/aop-module/package.json b/standalone/standalone/test/fixtures/aop-module/package.json new file mode 100644 index 00000000..4dcd7bf5 --- /dev/null +++ b/standalone/standalone/test/fixtures/aop-module/package.json @@ -0,0 +1,6 @@ +{ + "name": "aop-module", + "eggModule": { + "name": "aopModule" + } +} diff --git a/standalone/standalone/test/fixtures/custom-context/foo.ts b/standalone/standalone/test/fixtures/custom-context/foo.ts new file mode 100644 index 00000000..7e29b63d --- /dev/null +++ b/standalone/standalone/test/fixtures/custom-context/foo.ts @@ -0,0 +1,17 @@ +import { SingletonProto } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { ContextHandler } from '@eggjs/tegg-runtime'; + +export interface Hello { + hello(): string; +} + +@Runner() +@SingletonProto() +export class Foo implements MainRunner { + + async main(): Promise { + const ctx = ContextHandler.getContext(); + return ctx?.get('foo'); + } +} diff --git a/standalone/standalone/test/fixtures/custom-context/package.json b/standalone/standalone/test/fixtures/custom-context/package.json new file mode 100644 index 00000000..81153947 --- /dev/null +++ b/standalone/standalone/test/fixtures/custom-context/package.json @@ -0,0 +1,6 @@ +{ + "name": "innerobject", + "eggModule": { + "name": "innerobject" + } +} diff --git a/standalone/standalone/test/fixtures/dependency/foo.ts b/standalone/standalone/test/fixtures/dependency/foo.ts new file mode 100644 index 00000000..0f143b38 --- /dev/null +++ b/standalone/standalone/test/fixtures/dependency/foo.ts @@ -0,0 +1,19 @@ +import { ContextProto, Inject } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { Hello } from 'dependency-2/foo'; +import { ConfigSourceQualifier } from '../../../src/ConfigSource'; + +@ContextProto() +@Runner() +export class Foo implements MainRunner { + @Inject() + hello: Hello; + + @Inject() + @ConfigSourceQualifier('dependency2') + moduleConfig: any; + + async main(): Promise { + return this.hello.hello() + JSON.stringify(this.moduleConfig); + } +} diff --git a/standalone/standalone/test/fixtures/dependency/node_modules/dependency-1/package.json b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-1/package.json new file mode 100644 index 00000000..b466485a --- /dev/null +++ b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-1/package.json @@ -0,0 +1,9 @@ +{ + "name": "dependency-1", + "eggModule": { + "name": "dependency-1" + }, + "dependencies": { + "dependency-2": "*" + } +} diff --git a/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/foo.js b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/foo.js new file mode 100644 index 00000000..be45aaf7 --- /dev/null +++ b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/foo.js @@ -0,0 +1,16 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; + +const { SingletonProto, AccessLevel } = require('@eggjs/tegg'); + +class Hello { + hello() { + return 'hello!'; + } +} + +exports.Hello = __decorate([SingletonProto({ accessLevel: AccessLevel.PUBLIC })], Hello); diff --git a/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/module.yml b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/module.yml new file mode 100644 index 00000000..a0046c04 --- /dev/null +++ b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/module.yml @@ -0,0 +1,3 @@ +features: + dynamic: + foo: 'bar' diff --git a/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/package.json b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/package.json new file mode 100644 index 00000000..6937b57d --- /dev/null +++ b/standalone/standalone/test/fixtures/dependency/node_modules/dependency-2/package.json @@ -0,0 +1,6 @@ +{ + "name": "dependency-2", + "eggModule": { + "name": "dependency2" + } +} diff --git a/standalone/standalone/test/fixtures/dependency/package.json b/standalone/standalone/test/fixtures/dependency/package.json new file mode 100644 index 00000000..3f7079f6 --- /dev/null +++ b/standalone/standalone/test/fixtures/dependency/package.json @@ -0,0 +1,6 @@ +{ + "name": "entry", + "eggModule": { + "name": "entry" + } +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/AbstractContextHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/AbstractContextHello.ts new file mode 100644 index 00000000..7f52000d --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/AbstractContextHello.ts @@ -0,0 +1,3 @@ +export abstract class AbstractContextHello { + abstract hello(): string; +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/AbstractSingletonHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/AbstractSingletonHello.ts new file mode 100644 index 00000000..28e17f29 --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/AbstractSingletonHello.ts @@ -0,0 +1,3 @@ +export abstract class AbstractSingletonHello { + abstract hello(): string; +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/FooType.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/FooType.ts new file mode 100644 index 00000000..e5f322e3 --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/FooType.ts @@ -0,0 +1,9 @@ +export enum ContextHelloType { + FOO = 'FOO', + BAR = 'BAR', +} + +export enum SingletonHelloType { + FOO = 'FOO', + BAR = 'BAR', +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/HelloService.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/HelloService.ts new file mode 100644 index 00000000..3f34ec1c --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/HelloService.ts @@ -0,0 +1,21 @@ +import { ContextProto, Inject, EggObjectFactory } from '@eggjs/tegg'; +import { ContextHelloType, SingletonHelloType } from './FooType'; +import { AbstractContextHello } from './AbstractContextHello'; +import { AbstractSingletonHello } from './AbstractSingletonHello'; + +@ContextProto() +export class HelloService { + @Inject() + private readonly eggObjectFactory: EggObjectFactory; + + async hello(): Promise { + const helloImpls = await Promise.all([ + this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.FOO), + this.eggObjectFactory.getEggObject(AbstractContextHello, ContextHelloType.BAR), + this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.FOO), + this.eggObjectFactory.getEggObject(AbstractSingletonHello, SingletonHelloType.BAR), + ]); + const msgs = helloImpls.map(helloImpl => helloImpl.hello()); + return msgs; + } +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/decorator/ContextHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/decorator/ContextHello.ts new file mode 100644 index 00000000..4fea049d --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/decorator/ContextHello.ts @@ -0,0 +1,9 @@ +import { ContextHelloType } from '../FooType'; +import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; +import { AbstractContextHello } from '../AbstractContextHello'; + +export const CONTEXT_HELLO_ATTRIBUTE = 'CONTEXT_HELLO_ATTRIBUTE'; + +export const ContextHello: ImplDecorator = + QualifierImplDecoratorUtil.generatorDecorator(AbstractContextHello, CONTEXT_HELLO_ATTRIBUTE); + diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/decorator/SingletonHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/decorator/SingletonHello.ts new file mode 100644 index 00000000..98a28121 --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/decorator/SingletonHello.ts @@ -0,0 +1,8 @@ +import { SingletonHelloType } from '../FooType'; +import { ImplDecorator, QualifierImplDecoratorUtil } from '@eggjs/tegg'; +import { AbstractSingletonHello } from '../AbstractSingletonHello'; + +export const SINGLETON_HELLO_ATTRIBUTE = 'SINGLETON_HELLO_ATTRIBUTE'; + +export const SingletonHello: ImplDecorator = + QualifierImplDecoratorUtil.generatorDecorator(AbstractSingletonHello, SINGLETON_HELLO_ATTRIBUTE); diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarContextHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarContextHello.ts new file mode 100644 index 00000000..5f3d268c --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarContextHello.ts @@ -0,0 +1,14 @@ +import { ContextProto } from '@eggjs/core-decorator'; +import { ContextHello } from '../decorator/ContextHello'; +import { ContextHelloType } from '../FooType'; +import { AbstractContextHello } from '../AbstractContextHello'; + +@ContextProto() +@ContextHello(ContextHelloType.BAR) +export class BarContextHello extends AbstractContextHello { + id = 0; + + hello(): string { + return `hello, bar(context:${this.id++})`; + } +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarSingletonHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarSingletonHello.ts new file mode 100644 index 00000000..d0ec5e86 --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/BarSingletonHello.ts @@ -0,0 +1,14 @@ +import { SingletonProto } from '@eggjs/core-decorator'; +import { SingletonHelloType } from '../FooType'; +import { SingletonHello } from '../decorator/SingletonHello'; +import { AbstractContextHello } from '../AbstractContextHello'; + +@SingletonProto() +@SingletonHello(SingletonHelloType.BAR) +export class BarSingletonHello extends AbstractContextHello { + id = 0; + + hello(): string { + return `hello, bar(singleton:${this.id++})`; + } +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooContextHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooContextHello.ts new file mode 100644 index 00000000..e6ed0fd9 --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooContextHello.ts @@ -0,0 +1,14 @@ +import { ContextProto } from '@eggjs/core-decorator'; +import { ContextHello } from '../decorator/ContextHello'; +import { ContextHelloType } from '../FooType'; +import { AbstractContextHello } from '../AbstractContextHello'; + +@ContextProto() +@ContextHello(ContextHelloType.FOO) +export class FooContextHello extends AbstractContextHello { + id = 0; + + hello(): string { + return `hello, foo(context:${this.id++})`; + } +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooSingletonHello.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooSingletonHello.ts new file mode 100644 index 00000000..7deb20e2 --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/impl/FooSingletonHello.ts @@ -0,0 +1,14 @@ +import { SingletonProto } from '@eggjs/core-decorator'; +import { SingletonHelloType } from '../FooType'; +import { SingletonHello } from '../decorator/SingletonHello'; +import { AbstractContextHello } from '../AbstractContextHello'; + +@SingletonProto() +@SingletonHello(SingletonHelloType.FOO) +export class FooSingletonHello extends AbstractContextHello { + id = 0; + + hello(): string { + return `hello, foo(singleton:${this.id++})`; + } +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/main.ts b/standalone/standalone/test/fixtures/dynamic-inject-module/main.ts new file mode 100644 index 00000000..39383822 --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/main.ts @@ -0,0 +1,14 @@ +import { ContextProto, Inject } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { HelloService } from './HelloService'; + +@Runner() +@ContextProto() +export class Foo implements MainRunner { + @Inject() + helloService: HelloService; + + async main(): Promise { + return await this.helloService.hello(); + } +} diff --git a/standalone/standalone/test/fixtures/dynamic-inject-module/package.json b/standalone/standalone/test/fixtures/dynamic-inject-module/package.json new file mode 100644 index 00000000..876f583b --- /dev/null +++ b/standalone/standalone/test/fixtures/dynamic-inject-module/package.json @@ -0,0 +1,6 @@ +{ + "name": "dynamic-inject-module", + "eggModule": { + "name": "dynamicInjectModule" + } +} diff --git a/standalone/standalone/test/fixtures/inner-object/foo.ts b/standalone/standalone/test/fixtures/inner-object/foo.ts index 793c8726..61d66e89 100644 --- a/standalone/standalone/test/fixtures/inner-object/foo.ts +++ b/standalone/standalone/test/fixtures/inner-object/foo.ts @@ -14,4 +14,5 @@ export class Foo implements MainRunner { async main(): Promise { return this.hello.hello(); } + } diff --git a/standalone/standalone/test/fixtures/module-with-config/foo.ts b/standalone/standalone/test/fixtures/module-with-config/foo.ts new file mode 100644 index 00000000..79a1b7b3 --- /dev/null +++ b/standalone/standalone/test/fixtures/module-with-config/foo.ts @@ -0,0 +1,28 @@ +import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { ModuleConfigs } from '../../../src/ModuleConfigs'; + +@SingletonProto() +export class Hello { + hello() { + return 'hello!'; + } +} + +@ContextProto() +export class HelloContext { + hello() { + return 'hello from ctx'; + } +} + +@ContextProto() +@Runner() +export class Foo implements MainRunner { + @Inject() + moduleConfigs: ModuleConfigs; + + async main(): Promise { + return this.moduleConfigs.get('simple')!; + } +} diff --git a/standalone/standalone/test/fixtures/module-with-config/module.yml b/standalone/standalone/test/fixtures/module-with-config/module.yml new file mode 100644 index 00000000..a0046c04 --- /dev/null +++ b/standalone/standalone/test/fixtures/module-with-config/module.yml @@ -0,0 +1,3 @@ +features: + dynamic: + foo: 'bar' diff --git a/standalone/standalone/test/fixtures/module-with-config/package.json b/standalone/standalone/test/fixtures/module-with-config/package.json new file mode 100644 index 00000000..1c4562cc --- /dev/null +++ b/standalone/standalone/test/fixtures/module-with-config/package.json @@ -0,0 +1,6 @@ +{ + "name": "simple", + "eggModule": { + "name": "simple" + } +} diff --git a/standalone/standalone/test/fixtures/module-with-env-config/foo.ts b/standalone/standalone/test/fixtures/module-with-env-config/foo.ts new file mode 100644 index 00000000..79a1b7b3 --- /dev/null +++ b/standalone/standalone/test/fixtures/module-with-env-config/foo.ts @@ -0,0 +1,28 @@ +import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { ModuleConfigs } from '../../../src/ModuleConfigs'; + +@SingletonProto() +export class Hello { + hello() { + return 'hello!'; + } +} + +@ContextProto() +export class HelloContext { + hello() { + return 'hello from ctx'; + } +} + +@ContextProto() +@Runner() +export class Foo implements MainRunner { + @Inject() + moduleConfigs: ModuleConfigs; + + async main(): Promise { + return this.moduleConfigs.get('simple')!; + } +} diff --git a/standalone/standalone/test/fixtures/module-with-env-config/module.dev.yml b/standalone/standalone/test/fixtures/module-with-env-config/module.dev.yml new file mode 100644 index 00000000..0d817dc7 --- /dev/null +++ b/standalone/standalone/test/fixtures/module-with-env-config/module.dev.yml @@ -0,0 +1,3 @@ +features: + dynamic: + foo: 'foo' diff --git a/standalone/standalone/test/fixtures/module-with-env-config/module.yml b/standalone/standalone/test/fixtures/module-with-env-config/module.yml new file mode 100644 index 00000000..a0046c04 --- /dev/null +++ b/standalone/standalone/test/fixtures/module-with-env-config/module.yml @@ -0,0 +1,3 @@ +features: + dynamic: + foo: 'bar' diff --git a/standalone/standalone/test/fixtures/module-with-env-config/package.json b/standalone/standalone/test/fixtures/module-with-env-config/package.json new file mode 100644 index 00000000..1c4562cc --- /dev/null +++ b/standalone/standalone/test/fixtures/module-with-env-config/package.json @@ -0,0 +1,6 @@ +{ + "name": "simple", + "eggModule": { + "name": "simple" + } +} diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/biz.ts b/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/biz.ts new file mode 100644 index 00000000..4426e917 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/biz.ts @@ -0,0 +1,24 @@ +import { Inject, SingletonProto, AccessLevel } from '@eggjs/tegg'; +import { DynamicLogger, LogPath } from '../logger/DynamicLogger'; + +@SingletonProto({ + accessLevel: AccessLevel.PUBLIC, +}) +export class Biz { + @Inject({ + name: 'dynamicLogger', + }) + @LogPath('fooBiz') + fooDynamicLogger: DynamicLogger; + + @Inject({ + name: 'dynamicLogger', + }) + @LogPath('barBiz') + barDynamicLogger: DynamicLogger; + + async doSomething(): Promise { + await this.fooDynamicLogger.info('hello, foo biz'); + await this.barDynamicLogger.info('hello, bar biz'); + } +} diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/module.yml b/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/module.yml new file mode 100644 index 00000000..bd67fdd0 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/module.yml @@ -0,0 +1,4 @@ +features: + logger: + - fooBiz + - barBiz diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/package.json b/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/package.json new file mode 100644 index 00000000..f162d826 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/biz/package.json @@ -0,0 +1,6 @@ +{ + "name": "multi-callback-instance-module-biz", + "eggModule": { + "name": "multiCallbackInstanceModuleBiz" + } +} diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/logger/DynamicLogger.ts b/standalone/standalone/test/fixtures/multi-callback-instance-module/logger/DynamicLogger.ts new file mode 100644 index 00000000..fb19e007 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/logger/DynamicLogger.ts @@ -0,0 +1,78 @@ +import { + MultiInstanceProto, + MultiInstancePrototypeGetObjectsContext, + LifecycleInit, + LifecycleDestroy, + QualifierUtil, + EggProtoImplClass, + AccessLevel, +} from '@eggjs/tegg'; +import { EggObject, ModuleConfigUtil, EggObjectLifeCycleContext } from '@eggjs/tegg/helper'; +import fs from 'node:fs'; +import { Writable } from 'node:stream'; +import path from 'node:path'; +import { EOL } from 'node:os'; + +export const LOG_PATH_ATTRIBUTE = Symbol.for('LOG_PATH_ATTRIBUTE'); + +export function LogPath(name: string) { + return function(target: any, propertyKey: PropertyKey) { + QualifierUtil.addProperQualifier(target.constructor as EggProtoImplClass, propertyKey, LOG_PATH_ATTRIBUTE, name); + }; +} + + +@MultiInstanceProto({ + accessLevel: AccessLevel.PUBLIC, + getObjects(ctx: MultiInstancePrototypeGetObjectsContext) { + const config = ModuleConfigUtil.loadModuleConfigSync(ctx.unitPath); + const logger = (config as any)?.features.logger; + if (!logger) { + return []; + } + return logger.map(name => { + return { + name: 'dynamicLogger', + qualifiers: [{ + attribute: LOG_PATH_ATTRIBUTE, + value: name, + }], + }; + }); + }, +}) +export class DynamicLogger { + stream: Writable; + loggerName: string; + + @LifecycleInit() + async init(ctx: EggObjectLifeCycleContext, obj: EggObject) { + const loggerName = obj.proto.getQualifier(LOG_PATH_ATTRIBUTE); + this.loggerName = loggerName as string; + this.stream = fs.createWriteStream(path.join(ctx.loadUnit.unitPath, `${loggerName}.log`)); + } + + @LifecycleDestroy() + async destroy() { + return new Promise((resolve, reject) => { + this.stream.end(err => { + if (err) { + return reject(err); + } + return resolve(); + }); + }); + } + + info(msg: string) { + return new Promise((resolve, reject) => { + this.stream.write(msg + EOL, err => { + if (err) { + return reject(err); + } + return resolve(); + }); + }); + } + +} diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/logger/package.json b/standalone/standalone/test/fixtures/multi-callback-instance-module/logger/package.json new file mode 100644 index 00000000..aba4cfff --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/logger/package.json @@ -0,0 +1,6 @@ +{ + "name": "multi-callback-instance-module-logger", + "eggModule": { + "name": "multiCallbackInstanceModuleLogger" + } +} diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/main/foo.ts b/standalone/standalone/test/fixtures/multi-callback-instance-module/main/foo.ts new file mode 100644 index 00000000..d0a2203b --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/main/foo.ts @@ -0,0 +1,29 @@ +import { Inject, SingletonProto } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { DynamicLogger, LogPath } from '../logger/DynamicLogger'; +import { Biz } from '../biz/biz'; + +@SingletonProto() +@Runner() +export class Foo implements MainRunner { + @Inject({ + name: 'dynamicLogger', + }) + @LogPath('foo') + fooDynamicLogger: DynamicLogger; + + @Inject({ + name: 'dynamicLogger', + }) + @LogPath('bar') + barDynamicLogger: DynamicLogger; + + @Inject() + biz: Biz; + + async main(): Promise { + await this.fooDynamicLogger.info('hello, foo'); + await this.barDynamicLogger.info('hello, bar'); + await this.biz.doSomething(); + } +} diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/main/module.yml b/standalone/standalone/test/fixtures/multi-callback-instance-module/main/module.yml new file mode 100644 index 00000000..eb532ee2 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/main/module.yml @@ -0,0 +1,4 @@ +features: + logger: + - foo + - bar diff --git a/standalone/standalone/test/fixtures/multi-callback-instance-module/main/package.json b/standalone/standalone/test/fixtures/multi-callback-instance-module/main/package.json new file mode 100644 index 00000000..d7184f72 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-callback-instance-module/main/package.json @@ -0,0 +1,6 @@ +{ + "name": "multi-callback-instance-module-main", + "eggModule": { + "name": "multiCallbackInstanceModuleMain" + } +} diff --git a/standalone/standalone/test/fixtures/multi-modules/bar/module.yml b/standalone/standalone/test/fixtures/multi-modules/bar/module.yml new file mode 100644 index 00000000..e2929a9e --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-modules/bar/module.yml @@ -0,0 +1,3 @@ +features: + dynamic: + bar: 'bar' diff --git a/standalone/standalone/test/fixtures/multi-modules/bar/package.json b/standalone/standalone/test/fixtures/multi-modules/bar/package.json new file mode 100644 index 00000000..acfa2b48 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-modules/bar/package.json @@ -0,0 +1,6 @@ +{ + "name": "bar", + "eggModule": { + "name": "bar" + } +} diff --git a/standalone/standalone/test/fixtures/multi-modules/foo/foo.ts b/standalone/standalone/test/fixtures/multi-modules/foo/foo.ts new file mode 100644 index 00000000..6250d108 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-modules/foo/foo.ts @@ -0,0 +1,43 @@ +import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; +import { ModuleConfig } from 'egg'; +import { ConfigSourceQualifier } from '../../../../src/ConfigSource'; +import { ModuleConfigs } from '../../../../src/ModuleConfigs'; + +@SingletonProto() +export class Hello { + hello() { + return 'hello!'; + } +} + +@ContextProto() +export class HelloContext { + hello() { + return 'hello from ctx'; + } +} + +@ContextProto() +@Runner() +export class Foo implements MainRunner { + @Inject() + moduleConfigs: ModuleConfigs; + + @Inject() + moduleConfig: ModuleConfig; + + @Inject({ + name: 'moduleConfig', + }) + @ConfigSourceQualifier('bar') + barModuleConfig: ModuleConfig; + + async main(): Promise { + return { + configs: this.moduleConfigs, + foo: this.moduleConfig, + bar: this.barModuleConfig, + }; + } +} diff --git a/standalone/standalone/test/fixtures/multi-modules/foo/module.yml b/standalone/standalone/test/fixtures/multi-modules/foo/module.yml new file mode 100644 index 00000000..a0046c04 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-modules/foo/module.yml @@ -0,0 +1,3 @@ +features: + dynamic: + foo: 'bar' diff --git a/standalone/standalone/test/fixtures/multi-modules/foo/package.json b/standalone/standalone/test/fixtures/multi-modules/foo/package.json new file mode 100644 index 00000000..7f30a320 --- /dev/null +++ b/standalone/standalone/test/fixtures/multi-modules/foo/package.json @@ -0,0 +1,6 @@ +{ + "name": "foo", + "eggModule": { + "name": "foo" + } +} diff --git a/standalone/standalone/test/fixtures/runtime-config/foo.ts b/standalone/standalone/test/fixtures/runtime-config/foo.ts new file mode 100644 index 00000000..ab76aac3 --- /dev/null +++ b/standalone/standalone/test/fixtures/runtime-config/foo.ts @@ -0,0 +1,15 @@ +import { Inject, SingletonProto } from '@eggjs/tegg'; +import { RuntimeConfig } from '@eggjs/tegg-common-util'; +import { Runner, MainRunner } from '@eggjs/tegg/standalone'; + +@Runner() +@SingletonProto() +export class Foo implements MainRunner { + @Inject() + runtimeConfig: RuntimeConfig; + + async main(): Promise { + return this.runtimeConfig; + } + +} diff --git a/standalone/standalone/test/fixtures/runtime-config/package.json b/standalone/standalone/test/fixtures/runtime-config/package.json new file mode 100644 index 00000000..53cf3224 --- /dev/null +++ b/standalone/standalone/test/fixtures/runtime-config/package.json @@ -0,0 +1,6 @@ +{ + "name": "runtime-config", + "eggModule": { + "name": "runtime-config" + } +} diff --git a/standalone/standalone/test/fixtures/simple/foo.ts b/standalone/standalone/test/fixtures/simple/foo.ts index f915fb36..f480f856 100644 --- a/standalone/standalone/test/fixtures/simple/foo.ts +++ b/standalone/standalone/test/fixtures/simple/foo.ts @@ -1,20 +1,30 @@ -import { Inject, SingletonProto } from '@eggjs/tegg'; +import { ContextProto, Inject, SingletonProto } from '@eggjs/tegg'; import { Runner, MainRunner } from '@eggjs/tegg/standalone'; @SingletonProto() export class Hello { hello() { - return 'hello'; + return 'hello!'; } } -@SingletonProto() +@ContextProto() +export class HelloContext { + hello() { + return 'hello from ctx'; + } +} + +@ContextProto() @Runner() export class Foo implements MainRunner { @Inject() hello: Hello; + @Inject() + helloContext: HelloContext; + async main(): Promise { - return this.hello.hello(); + return this.hello.hello() + this.helloContext.hello(); } } diff --git a/standalone/standalone/test/index.test.ts b/standalone/standalone/test/index.test.ts index 7b257715..caa552da 100644 --- a/standalone/standalone/test/index.test.ts +++ b/standalone/standalone/test/index.test.ts @@ -1,27 +1,199 @@ -import assert from 'assert'; -import { main } from '../'; -import path from 'path'; +import assert from 'node:assert'; +import path from 'node:path'; +import fs from 'node:fs/promises'; +import { main, StandaloneContext, Runner } from '..'; +import { ModuleConfigs } from '../src/ModuleConfigs'; +import { ModuleConfig } from 'egg'; +import { crosscutAdviceParams, pointcutAdviceParams } from './fixtures/aop-module/Hello'; describe('test/index.test.ts', () => { describe('simple runner', () => { it('should work', async () => { const msg: string = await main(path.join(__dirname, './fixtures/simple')); - assert(msg === 'hello'); + assert(msg === 'hello!hello from ctx'); + }); + }); + + describe('runner with dependency', () => { + it('should work', async () => { + const msg: string = await main(path.join(__dirname, './fixtures/dependency'), { + dependencies: [ + path.join(__dirname, './fixtures/dependency/node_modules/dependency-1'), + ], + }); + assert(msg === 'hello!{"features":{"dynamic":{"foo":"bar"}}}'); }); }); describe('runner with inner object', () => { it('should work', async () => { const msg: string = await main(path.join(__dirname, './fixtures/inner-object'), { - innerObjects: { - hello: { - hello: () => { - return 'hello, inner'; + innerObjectHandlers: { + hello: [{ + obj: { + hello() { + return 'hello, inner'; + }, }, - }, + }], }, }); assert(msg === 'hello, inner'); }); }); + + describe('runner with custom context', () => { + it('should work', async () => { + const runner = new Runner(path.join(__dirname, './fixtures/custom-context')); + await runner.init(); + const ctx = new StandaloneContext(); + ctx.set('foo', 'foo'); + const msg = await runner.run(ctx); + await runner.destroy(); + assert(msg === 'foo'); + }); + }); + + describe('module with config', () => { + it('should work', async () => { + const config = await main(path.join(__dirname, './fixtures/module-with-config')); + assert.deepStrictEqual(config, { + features: { + dynamic: { + foo: 'bar', + }, + }, + }); + }); + + it('should work with env', async () => { + const config = await main(path.join(__dirname, './fixtures/module-with-env-config'), { + env: 'dev', + }); + assert.deepStrictEqual(config, { + features: { + dynamic: { + foo: 'foo', + }, + }, + }); + }); + }); + + describe('@ConfigSource qualifier', () => { + it('should work', async () => { + const { configs, foo, bar } = (await main(path.join(__dirname, './fixtures/multi-modules'))) as { + configs: ModuleConfigs, + foo: ModuleConfig, + bar: ModuleConfig, + }; + assert.deepStrictEqual(configs.get('foo'), foo); + assert.deepStrictEqual(configs.get('bar'), bar); + }); + }); + + describe('runner with runtimeConfig', () => { + it('should work', async () => { + const msg = await main(path.join(__dirname, './fixtures/runtime-config')); + assert.deepEqual(msg, { + baseDir: path.join(__dirname, './fixtures/runtime-config'), + env: undefined, + name: undefined, + }); + }); + + it('should auto set name and env', async () => { + const msg = await main(path.join(__dirname, './fixtures/runtime-config'), { + name: 'foo', + env: 'unittest', + }); + assert.deepEqual(msg, { + baseDir: path.join(__dirname, './fixtures/runtime-config'), + name: 'foo', + env: 'unittest', + }); + }); + }); + + describe('multi instance prototype runner', () => { + const fixturePath = path.join(__dirname, './fixtures/multi-callback-instance-module'); + afterEach(async () => { + await fs.unlink(path.join(fixturePath, 'main', 'foo.log')); + await fs.unlink(path.join(fixturePath, 'main', 'bar.log')); + await fs.unlink(path.join(fixturePath, 'biz', 'fooBiz.log')); + await fs.unlink(path.join(fixturePath, 'biz', 'barBiz.log')); + }); + + it('should work', async () => { + await main(fixturePath); + const fooContent = await fs.readFile(path.join(fixturePath, 'main', 'foo.log'), 'utf8'); + const barContent = await fs.readFile(path.join(fixturePath, 'main', 'bar.log'), 'utf8'); + assert(fooContent.includes('hello, foo')); + assert(barContent.includes('hello, bar')); + + const fooBizContent = await fs.readFile(path.join(fixturePath, 'biz', 'fooBiz.log'), 'utf8'); + const barBizContent = await fs.readFile(path.join(fixturePath, 'biz', 'barBiz.log'), 'utf8'); + assert(fooBizContent.includes('hello, foo biz')); + assert(barBizContent.includes('hello, bar biz')); + }); + }); + + describe('dynamic inject', () => { + const fixturePath = path.join(__dirname, './fixtures/dynamic-inject-module'); + + it('should work', async () => { + const msgs = await main(fixturePath); + assert.deepStrictEqual(msgs, [ + 'hello, foo(context:0)', + 'hello, bar(context:0)', + 'hello, foo(singleton:0)', + 'hello, bar(singleton:0)', + ]); + }); + }); + + describe('aop runtime', () => { + const fixturePath = path.join(__dirname, './fixtures/aop-module'); + + it('should work', async () => { + const msg = await main(fixturePath); + assert.deepStrictEqual(msg, + `withCrossAroundResult(withPointAroundResult(hello withPointAroundParam(withCrosscutAroundParam(aop))${JSON.stringify(pointcutAdviceParams)})${JSON.stringify(crosscutAdviceParams)})`); + }); + }); + + describe('load', () => { + let runner: Runner; + afterEach(async () => { + if (runner) await runner.destroy(); + }); + + it('should work', async () => { + runner = new Runner(path.join(__dirname, './fixtures/simple')); + const loadunits = await runner.load(); + for (const loadunit of loadunits) { + for (const proto of loadunit.iterateEggPrototype()) { + if (proto.id.match(/:hello$/)) { + assert.strictEqual(proto.className, 'Hello'); + } else if (proto.id.match(/:moduleConfigs$/)) { + assert.strictEqual(proto.className, undefined); + } else if (proto.id.match(/:moduleConfig$/)) { + assert.strictEqual(proto.className, undefined); + } + } + } + }); + + it('should work with multi', async () => { + runner = new Runner(path.join(__dirname, './fixtures/multi-callback-instance-module')); + const loadunits = await runner.load(); + for (const loadunit of loadunits) { + for (const proto of loadunit.iterateEggPrototype()) { + if (proto.id.match(/:dynamicLogger$/)) { + assert.strictEqual(proto.className, 'DynamicLogger'); + } + } + } + }); + }); }); diff --git a/standalone/standalone/tsconfig.json b/standalone/standalone/tsconfig.json index ed206ac6..64b22405 100644 --- a/standalone/standalone/tsconfig.json +++ b/standalone/standalone/tsconfig.json @@ -6,6 +6,7 @@ }, "exclude": [ "dist", - "node_modules" + "node_modules", + "test" ] } diff --git a/tsconfig.json b/tsconfig.json index be0145ff..0f9ad714 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,12 @@ { "compileOnSave": true, "compilerOptions": { - "target": "es2019", - "module": "commonjs", + "target": "ES2019", + "module": "CommonJS", "strict": true, "noImplicitAny": false, "esModuleInterop": true, "useUnknownInCatchVariables": false, - "charset": "utf8", "allowJs": false, "pretty": true, "noEmitOnError": false, @@ -24,5 +23,8 @@ "resolveJsonModule": true, "emitDecoratorMetadata": true, "experimentalDecorators": true + }, + "ts-node": { + "files": true } }