From b803be9182533c3b5a20f8da86ca83675ee85b03 Mon Sep 17 00:00:00 2001 From: Wenzhao Hu Date: Wed, 24 Apr 2024 14:36:47 +0800 Subject: [PATCH] feat: show resolution chain on resolving error (#19) * feat: show resolution chain on resolving error * ci: remove type check from CI --- .eslintrc.js | 13 -- .github/workflows/ci-test.yml | 17 +-- .prettierrc | 6 - package.json | 18 +-- pnpm-lock.yaml | 218 ++++++++++++++++++++++++---------- src/dependencyCollection.ts | 39 +++--- src/dependencyIdentifier.ts | 1 - src/dependencyLookUp.ts | 6 +- src/dependencyQuantity.ts | 9 +- src/dependencyWithNew.ts | 3 +- src/idleValue.ts | 75 ++++++------ src/injector.ts | 81 ++++++++----- test/core.spec.ts | 132 ++++++++++---------- tsconfig.json | 86 +++++++------- 14 files changed, 392 insertions(+), 312 deletions(-) delete mode 100644 .eslintrc.js delete mode 100644 .prettierrc diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index f4a0eff..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], - rules: { - // any is inevitable in libraries I guess - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-extra-semi': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/ban-ts-comment': 'off', - }, -} diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index 08db14e..5dd3950 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -41,23 +41,8 @@ jobs: restore-keys: | ${{ runner.os }}-pnpm-store- - - name: Setup ESLint cache - uses: actions/cache@v3 - with: - path: .eslintcache - key: | - ${{ runner.os }}-eslint-${{ hashFiles('.eslintrc.cjs') }} - restore-keys: | - ${{ runner.os }}-eslint- - - name: Install dependencies run: pnpm install - - name: 🩺 Run Test + - name: 🩺 Run test run: pnpm run test - - - name: 🌡️ Run ESLint - run: pnpm run lint - - - name: 🧼 Type checking - run: pnpm typeCheck diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 0538705..0000000 --- a/.prettierrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "singleQuote": true, - "semi": false, - "tabWidth": 2, - "printWidth": 80 -} \ No newline at end of file diff --git a/package.json b/package.json index ea927e9..640bc0a 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,13 @@ { "$schema": "https://raw.githubusercontent.com/wzhudev/squirrel/master/src/schema/package.schema.json", "name": "@wendellhu/redi", - "version": "0.13.1", + "version": "0.13.2", "description": "A dependency library for TypeScript and JavaScript, along with a binding for React.", "scripts": { - "test": "vitest run --coverage", + "test": "vitest run", + "coverage": "vitest run --coverage", "watch": "vitest", - "lint": "eslint --fix 'src/**/*.ts'", - "lint:prettier": "prettier --write '{src,test}/**/*.{ts,tsx}'", - "build": "squirrel", - "typeCheck": "tsc -p tsconfig.check.json --noEmit" + "build": "squirrel" }, "squirrel": { "copyFiles": [ @@ -24,17 +22,13 @@ "@testing-library/react": "^14.0.0", "@types/node": "^20.11.7", "@types/react": "^18.2.48", - "@typescript-eslint/eslint-plugin": "^6.19.1", - "@typescript-eslint/parser": "^6.19.1", + "@vitest/coverage-istanbul": "^1.5.0", "@wendellhu/squirrel": "^0.1.7", - "eslint": "^7.32.0", "jsdom": "^19.0.0", - "prettier": "^2.3.2", "react": "^18.0.0", "react-dom": "^18.0.0", "rxjs": "^6.2.1", "typescript": "^5.3.3", - "vitest": "^1.2.1", - "@vitest/coverage-istanbul": "^1.2.2" + "vitest": "^1.5.0" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 580d8a8..fef5e71 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 8.20.1 '@testing-library/react': specifier: ^14.0.0 - version: 14.2.1(react-dom@18.2.0)(react@18.2.0) + version: 14.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@types/node': specifier: ^20.11.7 version: 20.11.16 @@ -22,19 +22,25 @@ importers: version: 18.2.55 '@typescript-eslint/eslint-plugin': specifier: ^6.19.1 - version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@7.32.0)(typescript@5.3.3) + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@7.32.0)(typescript@5.3.3))(eslint@7.32.0)(typescript@5.3.3) '@typescript-eslint/parser': specifier: ^6.19.1 version: 6.21.0(eslint@7.32.0)(typescript@5.3.3) '@vitest/coverage-istanbul': - specifier: ^1.2.2 - version: 1.2.2(vitest@1.2.2) + specifier: ^1.5.0 + version: 1.5.0(vitest@1.5.0(@types/node@20.11.16)(jsdom@19.0.0)) '@wendellhu/squirrel': specifier: ^0.1.7 version: 0.1.7 eslint: specifier: ^7.32.0 version: 7.32.0 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@7.32.0) + eslint-plugin-prettier: + specifier: ^5.1.3 + version: 5.1.3(eslint-config-prettier@9.1.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8) jsdom: specifier: ^19.0.0 version: 19.0.0 @@ -54,8 +60,8 @@ importers: specifier: ^5.3.3 version: 5.3.3 vitest: - specifier: ^1.2.1 - version: 1.2.2(@types/node@20.11.16)(jsdom@19.0.0) + specifier: ^1.5.0 + version: 1.5.0(@types/node@20.11.16)(jsdom@19.0.0) packages: @@ -346,6 +352,9 @@ packages: '@jridgewell/trace-mapping@0.3.22': resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -358,6 +367,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@pkgr/core@0.1.1': + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@rollup/rollup-android-arm-eabi@4.9.6': resolution: {integrity: sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg==} cpu: [arm] @@ -530,25 +543,25 @@ packages: resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} engines: {node: ^16.0.0 || >=18.0.0} - '@vitest/coverage-istanbul@1.2.2': - resolution: {integrity: sha512-tJybwO8JT4H9ANz0T0/tJ1M5g3BkuHKYF1w5YO3z9sAiHBdGANrxN9c5lomJx1WSnLzCxQR5xxlJ4TLKbzrR3w==} + '@vitest/coverage-istanbul@1.5.0': + resolution: {integrity: sha512-mEbVTIAPKhMkszO0lwOwWiG8Cvkj7rdMgdmCNUDnmcSZYUWGIqM8+4O1bcQ1WMHkejpcwvED5oU6ZFm3syVb6A==} peerDependencies: - vitest: ^1.0.0 + vitest: 1.5.0 - '@vitest/expect@1.2.2': - resolution: {integrity: sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg==} + '@vitest/expect@1.5.0': + resolution: {integrity: sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==} - '@vitest/runner@1.2.2': - resolution: {integrity: sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg==} + '@vitest/runner@1.5.0': + resolution: {integrity: sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==} - '@vitest/snapshot@1.2.2': - resolution: {integrity: sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA==} + '@vitest/snapshot@1.5.0': + resolution: {integrity: sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==} - '@vitest/spy@1.2.2': - resolution: {integrity: sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g==} + '@vitest/spy@1.5.0': + resolution: {integrity: sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==} - '@vitest/utils@1.2.2': - resolution: {integrity: sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g==} + '@vitest/utils@1.5.0': + resolution: {integrity: sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==} '@wendellhu/squirrel@0.1.7': resolution: {integrity: sha512-C/0GEXgMeEq21zw7j4nVVfHmgd4HUGyBqybPMkj4ZcSpmCspVrXFm5GVZ2vFRCeeyHzYyKFbvI5ZvP0r3YSGKg==} @@ -835,6 +848,26 @@ packages: engines: {node: '>=6.0'} hasBin: true + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-prettier@5.1.3: + resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -899,6 +932,9 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + fast-glob@3.3.2: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} @@ -1165,8 +1201,8 @@ packages: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + istanbul-lib-source-maps@5.0.4: + resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==} engines: {node: '>=10'} istanbul-reports@3.1.6: @@ -1176,6 +1212,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true @@ -1391,6 +1430,10 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} @@ -1574,8 +1617,8 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} @@ -1588,6 +1631,10 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.8.8: + resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} + engines: {node: ^14.18.0 || >=16.0.0} + table@6.8.1: resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} engines: {node: '>=10.0.0'} @@ -1602,8 +1649,8 @@ packages: tinybench@2.6.0: resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==} - tinypool@0.8.2: - resolution: {integrity: sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==} + tinypool@0.8.4: + resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} engines: {node: '>=14.0.0'} tinyspy@2.2.0: @@ -1635,6 +1682,9 @@ packages: tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1677,8 +1727,8 @@ packages: v8-compile-cache@2.4.0: resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} - vite-node@1.2.2: - resolution: {integrity: sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg==} + vite-node@1.5.0: + resolution: {integrity: sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -1710,15 +1760,15 @@ packages: terser: optional: true - vitest@1.2.2: - resolution: {integrity: sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw==} + vitest@1.5.0: + resolution: {integrity: sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': ^1.0.0 - '@vitest/ui': ^1.0.0 + '@vitest/browser': 1.5.0 + '@vitest/ui': 1.5.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2080,6 +2130,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2092,6 +2147,8 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@pkgr/core@0.1.1': {} + '@rollup/rollup-android-arm-eabi@4.9.6': optional: true @@ -2155,7 +2212,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/react@14.2.1(react-dom@18.2.0)(react@18.2.0)': + '@testing-library/react@14.2.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@babel/runtime': 7.23.9 '@testing-library/dom': 9.3.4 @@ -2191,7 +2248,7 @@ snapshots: '@types/semver@7.5.6': {} - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@7.32.0)(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@7.32.0)(typescript@5.3.3))(eslint@7.32.0)(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.10.0 '@typescript-eslint/parser': 6.21.0(eslint@7.32.0)(typescript@5.3.3) @@ -2206,6 +2263,7 @@ snapshots: natural-compare: 1.4.0 semver: 7.6.0 ts-api-utils: 1.2.1(typescript@5.3.3) + optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: - supports-color @@ -2218,6 +2276,7 @@ snapshots: '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.3.4 eslint: 7.32.0 + optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: - supports-color @@ -2234,6 +2293,7 @@ snapshots: debug: 4.3.4 eslint: 7.32.0 ts-api-utils: 1.2.1(typescript@5.3.3) + optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: - supports-color @@ -2250,6 +2310,7 @@ snapshots: minimatch: 9.0.3 semver: 7.6.0 ts-api-utils: 1.2.1(typescript@5.3.3) + optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: - supports-color @@ -2273,44 +2334,44 @@ snapshots: '@typescript-eslint/types': 6.21.0 eslint-visitor-keys: 3.4.3 - '@vitest/coverage-istanbul@1.2.2(vitest@1.2.2)': + '@vitest/coverage-istanbul@1.5.0(vitest@1.5.0(@types/node@20.11.16)(jsdom@19.0.0))': dependencies: debug: 4.3.4 istanbul-lib-coverage: 3.2.2 istanbul-lib-instrument: 6.0.1 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 + istanbul-lib-source-maps: 5.0.4 istanbul-reports: 3.1.6 magicast: 0.3.3 picocolors: 1.0.0 test-exclude: 6.0.0 - vitest: 1.2.2(@types/node@20.11.16)(jsdom@19.0.0) + vitest: 1.5.0(@types/node@20.11.16)(jsdom@19.0.0) transitivePeerDependencies: - supports-color - '@vitest/expect@1.2.2': + '@vitest/expect@1.5.0': dependencies: - '@vitest/spy': 1.2.2 - '@vitest/utils': 1.2.2 + '@vitest/spy': 1.5.0 + '@vitest/utils': 1.5.0 chai: 4.4.1 - '@vitest/runner@1.2.2': + '@vitest/runner@1.5.0': dependencies: - '@vitest/utils': 1.2.2 + '@vitest/utils': 1.5.0 p-limit: 5.0.0 pathe: 1.1.2 - '@vitest/snapshot@1.2.2': + '@vitest/snapshot@1.5.0': dependencies: magic-string: 0.30.7 pathe: 1.1.2 pretty-format: 29.7.0 - '@vitest/spy@1.2.2': + '@vitest/spy@1.5.0': dependencies: tinyspy: 2.2.0 - '@vitest/utils@1.2.2': + '@vitest/utils@1.5.0': dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 @@ -2632,6 +2693,19 @@ snapshots: optionalDependencies: source-map: 0.6.1 + eslint-config-prettier@9.1.0(eslint@7.32.0): + dependencies: + eslint: 7.32.0 + + eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8): + dependencies: + eslint: 7.32.0 + prettier: 2.8.8 + prettier-linter-helpers: 1.0.0 + synckit: 0.8.8 + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@7.32.0) + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 @@ -2732,6 +2806,8 @@ snapshots: fast-deep-equal@3.1.3: {} + fast-diff@1.3.0: {} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2999,11 +3075,11 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1: + istanbul-lib-source-maps@5.0.4: dependencies: + '@jridgewell/trace-mapping': 0.3.25 debug: 4.3.4 istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 transitivePeerDependencies: - supports-color @@ -3014,6 +3090,8 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.0: {} + js-yaml@3.14.1: dependencies: argparse: 1.0.10 @@ -3236,6 +3314,10 @@ snapshots: prelude-ls@1.2.1: {} + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + prettier@2.8.8: {} pretty-format@27.5.1: @@ -3385,7 +3467,8 @@ snapshots: source-map-js@1.0.2: {} - source-map@0.6.1: {} + source-map@0.6.1: + optional: true sprintf-js@1.0.3: {} @@ -3411,9 +3494,9 @@ snapshots: strip-json-comments@3.1.1: {} - strip-literal@1.3.0: + strip-literal@2.1.0: dependencies: - acorn: 8.11.3 + js-tokens: 9.0.0 supports-color@5.5.0: dependencies: @@ -3425,6 +3508,11 @@ snapshots: symbol-tree@3.2.4: {} + synckit@0.8.8: + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.6.2 + table@6.8.1: dependencies: ajv: 8.12.0 @@ -3443,7 +3531,7 @@ snapshots: tinybench@2.6.0: {} - tinypool@0.8.2: {} + tinypool@0.8.4: {} tinyspy@2.2.0: {} @@ -3470,6 +3558,8 @@ snapshots: tslib@1.14.1: {} + tslib@2.6.2: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -3503,7 +3593,7 @@ snapshots: v8-compile-cache@2.4.0: {} - vite-node@1.2.2(@types/node@20.11.16): + vite-node@1.5.0(@types/node@20.11.16): dependencies: cac: 6.7.14 debug: 4.3.4 @@ -3522,38 +3612,38 @@ snapshots: vite@5.0.12(@types/node@20.11.16): dependencies: - '@types/node': 20.11.16 esbuild: 0.19.12 postcss: 8.4.35 rollup: 4.9.6 optionalDependencies: + '@types/node': 20.11.16 fsevents: 2.3.3 - vitest@1.2.2(@types/node@20.11.16)(jsdom@19.0.0): + vitest@1.5.0(@types/node@20.11.16)(jsdom@19.0.0): dependencies: - '@types/node': 20.11.16 - '@vitest/expect': 1.2.2 - '@vitest/runner': 1.2.2 - '@vitest/snapshot': 1.2.2 - '@vitest/spy': 1.2.2 - '@vitest/utils': 1.2.2 + '@vitest/expect': 1.5.0 + '@vitest/runner': 1.5.0 + '@vitest/snapshot': 1.5.0 + '@vitest/spy': 1.5.0 + '@vitest/utils': 1.5.0 acorn-walk: 8.3.2 - cac: 6.7.14 chai: 4.4.1 debug: 4.3.4 execa: 8.0.1 - jsdom: 19.0.0 local-pkg: 0.5.0 magic-string: 0.30.7 pathe: 1.1.2 picocolors: 1.0.0 std-env: 3.7.0 - strip-literal: 1.3.0 + strip-literal: 2.1.0 tinybench: 2.6.0 - tinypool: 0.8.2 + tinypool: 0.8.4 vite: 5.0.12(@types/node@20.11.16) - vite-node: 1.2.2(@types/node@20.11.16) + vite-node: 1.5.0(@types/node@20.11.16) why-is-node-running: 2.2.2 + optionalDependencies: + '@types/node': 20.11.16 + jsdom: 19.0.0 transitivePeerDependencies: - less - lightningcss diff --git a/src/dependencyCollection.ts b/src/dependencyCollection.ts index f0b49af..8f19545 100644 --- a/src/dependencyCollection.ts +++ b/src/dependencyCollection.ts @@ -25,29 +25,41 @@ export function isBareClassDependency( return thing.length === 1 } +const ResolvingStack: DependencyIdentifier[] = [] + +export function pushResolvingStack(id: DependencyIdentifier) { + ResolvingStack.push(id) +} + +export function popupResolvingStack() { + ResolvingStack.pop() +} + +export function clearResolvingStack() { + ResolvingStack.length = 0 +} + export class DependencyNotFoundForModuleError extends RediError { constructor( toInstantiate: Ctor | DependencyIdentifier, id: DependencyIdentifier, - index: number + index: number, ) { - const msg = `Cannot find "${prettyPrintIdentifier( - id - )}" registered by any injector. It is the ${index}th param of "${ - isIdentifierDecorator(toInstantiate) - ? prettyPrintIdentifier(toInstantiate) - : (toInstantiate as Ctor).name - }".` - + const msg = `Cannot find "${prettyPrintIdentifier(id)}" registered by any injector. It is the ${index}th param of "${isIdentifierDecorator(toInstantiate) + ? prettyPrintIdentifier(toInstantiate) + : (toInstantiate as Ctor).name + }". The stack of dependencies is: "${ResolvingStack.map((id) => prettyPrintIdentifier(id)).join(' -> ')}".` super(msg) + + clearResolvingStack(); } } export class DependencyNotFoundError extends RediError { - constructor(id: DependencyIdentifier) { - const msg = `Cannot find "${prettyPrintIdentifier( - id - )}" registered by any injector.` + constructor( + id: DependencyIdentifier, + ) { + const msg = `Cannot find "${prettyPrintIdentifier(id)}" registered by any injector.` super(msg) } @@ -113,7 +125,6 @@ export class DependencyCollection implements IDisposable { id: DependencyIdentifier, quantity: Quantity = Quantity.REQUIRED ): DependencyItem | DependencyItem[] | null { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const ret = this.dependencyMap.get(id)! checkQuantity(id, quantity, ret.length) diff --git a/src/dependencyIdentifier.ts b/src/dependencyIdentifier.ts index 6225b93..d9554cf 100644 --- a/src/dependencyIdentifier.ts +++ b/src/dependencyIdentifier.ts @@ -17,7 +17,6 @@ export type IdentifierDecorator = { type: T } -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types export function isIdentifierDecorator( thing: any ): thing is IdentifierDecorator { diff --git a/src/dependencyLookUp.ts b/src/dependencyLookUp.ts index 8da8324..c5a73e2 100644 --- a/src/dependencyLookUp.ts +++ b/src/dependencyLookUp.ts @@ -21,8 +21,7 @@ function lookupDecoratorFactoryProducer(lookUp: LookUp) { interface SkipSelfDecorator { (): any - // eslint-disable-next-line @typescript-eslint/no-misused-new - new (): SkipSelfDecorator + new(): SkipSelfDecorator } /** * when resolving this dependency, skip the current injector @@ -33,8 +32,7 @@ export const SkipSelf: SkipSelfDecorator = lookupDecoratorFactoryProducer( interface SelfDecorator { (): any - // eslint-disable-next-line @typescript-eslint/no-misused-new - new (): SelfDecorator + new(): SelfDecorator } /** * when resolving this dependency, only search the current injector diff --git a/src/dependencyQuantity.ts b/src/dependencyQuantity.ts index de3e626..20b7513 100644 --- a/src/dependencyQuantity.ts +++ b/src/dependencyQuantity.ts @@ -75,8 +75,7 @@ function quantifyDecoratorFactoryProducer(quantity: Quantity) { interface ManyDecorator { (id?: DependencyIdentifier): any - // eslint-disable-next-line @typescript-eslint/no-misused-new - new (): ManyDecorator + new(): ManyDecorator } export const Many: ManyDecorator = quantifyDecoratorFactoryProducer( Quantity.MANY @@ -84,8 +83,7 @@ export const Many: ManyDecorator = quantifyDecoratorFactoryProducer( interface OptionalDecorator { (id?: DependencyIdentifier): any - // eslint-disable-next-line @typescript-eslint/no-misused-new - new (): OptionalDecorator + new(): OptionalDecorator } export const Optional: OptionalDecorator = quantifyDecoratorFactoryProducer( Quantity.OPTIONAL @@ -93,8 +91,7 @@ export const Optional: OptionalDecorator = quantifyDecoratorFactoryProducer( interface InjectDecorator { (id: DependencyIdentifier): any - // eslint-disable-next-line @typescript-eslint/no-misused-new - new (): InjectDecorator + new(): InjectDecorator } export const Inject: InjectDecorator = quantifyDecoratorFactoryProducer( Quantity.REQUIRED diff --git a/src/dependencyWithNew.ts b/src/dependencyWithNew.ts index 8a2d990..ade8658 100644 --- a/src/dependencyWithNew.ts +++ b/src/dependencyWithNew.ts @@ -20,8 +20,7 @@ function withNewDecoratorFactoryProducer(withNew: boolean) { interface ToSelfDecorator { (): any - // eslint-disable-next-line @typescript-eslint/no-misused-new - new (): ToSelfDecorator + new(): ToSelfDecorator } /** diff --git a/src/idleValue.ts b/src/idleValue.ts index 4e2c9b4..eae4efa 100644 --- a/src/idleValue.ts +++ b/src/idleValue.ts @@ -23,49 +23,49 @@ declare function requestIdleCallback( ): number declare function cancelIdleCallback(handle: number): void -// use an IIFE to set up runWhenIdle -;(function () { - if ( - typeof requestIdleCallback !== 'undefined' && - typeof cancelIdleCallback !== 'undefined' - ) { - // use native requestIdleCallback - runWhenIdle = (runner, timeout?) => { - const handle: number = requestIdleCallback( - runner, - typeof timeout === 'number' ? { timeout } : undefined - ) - let disposed = false - return () => { - if (disposed) { - return + // use an IIFE to set up runWhenIdle + ; (function () { + if ( + typeof requestIdleCallback !== 'undefined' && + typeof cancelIdleCallback !== 'undefined' + ) { + // use native requestIdleCallback + runWhenIdle = (runner, timeout?) => { + const handle: number = requestIdleCallback( + runner, + typeof timeout === 'number' ? { timeout } : undefined + ) + let disposed = false + return () => { + if (disposed) { + return + } + disposed = true + cancelIdleCallback(handle) } - disposed = true - cancelIdleCallback(handle) } - } - } else { - // use setTimeout as hack - const dummyIdle: IdleDeadline = Object.freeze({ - didTimeout: true, - timeRemaining() { - return 15 - }, - }) + } else { + // use setTimeout as hack + const dummyIdle: IdleDeadline = Object.freeze({ + didTimeout: true, + timeRemaining() { + return 15 + }, + }) - runWhenIdle = (runner) => { - const handle = setTimeout(() => runner(dummyIdle)) - let disposed = false - return () => { - if (disposed) { - return + runWhenIdle = (runner) => { + const handle = setTimeout(() => runner(dummyIdle)) + let disposed = false + return () => { + if (disposed) { + return + } + disposed = true + clearTimeout(handle) } - disposed = true - clearTimeout(handle) } } - } -})() + })() /** * a wrapper of a executor so it can be evaluated when it's necessary or the CPU is idle @@ -110,7 +110,6 @@ export class IdleValue implements IDisposable { if (this.error) { throw this.error } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.value! } } diff --git a/src/injector.ts b/src/injector.ts index f62124c..1e20bb7 100644 --- a/src/injector.ts +++ b/src/injector.ts @@ -1,3 +1,4 @@ +import { findIndex } from 'rxjs/operators' import { getDependencies } from './decorators' import { Dependency, @@ -6,6 +7,9 @@ import { DependencyNotFoundForModuleError, DependencyOrInstance, ResolvedDependencyCollection, + clearResolvingStack, + popupResolvingStack, + pushResolvingStack, } from './dependencyCollection' import { normalizeFactoryDeps } from './dependencyDescriptor' import { normalizeForwardRef } from './dependencyForwardRef' @@ -297,16 +301,20 @@ export class Injector { quantityOrLookup?: Quantity | LookUp, lookUp?: LookUp ): T[] | T | null { - const newResult = this._get(id, quantityOrLookup, lookUp) + try { + const newResult = this._get(id, quantityOrLookup, lookUp) + if ((Array.isArray(newResult) && newResult.some((r) => isAsyncHook(r))) || isAsyncHook(newResult)) { + throw new GetAsyncItemFromSyncApiError(id) + } - if ( - (Array.isArray(newResult) && newResult.some((r) => isAsyncHook(r))) || - isAsyncHook(newResult) - ) { - throw new GetAsyncItemFromSyncApiError(id) - } + return newResult as T | T[] | null + } catch (e: unknown) { + if (e instanceof DependencyNotFoundError) { + clearResolvingStack(); + } - return newResult as T | T[] | null + throw e; + } } private _get( @@ -380,22 +388,39 @@ export class Injector { item: DependencyItem, shouldCache = true ): T | AsyncHook { - if (isValueDependencyItem(item)) { - return this.resolveValueDependency(id, item as ValueDependencyItem) - } else if (isFactoryDependencyItem(item)) { - return this.resolveFactory( - id, - item as FactoryDependencyItem, - shouldCache - ) - } else if (isClassDependencyItem(item)) { - return this.resolveClass(id, item as ClassDependencyItem, shouldCache) - } else { - return this.resolveAsync(id, item as AsyncDependencyItem) + let result: T | AsyncHook + + pushResolvingStack(id) + + try { + if (isValueDependencyItem(item)) { + result = this._resolveValueDependency(id, item as ValueDependencyItem) + } else if (isFactoryDependencyItem(item)) { + result = this.resolveFactory( + id, + item as FactoryDependencyItem, + shouldCache + ) + } else if (isClassDependencyItem(item)) { + result = this.resolveClass( + id, + item as ClassDependencyItem, + shouldCache + ) + } else { + result = this.resolveAsync(id, item as AsyncDependencyItem) + } + + popupResolvingStack() + } catch (e: unknown) { + popupResolvingStack() + throw e; } + + return result } - private resolveValueDependency( + private _resolveValueDependency( id: DependencyIdentifier, item: ValueDependencyItem ): T { @@ -442,7 +467,7 @@ export class Injector { return property }, set(_target: any, key: string | number | symbol, value: any): boolean { - ;(idle.getValue() as any)[key] = value + ; (idle.getValue() as any)[key] = value return true }, }) @@ -484,7 +509,7 @@ export class Injector { throw new DependencyNotFoundForModuleError( ctor, dep.identifier, - dep.paramIndex + dep.paramIndex, ) } @@ -500,8 +525,7 @@ export class Injector { if (args.length !== firstDependencyArgIndex) { console.warn( - `[redi]: Expect ${firstDependencyArgIndex} custom parameter(s) of ${ctor.toString()} but get ${ - args.length + `[redi]: Expect ${firstDependencyArgIndex} custom parameter(s) of ${ctor.toString()} but get ${args.length }.` ) @@ -544,7 +568,7 @@ export class Injector { throw new DependencyNotFoundForModuleError( id, dep.identifier, - dep.paramIndex + dep.paramIndex, ) } @@ -661,9 +685,7 @@ export class Injector { let ret: (T | AsyncHook)[] | T | AsyncHook | null = null if (Array.isArray(registrations)) { - ret = registrations.map((dependencyItem) => - this.resolveDependency(id, dependencyItem, shouldCache) - ) + ret = registrations.map((dependencyItem) => this.resolveDependency(id, dependencyItem, shouldCache)) } else if (registrations) { ret = this.resolveDependency(id, registrations, shouldCache) } @@ -684,6 +706,7 @@ export class Injector { return null } + pushResolvingStack(id) throw new DependencyNotFoundError(id) } } diff --git a/test/core.spec.ts b/test/core.spec.ts index 8305e3f..2c637fc 100644 --- a/test/core.spec.ts +++ b/test/core.spec.ts @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/no-empty-function */ - import { vi, describe, afterEach, it, expect } from 'vitest' import { @@ -28,17 +25,19 @@ function cleanupTest() { } describe('core', () => { - describe('basics', () => { - afterEach(() => cleanupTest()) + afterEach(() => cleanupTest()); - it('should throw error when identifier has been declared before', () => { - createIdentifier('a') + it('should print the dependencies stack when cannot resolve', () => { + class A { } + class B { constructor(@Inject(A) private a: A) { } } + class C { constructor(@Inject(B) private b: B) { } } + class D { constructor(@Inject(C) private c: C) { } } - expectToThrow( - () => createIdentifier('a'), - '[redi]: Identifier "a" already exists.' - ) - }) + const j = new Injector([[B], [C], [D]]); + expectToThrow(() => j.get(D), 'Cannot find "A" registered by any injector. It is the 0th param of "B". The stack of dependencies is: "D -> C -> B -> A"'); + }); + + describe('basics', () => { it('should resolve instance and then cache it', () => { let createCount = 0 @@ -66,7 +65,7 @@ describe('core', () => { } class B { - constructor(@Inject(A) public a: A) {} + constructor(@Inject(A) public a: A) { } } interface C { @@ -123,7 +122,7 @@ describe('core', () => { } class B { - constructor(@Inject(IA) public a: IA) {} + constructor(@Inject(IA) public a: IA) { } } j.add([IA, { useClass: A }]) @@ -158,7 +157,7 @@ describe('core', () => { } class B { - constructor(@Many(IA) public a: IA[]) {} + constructor(@Many(IA) public a: IA[]) { } } j.add([IA, { useClass: A }]) @@ -173,7 +172,7 @@ describe('core', () => { } class B { - constructor(@Inject(A) public a: A) {} + constructor(@Inject(A) public a: A) { } } const j = new Injector([[A]]) @@ -191,7 +190,7 @@ describe('core', () => { constructor( private readonly otherKey: string, @Inject(A) public readonly a: A - ) {} + ) { } get key() { return this.otherKey + 'a' @@ -203,7 +202,7 @@ describe('core', () => { expect(b.key).toBe('another a') }) - it('should "createInstance" truncate extra custom args', () => {}) + it('should "createInstance" truncate extra custom args', () => { }) it('should "createInstance" fill unprovided custom args with "undefined"', () => { class A { @@ -215,7 +214,7 @@ describe('core', () => { private readonly otherKey: string, private readonly secondKey: string, @Inject(A) public readonly a: A - ) {} + ) { } get key() { return this.otherKey + this.secondKey + ' ' + this.a.key @@ -223,7 +222,7 @@ describe('core', () => { } const spy = vi.spyOn(console, 'warn') - spy.mockImplementation(() => {}) + spy.mockImplementation(() => { }) const j = new Injector([[A]]) const b = j.createInstance(B, 'another ') @@ -249,11 +248,11 @@ describe('core', () => { const bI = createIdentifier('bI') class A { - constructor(@Inject(bI) private readonly b: any) {} + constructor(@Inject(bI) private readonly b: any) { } } class B { - constructor(@Inject(aI) private readonly a: any) {} + constructor(@Inject(aI) private readonly a: any) { } } const j = new Injector([ @@ -282,9 +281,9 @@ describe('core', () => { }) it('should support checking if a dependency could be resolved by an injector', () => { - class A {} + class A { } - class B {} + class B { } const j = new Injector([[A]]) @@ -295,7 +294,7 @@ describe('core', () => { describe('different types of dependency items', () => { describe('class item', () => { - afterEach(() => cleanupTest()) + it('should dispose idle callback when dependency immediately resolved', async () => { interface A { @@ -356,7 +355,7 @@ describe('core', () => { } class B { - constructor(@Inject(aI) private a: A) {} + constructor(@Inject(aI) private a: A) { } get key(): string { return this.a.key + 'b' @@ -392,7 +391,7 @@ describe('core', () => { } class B { - constructor(public readonly a: A) {} + constructor(public readonly a: A) { } } setDependencies(B, [[A]]) @@ -406,7 +405,7 @@ describe('core', () => { it('should warn use when a dependency is missing', () => { class A { - constructor(private b: typeof B) {} + constructor(private b: typeof B) { } get key(): string { return typeof this.b === 'undefined' @@ -418,9 +417,11 @@ describe('core', () => { // mock that B is not assigned to the class constructor let B: any = undefined - expectToThrow(() => { + expect(() => { setDependencies(A, [[B]]) - }, '[redi]: It seems that you register "undefined" as dependency on the 1 parameter of "A".') + }).toThrow( + '[redi]: It seems that you register "undefined" as dependency on the 1 parameter of "A".' + ) B = class { key = 'b' @@ -428,21 +429,19 @@ describe('core', () => { }) it('[class item] should throw error when a dependency cannot be resolved', () => { - class A {} + class A { } class B { - constructor(_param: string, @Inject(A) private readonly _a: A) {} + constructor(_param: string, @Inject(A) private readonly _a: A) { } } const j = new Injector([[B]]) - expectToThrow(() => { - j.get(B) - }, '[redi]: Cannot find "A" registered by any injector. It is the 1th param of "B".') + expect(() => { j.get(B) }).toThrow('[redi]: Cannot find "A" registered by any injector. It is the 1th param of "B". The stack of dependencies is: "B -> A".'); }) }) describe('instance item', () => { - afterEach(() => cleanupTest()) + it('should just work', () => { const a = { @@ -462,7 +461,7 @@ describe('core', () => { }) describe('factory item', () => { - afterEach(() => cleanupTest()) + it('should just work with zero dep', () => { interface A { @@ -486,7 +485,7 @@ describe('core', () => { }) it('[factory item] should throw error when a dependency cannot be resolved', () => { - class A {} + class A { } interface IB { name: string @@ -499,12 +498,12 @@ describe('core', () => { ]) expectToThrow(() => { j.get(b) - }, '[redi]: Cannot find "A" registered by any injector. It is the 0th param of "b".') + }, '[redi]: Cannot find "A" registered by any injector. It is the 0th param of "b". The stack of dependencies is: "b -> A".') }) }) describe('async item', () => { - afterEach(() => cleanupTest()) + it('should support async loaded ctor', () => new Promise((done) => { @@ -650,7 +649,7 @@ describe('core', () => { it('should "AsyncHook" work', () => new Promise((done) => { class A { - constructor(@Inject(bbI) private bbILoader: AsyncHook) {} + constructor(@Inject(bbI) private bbILoader: AsyncHook) { } public readKey(): Promise { return this.bbILoader.whenReady().then((bb) => bb.key) @@ -685,7 +684,6 @@ describe('core', () => { }) describe('injector', () => { - afterEach(() => cleanupTest()) it('should support inject itself', () => { const a = { @@ -707,7 +705,6 @@ describe('core', () => { }) describe('quantities', () => { - afterEach(() => cleanupTest()) it('should support "Many"', () => { interface A { @@ -725,7 +722,7 @@ describe('core', () => { const aI = createIdentifier('aI') class B { - constructor(@Many(aI) private aS: A[]) {} + constructor(@Many(aI) private aS: A[]) { } get key(): string { return this.aS.map((a) => a.key).join('') + 'b' @@ -761,7 +758,7 @@ describe('core', () => { const aI = createIdentifier('aI') class B { - constructor(@Optional() @aI private a?: A) {} + constructor(@Optional() @aI private a?: A) { } get key(): string { return this.a?.key || 'no a' + 'b' @@ -788,11 +785,11 @@ describe('core', () => { }) it('should throw error when using decorator on a non-injectable parameter', () => { - class A {} + class A { } expectToThrow(() => { class B { - constructor(@Optional() _a: A) {} + constructor(@Optional() _a: A) { } } }, `[redi]: Could not find dependency registered on the 0 (indexed) parameter of the constructor of "B".`) }) @@ -805,7 +802,7 @@ describe('core', () => { const aI = createIdentifier('aI') class B { - constructor(@aI private a: A) {} + constructor(@aI private a: A) { } get key(): string { return this.a?.key || 'no a' + 'b' @@ -826,7 +823,6 @@ describe('core', () => { }) describe('layered injection system', () => { - afterEach(() => cleanupTest()) it('should get dependencies upwards', () => { class A { @@ -840,7 +836,7 @@ describe('core', () => { } class B { - constructor(@Inject(A) private a: A, @Inject(cI) private c: C) {} + constructor(@Inject(A) private a: A, @Inject(cI) private c: C) { } get key() { return this.a.key + 'b' + this.c.key @@ -887,7 +883,7 @@ describe('core', () => { } class D { - constructor(@SkipSelf() @cI private readonly c: C) {} + constructor(@SkipSelf() @cI private readonly c: C) { } get key(): string { return this.c.key + 'd' @@ -951,12 +947,12 @@ describe('core', () => { expectToThrow( () => child.get(bI), - '[redi]: Cannot find "cI" registered by any injector.' + '[redi]: Cannot find "cI" registered by any injector. It is the 1th param of "bI". The stack of dependencies is: "bI".' ) }) it('should throw error when no ancestor injector could provide dependency', () => { - class A {} + class A { } const j = new Injector() @@ -968,12 +964,12 @@ describe('core', () => { }) describe('forwardRef', () => { - afterEach(() => cleanupTest()) + it('should throw Error when forwardRef is not used', () => { expectToThrow(() => { class A { - constructor(@Inject(B) private b: B) {} + constructor(@Inject(B) private b: B) { } get key(): string { return typeof this.b === 'undefined' @@ -990,7 +986,7 @@ describe('core', () => { it('should work when "forwardRef" is used', () => { class A { - constructor(@Inject(forwardRef(() => B)) private b: B) {} + constructor(@Inject(forwardRef(() => B)) private b: B) { } get key(): string { return typeof this.b === 'undefined' ? 'undefined' : 'a' + this.b.key @@ -1015,7 +1011,7 @@ describe('core', () => { } class B { - constructor(@WithNew() @Inject(A) private readonly a: A) {} + constructor(@WithNew() @Inject(A) private readonly a: A) { } get(): number { return this.a.count @@ -1036,7 +1032,7 @@ describe('core', () => { const ICount = createIdentifier('ICount') class B { - constructor(@WithNew() @Inject(ICount) public readonly count: number) {} + constructor(@WithNew() @Inject(ICount) public readonly count: number) { } } const j = new Injector([[B], [ICount, { useFactory: () => c++ }]]) @@ -1050,7 +1046,7 @@ describe('core', () => { }) describe('hooks', () => { - afterEach(() => cleanupTest()) + it('should "onInstantiation" work for class dependencies', () => { interface A { @@ -1076,7 +1072,7 @@ describe('core', () => { } class B { - constructor(@Inject(aI) private a: A) {} + constructor(@Inject(aI) private a: A) { } get key(): string { return this.a.key + 'b' @@ -1140,7 +1136,6 @@ describe('core', () => { }) describe('dispose', () => { - afterEach(() => cleanupTest()) it('should dispose', () => { let flag = false @@ -1151,7 +1146,7 @@ describe('core', () => { } class B implements IDisposable { - constructor(@Inject(A) private readonly a: A) {} + constructor(@Inject(A) private readonly a: A) { } get key(): string { return this.a.key + 'b' @@ -1171,12 +1166,21 @@ describe('core', () => { }) it('should throw error when called after disposing', () => { - class A {} + class A { } const j = new Injector() j.dispose() - expectToThrow(() => j.get(A), '') + expectToThrow(() => j.get(A), 'Injector cannot be accessed after it was disposed.') }) }) + + it('should throw error when identifier has been declared before', () => { + createIdentifier('a') + + expectToThrow( + () => createIdentifier('a'), + '[redi]: Identifier "a" already exists.' + ) + }) }) diff --git a/tsconfig.json b/tsconfig.json index d41521b..9cd0207 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,45 +1,45 @@ { - "compilerOptions": { - "baseUrl": "./", - "jsx": "react", - "lib": [ - "esnext", - "dom" - ], - "sourceMap": true, - "target": "es5", - "outDir": "esm", - "module": "esnext", - "moduleResolution": "node", - "skipLibCheck": true, - "allowSyntheticDefaultImports": true, - "declaration": true, - "noImplicitAny": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noImplicitThis": true, - "noUnusedParameters": true, - "strict": true, - "strictNullChecks": true, - "strictPropertyInitialization": true, - "experimentalDecorators": true, - "esModuleInterop": true, - "types": [ - "node", - ], - "typeRoots": [ - "./node_modules/@types" - ], - "paths": { - "@wendellhu/redi": [ - "./src/index.ts" - ], - "@wendellhu/redi/react-bindings": [ - "./src/react-bindings/index.ts" - ] - } - }, - "include": [ - "src/**/*" - ] + "compilerOptions": { + "baseUrl": "./", + "jsx": "react", + "lib": [ + "esnext", + "dom" + ], + "sourceMap": true, + "target": "es5", + "outDir": "esm", + "module": "esnext", + "moduleResolution": "node", + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "declaration": true, + "noImplicitAny": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedParameters": true, + "strict": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "types": [ + "node", + ], + "typeRoots": [ + "./node_modules/@types" + ], + "paths": { + "@wendellhu/redi": [ + "./src/index.ts" + ], + "@wendellhu/redi/react-bindings": [ + "./src/react-bindings/index.ts" + ] + } + }, + "include": [ + "src/**/*", + ] } \ No newline at end of file