From 7001e0837ad39448e6a170aeea0d7fa2c9926bc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 09:02:13 +0000 Subject: [PATCH 01/24] build(deps-dev): bump rollup-plugin-terser from 6.1.0 to 7.0.2 (#175) --- package-lock.json | 43 +++++++++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 310bf53c..ea22ed0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5351,11 +5351,12 @@ } }, "jest-worker": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.1.0.tgz", - "integrity": "sha512-Z9P5pZ6UC+kakMbNJn+tA2RdVdNX5WH1x+5UCBZ9MxIK24pjYtFt96fK+UwBTrjLYm232g1xz0L3eTh51OW+yQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, "requires": { + "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^7.0.0" } @@ -6519,15 +6520,15 @@ } }, "rollup-plugin-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-6.1.0.tgz", - "integrity": "sha512-4fB3M9nuoWxrwm39habpd4hvrbrde2W2GG4zEGPQg1YITNkM3Tqur5jSuXlWNzbv/2aMLJ+dZJaySc3GCD8oDw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "jest-worker": "^26.0.0", - "serialize-javascript": "^3.0.0", - "terser": "^4.7.0" + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" } }, "rsvp": { @@ -6741,9 +6742,9 @@ "dev": true }, "serialize-javascript": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", - "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -7251,14 +7252,14 @@ } }, "terser": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz", - "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz", + "integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==", "dev": true, "requires": { "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" + "source-map": "~0.7.2", + "source-map-support": "~0.5.19" }, "dependencies": { "commander": { @@ -7266,6 +7267,12 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true } } }, diff --git a/package.json b/package.json index 4ff62a85..12575c8f 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "reflect-metadata": "0.1.13", "rimraf": "3.0.2", "rollup": "^2.23.1", - "rollup-plugin-terser": "^6.1.0", + "rollup-plugin-terser": "^7.0.2", "ts-jest": "^26.4.4", "ts-node": "^8.10.2", "typescript": "^3.9.7" From 8e63311e3b9fff695ea865896d6047d05c70256b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 09:05:03 +0000 Subject: [PATCH 02/24] build(deps-dev): bump @types/jest from 26.0.7 to 26.0.20 (#176) --- package-lock.json | 80 +++++++++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index ea22ed0d..3947280c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1035,15 +1035,27 @@ } }, "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "dev": true, "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" + "chalk": "^4.0.0" + }, + "dependencies": { + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + } } }, "@nodelib/fs.scandir": { @@ -1233,13 +1245,13 @@ } }, "@types/jest": { - "version": "26.0.7", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.7.tgz", - "integrity": "sha512-+x0077/LoN6MjqBcVOe1y9dpryWnfDZ+Xfo3EqGeBcfPRJlQp3Lw62RvNlWxuGv7kOEwlHriAa54updi3Jvvwg==", + "version": "26.0.20", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz", + "integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==", "dev": true, "requires": { - "jest-diff": "^25.2.1", - "pretty-format": "^25.2.1" + "jest-diff": "^26.0.0", + "pretty-format": "^26.0.0" } }, "@types/json-schema": { @@ -1883,9 +1895,9 @@ "dev": true }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -2286,9 +2298,9 @@ "dev": true }, "diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", "dev": true }, "dir-glob": { @@ -4083,15 +4095,15 @@ } }, "jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", "dev": true, "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" } }, "jest-docblock": { @@ -4314,9 +4326,9 @@ } }, "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", "dev": true }, "jest-haste-map": { @@ -6204,15 +6216,15 @@ "dev": true }, "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", "dev": true, "requires": { - "@jest/types": "^25.5.0", + "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "react-is": "^17.0.1" } }, "progress": { @@ -6269,9 +6281,9 @@ } }, "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==", "dev": true }, "read-pkg": { diff --git a/package.json b/package.json index 12575c8f..68a62528 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "devDependencies": { "@rollup/plugin-commonjs": "^15.1.0", "@rollup/plugin-node-resolve": "^8.4.0", - "@types/jest": "^26.0.7", + "@types/jest": "^26.0.20", "@types/node": "^14.14.20", "@typescript-eslint/eslint-plugin": "^3.7.1", "@typescript-eslint/parser": "^3.7.1", From 89177a246af44f8dbb14f580932786ee81fd30a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 09:08:10 +0000 Subject: [PATCH 03/24] build(deps-dev): bump rollup from 2.23.1 to 2.36.1 (#178) --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3947280c..525383c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6523,9 +6523,9 @@ } }, "rollup": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.23.1.tgz", - "integrity": "sha512-Heyl885+lyN/giQwxA8AYT2GY3U+gOlTqVLrMQYno8Z1X9lAOpfXPiKiZCyPc25e9BLJM3Zlh957dpTlO4pa8A==", + "version": "2.36.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.36.1.tgz", + "integrity": "sha512-eAfqho8dyzuVvrGqpR0ITgEdq0zG2QJeWYh+HeuTbpcaXk8vNFc48B7bJa1xYosTCKx0CuW+447oQOW8HgBIZQ==", "dev": true, "requires": { "fsevents": "~2.1.2" diff --git a/package.json b/package.json index 68a62528..260ef8d4 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "prettier": "^2.2.1", "reflect-metadata": "0.1.13", "rimraf": "3.0.2", - "rollup": "^2.23.1", + "rollup": "^2.36.1", "rollup-plugin-terser": "^7.0.2", "ts-jest": "^26.4.4", "ts-node": "^8.10.2", From 99d2ad35d6d2033365446eda11d1765448dbb6bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 09:10:30 +0000 Subject: [PATCH 04/24] build(deps-dev): bump lint-staged from 10.2.11 to 10.5.3 (#177) --- package-lock.json | 159 ++++++++++++---------------------------------- package.json | 2 +- 2 files changed, 41 insertions(+), 120 deletions(-) diff --git a/package-lock.json b/package-lock.json index 525383c7..262dae34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1454,9 +1454,9 @@ "dev": true }, "aggregate-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", - "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, "requires": { "clean-stack": "^2.0.0", @@ -1970,24 +1970,6 @@ "string-width": "^4.2.0" }, "dependencies": { - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, "slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -1998,17 +1980,6 @@ "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } } } }, @@ -2070,9 +2041,9 @@ } }, "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true }, "commondir": { @@ -2121,16 +2092,16 @@ "dev": true }, "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", + "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", - "yaml": "^1.7.2" + "yaml": "^1.10.0" } }, "cross-spawn": { @@ -5521,20 +5492,20 @@ "dev": true }, "lint-staged": { - "version": "10.2.11", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.11.tgz", - "integrity": "sha512-LRRrSogzbixYaZItE2APaS4l2eJMjjf5MbclRZpLJtcQJShcvUzKXsNeZgsLIZ0H0+fg2tL4B59fU9wHIHtFIA==", + "version": "10.5.3", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.3.tgz", + "integrity": "sha512-TanwFfuqUBLufxCc3RUtFEkFraSPNR3WzWcGF39R3f2J7S9+iF9W0KTVLfSy09lYGmZS5NDCxjNvhGMSJyFCWg==", "dev": true, "requires": { - "chalk": "^4.0.0", - "cli-truncate": "2.1.0", - "commander": "^5.1.0", - "cosmiconfig": "^6.0.0", - "debug": "^4.1.1", + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", "dedent": "^0.7.0", - "enquirer": "^2.3.5", - "execa": "^4.0.1", - "listr2": "^2.1.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", "log-symbols": "^4.0.0", "micromatch": "^4.0.2", "normalize-path": "^3.0.0", @@ -5543,20 +5514,19 @@ "stringify-object": "^3.3.0" }, "dependencies": { - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ms": "2.1.2" } }, "execa": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz", - "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -5571,9 +5541,9 @@ } }, "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "requires": { "pump": "^3.0.0" @@ -5597,9 +5567,9 @@ } }, "listr2": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.4.1.tgz", - "integrity": "sha512-8pYsCZCztr5+KAjReLyBeGhLV0vaQ2Du/eMe/ux9QAfQl7efiWejM1IWjALh0zHIRYuIbhQ8N2KztZ4ci56pnQ==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.3.tgz", + "integrity": "sha512-vUb80S2dSUi8YxXahO8/I/s29GqnOL8ozgHVLjfWQXa03BNEeS1TpBLjh2ruaqq5ufx46BRGvfymdBSuoXET5w==", "dev": true, "requires": { "chalk": "^4.1.0", @@ -5608,20 +5578,8 @@ "indent-string": "^4.0.0", "log-update": "^4.0.0", "p-map": "^4.0.0", - "rxjs": "^6.6.0", + "rxjs": "^6.6.3", "through": "^2.3.8" - }, - "dependencies": { - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "locate-path": { @@ -5658,18 +5616,6 @@ "dev": true, "requires": { "chalk": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } } }, "log-update": { @@ -5682,31 +5628,6 @@ "cli-cursor": "^3.1.0", "slice-ansi": "^4.0.0", "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } } }, "magic-string": { @@ -6556,9 +6477,9 @@ "dev": true }, "rxjs": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.0.tgz", - "integrity": "sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==", + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", "dev": true, "requires": { "tslib": "^1.9.0" diff --git a/package.json b/package.json index 260ef8d4..6e57f278 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "eslint-plugin-jest": "^24.1.3", "husky": "^4.3.7", "jest": "^26.6.3", - "lint-staged": "^10.2.11", + "lint-staged": "^10.5.3", "prettier": "^2.2.1", "reflect-metadata": "0.1.13", "rimraf": "3.0.2", From a3f28d41f8f5164c986828515b51e3884c03a351 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 09:12:51 +0000 Subject: [PATCH 05/24] build(deps-dev): bump typescript from 3.9.7 to 4.1.3 (#179) --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 262dae34..f8a0080b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7418,9 +7418,9 @@ } }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", "dev": true }, "union-value": { diff --git a/package.json b/package.json index 6e57f278..e4d70cde 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,6 @@ "rollup-plugin-terser": "^7.0.2", "ts-jest": "^26.4.4", "ts-node": "^8.10.2", - "typescript": "^3.9.7" + "typescript": "^4.1.3" } } From 8287978ab8b7d9efe2ad8ffa9a6e1d4e38c735e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jan 2021 09:02:36 +0000 Subject: [PATCH 06/24] build(deps-dev): bump ts-node from 8.10.2 to 9.1.1 (#180) --- package-lock.json | 13 ++++++++++--- package.json | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f8a0080b..2abe3e55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2104,6 +2104,12 @@ "yaml": "^1.10.0" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7345,12 +7351,13 @@ } }, "ts-node": { - "version": "8.10.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", - "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", "dev": true, "requires": { "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", "source-map-support": "^0.5.17", diff --git a/package.json b/package.json index e4d70cde..f880db61 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "rollup": "^2.36.1", "rollup-plugin-terser": "^7.0.2", "ts-jest": "^26.4.4", - "ts-node": "^8.10.2", + "ts-node": "^9.1.1", "typescript": "^4.1.3" } } From 6b1e31f7967821be5f1571fea968850d7b71f507 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jan 2021 09:05:07 +0000 Subject: [PATCH 07/24] build(deps-dev): bump @rollup/plugin-node-resolve from 8.4.0 to 11.0.1 (#181) --- package-lock.json | 33 +++++++++++++++++++-------------- package.json | 2 +- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2abe3e55..dc8343f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1108,18 +1108,29 @@ } }, "@rollup/plugin-node-resolve": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.4.0.tgz", - "integrity": "sha512-LFqKdRLn0ShtQyf6SBYO69bGE1upV6wUhBX0vFOUnLAyzx5cwp8svA0eHUnu8+YU57XOkrMtfG63QOpQx25pHQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.0.1.tgz", + "integrity": "sha512-ltlsj/4Bhwwhb+Nb5xCz/6vieuEj2/BAkkqVIKmZwC7pIdl8srmgmglE4S0jFlZa32K4qvdQ6NHdmpRKD/LwoQ==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", "@types/resolve": "1.17.1", "builtin-modules": "^3.1.0", - "deep-freeze": "^0.0.1", "deepmerge": "^4.2.2", "is-module": "^1.0.0", - "resolve": "^1.17.0" + "resolve": "^1.19.0" + }, + "dependencies": { + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + } } }, "@rollup/pluginutils": { @@ -1845,9 +1856,9 @@ "dev": true }, "builtin-modules": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", - "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz", + "integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==", "dev": true }, "cache-base": { @@ -2197,12 +2208,6 @@ "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", "dev": true }, - "deep-freeze": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz", - "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=", - "dev": true - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", diff --git a/package.json b/package.json index f880db61..7d63c5a3 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ }, "devDependencies": { "@rollup/plugin-commonjs": "^15.1.0", - "@rollup/plugin-node-resolve": "^8.4.0", + "@rollup/plugin-node-resolve": "^11.0.1", "@types/jest": "^26.0.20", "@types/node": "^14.14.20", "@typescript-eslint/eslint-plugin": "^3.7.1", From c7bf0faa9120ad01e53534704bcf99446ca6c531 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jan 2021 09:07:34 +0000 Subject: [PATCH 08/24] build(deps-dev): bump @typescript-eslint/parser from 3.7.1 to 3.10.1 (#183) --- package-lock.json | 58 ++++++++++++++++++++++++++++++++++++++++++----- package.json | 2 +- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index dc8343f7..06401cbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1353,16 +1353,62 @@ } }, "@typescript-eslint/parser": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.7.1.tgz", - "integrity": "sha512-W4QV/gXvfIsccN8225784LNOorcm7ch68Fi3V4Wg7gmkWSQRKevO4RrRqWo6N/Z/myK1QAiGgeaXN57m+R/8iQ==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.1.tgz", + "integrity": "sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==", "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "3.7.1", - "@typescript-eslint/types": "3.7.1", - "@typescript-eslint/typescript-estree": "3.7.1", + "@typescript-eslint/experimental-utils": "3.10.1", + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/typescript-estree": "3.10.1", "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "@typescript-eslint/experimental-utils": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz", + "integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/typescript-estree": "3.10.1", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + } + }, + "@typescript-eslint/types": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz", + "integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz", + "integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "3.10.1", + "@typescript-eslint/visitor-keys": "3.10.1", + "debug": "^4.1.1", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz", + "integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + } } }, "@typescript-eslint/scope-manager": { diff --git a/package.json b/package.json index 7d63c5a3..3b6a1321 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@types/jest": "^26.0.20", "@types/node": "^14.14.20", "@typescript-eslint/eslint-plugin": "^3.7.1", - "@typescript-eslint/parser": "^3.7.1", + "@typescript-eslint/parser": "^3.10.1", "eslint": "^7.17.0", "eslint-config-prettier": "^7.1.0", "eslint-plugin-jest": "^24.1.3", From e53a4aa1fb5b9facece68c0c31b84ec693233fe3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Jan 2021 09:09:59 +0000 Subject: [PATCH 09/24] build(deps-dev): bump @rollup/plugin-commonjs from 15.1.0 to 17.0.0 (#182) --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 06401cbd..c0833d48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1085,9 +1085,9 @@ } }, "@rollup/plugin-commonjs": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-15.1.0.tgz", - "integrity": "sha512-xCQqz4z/o0h2syQ7d9LskIMvBSH4PX5PjYdpSSvgS+pQik3WahkQVNWg3D8XJeYjZoVWnIUQYDghuEMRGrmQYQ==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.0.0.tgz", + "integrity": "sha512-/omBIJG1nHQc+bgkYDuLpb/V08QyutP9amOrJRUSlYJZP+b/68gM//D8sxJe3Yry2QnYIr3QjR3x4AlxJEN3GA==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", diff --git a/package.json b/package.json index 3b6a1321..000b8a59 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ ] }, "devDependencies": { - "@rollup/plugin-commonjs": "^15.1.0", + "@rollup/plugin-commonjs": "^17.0.0", "@rollup/plugin-node-resolve": "^11.0.1", "@types/jest": "^26.0.20", "@types/node": "^14.14.20", From 61b8c8f9dc27de75889eb34f55ef152cc1a80fe7 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Thu, 14 Jan 2021 05:50:06 +0100 Subject: [PATCH 10/24] build: enable inlineSources option in TS config To prevent various reference errors to non-existent TS files when consuming the package we have to enable source map inlining. This option allows the TS source code to be inlined into the generated sourcemap. --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index 98c4e95a..adedd825 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "rootDirs": ["./src"], "strict": true, "sourceMap": true, + "inlineSources": true, "removeComments": false, "esModuleInterop": true, "experimentalDecorators": true, From a25c98026450468eef37c0c46e775dacab62d97e Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 00:23:07 +0100 Subject: [PATCH 11/24] feat: add option to specify service as eager loaded --- src/container-instance.class.ts | 12 ++++ src/decorators/service.decorator.ts | 5 +- src/interfaces/service-metadata.interface..ts | 6 ++ src/interfaces/service-options.interface.ts | 6 ++ test/eager-loading-services.spec.ts | 66 +++++++++++++++++++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 test/eager-loading-services.spec.ts diff --git a/src/container-instance.class.ts b/src/container-instance.class.ts index b6446881..b5a5906e 100644 --- a/src/container-instance.class.ts +++ b/src/container-instance.class.ts @@ -43,6 +43,7 @@ export class ContainerInstance { get(type: AbstractConstructable): T; get(id: string): T; get(id: Token): T; + get(id: ServiceIdentifier): T; get(identifier: ServiceIdentifier): T { const globalContainer = Container.of(undefined); const globalService = globalContainer.findService(identifier); @@ -101,8 +102,10 @@ export class ContainerInstance { ): this { if (identifierOrServiceMetadata instanceof Array) { identifierOrServiceMetadata.forEach(data => this.set(data)); + return this; } + if (typeof identifierOrServiceMetadata === 'string' || identifierOrServiceMetadata instanceof Token) { return this.set({ id: identifierOrServiceMetadata, @@ -111,6 +114,7 @@ export class ContainerInstance { factory: undefined, global: false, multiple: false, + eager: false, transient: false, }); } @@ -124,6 +128,7 @@ export class ContainerInstance { factory: undefined, global: false, multiple: false, + eager: false, transient: false, }); } @@ -135,16 +140,23 @@ export class ContainerInstance { value: EMPTY_VALUE, global: false, multiple: false, + eager: false, transient: false, ...identifierOrServiceMetadata, }; + const service = this.findService(newService.id); + if (service && service.multiple !== true) { Object.assign(service, newService); } else { this.services.push(newService); } + if (newService.eager) { + this.get(newService.id); + } + return this; } diff --git a/src/decorators/service.decorator.ts b/src/decorators/service.decorator.ts index 27d28a4e..ae538b1f 100644 --- a/src/decorators/service.decorator.ts +++ b/src/decorators/service.decorator.ts @@ -171,8 +171,9 @@ export function Service( }, 'create', ], - global: false, multiple: false, + global: false, + eager: false, transient: false, value: EMPTY_VALUE, }); @@ -186,6 +187,7 @@ export function Service( factory: undefined, multiple: false, global: false, + eager: false, transient: false, value: EMPTY_VALUE, }; @@ -198,6 +200,7 @@ export function Service( service.factory = (optionsOrServiceName as ServiceOptions).factory || undefined; service.multiple = (optionsOrServiceName as ServiceOptions).multiple || false; service.global = (optionsOrServiceName as ServiceOptions).global || false; + service.eager = (optionsOrServiceName as ServiceOptions).eager || false; service.transient = (optionsOrServiceName as ServiceOptions).transient || false; } diff --git a/src/interfaces/service-metadata.interface..ts b/src/interfaces/service-metadata.interface..ts index 631f4efc..8f1be91d 100644 --- a/src/interfaces/service-metadata.interface..ts +++ b/src/interfaces/service-metadata.interface..ts @@ -32,6 +32,12 @@ export interface ServiceMetadata extends ServiceOptions { */ multiple: boolean; + /** + * Indicates whether a new instance should be created as soon as the class is registered. + * By default the registered classes are only instantiated when they are requested from the container. + */ + eager?: boolean; + /** * Factory function used to initialize this service. * Can be regular function ("createCar" for example), diff --git a/src/interfaces/service-options.interface.ts b/src/interfaces/service-options.interface.ts index 9bb2b30b..f73a11a4 100644 --- a/src/interfaces/service-options.interface.ts +++ b/src/interfaces/service-options.interface.ts @@ -34,6 +34,12 @@ export interface ServiceOptions { */ multiple?: boolean; + /** + * Indicates whether a new instance should be created as soon as the class is registered. + * By default the registered classes are only instantiated when they are requested from the container. + */ + eager?: boolean; + /** * Factory used to produce this service. */ diff --git a/test/eager-loading-services.spec.ts b/test/eager-loading-services.spec.ts new file mode 100644 index 00000000..2c6a01b5 --- /dev/null +++ b/test/eager-loading-services.spec.ts @@ -0,0 +1,66 @@ +import 'reflect-metadata'; +import { Container } from '../src/container.class'; +import { Service } from '../src/decorators/service.decorator'; + +describe('Eager loading of services', function () { + beforeEach(() => Container.reset()); + + describe('Container API', () => { + it('should be able to set eager and lazy service with Container API', () => { + let callOrder = 1; + + class MyService { + public createdAt = callOrder++; + } + + Container.set({ id: 'eager-service', type: MyService, eager: true }); + Container.set({ id: 'lazy-service', type: MyService, eager: false }); + + const timeStampBeforeRequests = callOrder++; + + const eagerService = Container.get('eager-service'); + const lazyService = Container.get('lazy-service'); + + /** Both should resolve to an instance of the service. */ + expect(eagerService).toBeInstanceOf(MyService); + expect(lazyService).toBeInstanceOf(MyService); + + /** Eager service should have a lower creation order number than the reference timestamp. */ + /** Lazy service should have a higher creation order number than the reference timestamp. */ + expect(eagerService.createdAt).toBe(1); + expect(timeStampBeforeRequests).toBe(2); + expect(lazyService.createdAt).toBe(3); + }); + }); + + describe('@Service decorator', () => { + it('should be able to set eager and lazy service with @Service decorator', () => { + let callOrder = 1; + + @Service({ eager: true }) + class MyEagerService { + public createdAt = callOrder++; + } + + @Service({ eager: false }) + class MyLazyService { + public createdAt = callOrder++; + } + + const timeStampBeforeRequests = callOrder++; + + const eagerService = Container.get(MyEagerService); + const lazyService = Container.get(MyLazyService); + + /** Both should resolve to an instance of the service. */ + expect(eagerService).toBeInstanceOf(MyEagerService); + expect(lazyService).toBeInstanceOf(MyLazyService); + + /** Eager service should have a lower creation order number than the reference timestamp. */ + /** Lazy service should have a higher creation order number than the reference timestamp. */ + expect(eagerService.createdAt).toBe(1); + expect(timeStampBeforeRequests).toBe(2); + expect(lazyService.createdAt).toBe(3); + }); + }); +}); From 9228bfaa6c91a7b28d464a9bdcec63fb94d6079f Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 01:36:54 +0100 Subject: [PATCH 12/24] refactor: remove `Service([dep], factory)` format The @Service decorator supported being called via the following format: `Service([depA, debB], factoryFn)` where the dep array would have been resolved through TypeDI. This has been removed in favor of `Container.set({ factory })` format --- src/decorators/service.decorator.ts | 198 ++++------------------------ test/decorators/Service.spec.ts | 28 +++- 2 files changed, 45 insertions(+), 181 deletions(-) diff --git a/src/decorators/service.decorator.ts b/src/decorators/service.decorator.ts index ae538b1f..2c322d3c 100644 --- a/src/decorators/service.decorator.ts +++ b/src/decorators/service.decorator.ts @@ -1,128 +1,10 @@ import { Container } from '../container.class'; -import { ContainerInstance } from '../container-instance.class'; import { Token } from '../token.class'; import { ServiceMetadata } from '../interfaces/service-metadata.interface.'; import { ServiceOptions } from '../interfaces/service-options.interface'; import { EMPTY_VALUE } from '../empty.const'; import { Constructable } from '../types/constructable.type'; -export type ObjectType = { new (...args: any[]): T1 } | { service: T1 } | Token; - -export function Service(factory: () => R): { service: R }; -export function Service(dependencies: [ObjectType], factory: (dependency1: T1) => R): { service: R }; -export function Service( - dependencies: [ObjectType, ObjectType], - factory: (dependency1: T1, dependency2: T2) => R -): { service: R }; -export function Service( - dependencies: [ObjectType, ObjectType, ObjectType], - factory: (dependency1: T1, dependency2: T2, dependency3: T3) => R -): { service: R }; -export function Service( - dependencies: [ObjectType, ObjectType, ObjectType, ObjectType], - factory: (dependency1: T1, dependency2: T2, dependency3: T3, dependency4: T4) => R -): { service: R }; -export function Service( - dependencies: [ObjectType, ObjectType, ObjectType, ObjectType, ObjectType], - factory: (dependency1: T1, dependency2: T2, dependency3: T3, dependency4: T4, dependency5: T5) => R -): { service: R }; -export function Service( - dependencies: [ObjectType, ObjectType, ObjectType, ObjectType, ObjectType, ObjectType], - factory: (dependency1: T1, dependency2: T2, dependency3: T3, dependency4: T4, dependency5: T5, dependency6: T6) => R -): { service: R }; -export function Service( - dependencies: [ - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType - ], - factory: ( - dependency1: T1, - dependency2: T2, - dependency3: T3, - dependency4: T4, - dependency5: T5, - dependency6: T6, - dependency7: T7 - ) => R -): { service: R }; -export function Service( - dependencies: [ - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType - ], - factory: ( - dependency1: T1, - dependency2: T2, - dependency3: T3, - dependency4: T4, - dependency5: T5, - dependency6: T6, - dependency7: T7, - dependency8: T8 - ) => R -): { service: R }; -export function Service( - dependencies: [ - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType - ], - factory: ( - dependency1: T1, - dependency2: T2, - dependency3: T3, - dependency4: T4, - dependency5: T5, - dependency6: T6, - dependency7: T7, - dependency8: T8, - dependency9: T9 - ) => R -): { service: R }; -export function Service( - dependencies: [ - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType, - ObjectType - ], - factory: ( - dependency1: T1, - dependency2: T2, - dependency3: T3, - dependency4: T4, - dependency5: T5, - dependency6: T6, - dependency7: T7, - dependency8: T8, - dependency9: T9, - dependency10: T10 - ) => R -): { service: R }; - /** * Marks class as a service that can be injected using Container. */ @@ -136,75 +18,43 @@ export function Service(name: string): Function; /** * Marks class as a service that can be injected using Container. */ -export function Service(token: Token): Function; +export function Service(token: Token): Function; /** * Marks class as a service that can be injected using Container. */ -export function Service(options?: ServiceOptions): Function; +export function Service(options?: ServiceOptions): Function; /** * Marks class as a service that can be injected using container. */ -export function Service( - optionsOrServiceName?: ServiceOptions | Token | string | any[] | (() => any), - maybeFactory?: (...args: any[]) => any -): any { - if (arguments.length === 2 || typeof optionsOrServiceName === 'function') { - const serviceId = new Token(); - const dependencies = arguments.length === 2 ? (optionsOrServiceName as any[]) : []; - const factory = arguments.length === 2 ? maybeFactory : (optionsOrServiceName as Function); - - if (factory === undefined) { - // TODO: This should never happen, but regardless, lets re-think this bit. - throw Error('Ohh no.'); - } - - Container.set({ - id: serviceId, - factory: [ - class DefaultFactory { - create(container: ContainerInstance) { - const params = dependencies.map(dependency => container.get(dependency)); - return factory(...params); - } - }, - 'create', - ], +export function Service(optionsOrServiceIdentifier?: ServiceOptions | Token | string): ClassDecorator { + return targetConstructor => { + const serviceMetadata: ServiceMetadata = { + id: targetConstructor, + // TODO: Let's investigate why we receive Function type instead of a constructable. + type: (targetConstructor as unknown) as Constructable, + factory: undefined, multiple: false, global: false, eager: false, transient: false, value: EMPTY_VALUE, - }); - - return serviceId; - } else { - return function (target: Constructable) { - const service: ServiceMetadata = { - id: target, - type: target, - factory: undefined, - multiple: false, - global: false, - eager: false, - transient: false, - value: EMPTY_VALUE, - }; + }; - if (typeof optionsOrServiceName === 'string' || optionsOrServiceName instanceof Token) { - service.id = optionsOrServiceName; - } else if (optionsOrServiceName) { - // ServiceOptions - service.id = (optionsOrServiceName as ServiceOptions).id || target; - service.factory = (optionsOrServiceName as ServiceOptions).factory || undefined; - service.multiple = (optionsOrServiceName as ServiceOptions).multiple || false; - service.global = (optionsOrServiceName as ServiceOptions).global || false; - service.eager = (optionsOrServiceName as ServiceOptions).eager || false; - service.transient = (optionsOrServiceName as ServiceOptions).transient || false; - } + if (optionsOrServiceIdentifier instanceof Token || typeof optionsOrServiceIdentifier === 'string') { + /** We received a Token or string ID. */ + serviceMetadata.id = optionsOrServiceIdentifier; + } else if (optionsOrServiceIdentifier) { + /** We received a ServiceOptions object. */ + serviceMetadata.id = optionsOrServiceIdentifier.id || targetConstructor; + serviceMetadata.factory = optionsOrServiceIdentifier.factory || undefined; + serviceMetadata.multiple = optionsOrServiceIdentifier.multiple || false; + serviceMetadata.global = optionsOrServiceIdentifier.global || false; + serviceMetadata.eager = optionsOrServiceIdentifier.eager || false; + serviceMetadata.transient = optionsOrServiceIdentifier.transient || false; + } - Container.set(service); - }; - } + Container.set(serviceMetadata); + }; } diff --git a/test/decorators/Service.spec.ts b/test/decorators/Service.spec.ts index 3e9712dd..1fa9dd21 100644 --- a/test/decorators/Service.spec.ts +++ b/test/decorators/Service.spec.ts @@ -164,14 +164,28 @@ describe('Service Decorator', function () { }); it('should support function injection with Token dependencies', function () { - const token: Token = new Token('token'); - - Container.set(token, 'test_string'); - - const TestService = Service([token], function (s: string): string { - return s.toUpperCase(); + const myToken: Token = new Token('myToken'); + + Container.set(myToken, 'test_string'); + Container.set({ + id: 'my-service-A', + factory: function myServiceFactory(container): string { + return container.get(myToken).toUpperCase(); + }, }); - expect(Container.get(TestService as any)).toBe('TEST_STRING'); + /** + * This is an unusual format and not officially supported, but it should work. + * We can set null as the target, because we have set the ID manually, so it won't be used. + */ + Service({ + id: 'my-service-B', + factory: function myServiceFactory(container): string { + return container.get(myToken).toUpperCase(); + }, + })(null); + + expect(Container.get('my-service-A')).toBe('TEST_STRING'); + expect(Container.get('my-service-B')).toBe('TEST_STRING'); }); }); From e4892ce793409cde18c0fa839dded4ad53612ec2 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 01:50:22 +0100 Subject: [PATCH 13/24] docs: add partial changelog for next release --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a7fda8e..3f6825d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,45 @@ # Changelog +## 0.10.0 [BREAKING] - [NOT RELEASED] + +### BREAKING CHANGES + +#### Removed support for calling `Service([depA, depB], factory)` + +This was an undocumented way of calling the `Service` function directly instead of using it as a decorator. This option +has been removed and the official supported way of achieving the same is with `Container.set`. Example: + +```ts +const myToken = new Token('myToken'); + +Container.set(myToken, 'test-value'); + +// Old format: +const oldWayService = Service([myToken], function myFactory(myToken) { + return myToken.toUpperCase(); +}); +const oldResult = Container.get(oldWayService); +// New format +const newWayService = Container.set({ + // ID can be anything, we use string for simplicity + id: 'my-custom-service', + factory: function myFactory(container) { + return container.get(myToken).toUppserCase(); + }, +}); +const newResult = Container.get('my-custom-service'); + +oldResult === newResult; // -> true, both equals to "TEST-VALUE" +``` + +### Added + +- added `eager` option to `ServiceOptions`, when enabled the class will be instantiated as soon as it's registered in the container + +### Changed + +- removed old, undocumented way of calling `@Service` decorator directly + ## 0.9.1 - 2021.01.11 ### Fixed From 565b9e910f5ce7352f853ce6ea5ecaccf7932955 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 03:12:51 +0100 Subject: [PATCH 14/24] refactor: rework internal instantiation process --- src/container-instance.class.ts | 107 +++++++++--------- ...r.ts => cannot-instantiate-value.error.ts} | 14 ++- src/index.ts | 9 ++ 3 files changed, 71 insertions(+), 59 deletions(-) rename src/error/{missing-provided-service-type.error.ts => cannot-instantiate-value.error.ts} (62%) diff --git a/src/container-instance.class.ts b/src/container-instance.class.ts index b5a5906e..e55b5384 100644 --- a/src/container-instance.class.ts +++ b/src/container-instance.class.ts @@ -1,6 +1,6 @@ import { Container } from './container.class'; -import { MissingProvidedServiceTypeError } from './error/missing-provided-service-type.error'; import { ServiceNotFoundError } from './error/service-not-found.error'; +import { CannotInstantiateValueError } from './error/cannot-instantiate-value.error'; import { Token } from './token.class'; import { Constructable } from './types/constructable.type'; import { AbstractConstructable } from './types/abstract-constructable.type'; @@ -236,92 +236,91 @@ export class ContainerInstance { } /** - * Gets service value. + * Gets the value belonging to `serviceMetadata.id`. + * + * - if `serviceMetadata.value` is already set it is immediately returned + * - otherwise the requested type is resolved to the value saved to `serviceMetadata.value` and returned */ private getServiceValue(serviceMetadata: ServiceMetadata): any { - // find if instance of this object already initialized in the container and return it if it is + let value: unknown = EMPTY_VALUE; + /** - * If the service value has been set to anything prior to this call we return the set value. + * If the service value has been set to anything prior to this call we return that value. * NOTE: This part builds on the assumption that transient dependencies has no value set ever. */ if (serviceMetadata.value !== EMPTY_VALUE) { return serviceMetadata.value; } - // if named service was requested and its instance was not found plus there is not type to know what to initialize, - // this means service was not pre-registered and we throw an exception - if ( - (!serviceMetadata || !serviceMetadata.type) && - (!serviceMetadata || !serviceMetadata.factory) && - (typeof serviceMetadata.id === 'string' || serviceMetadata.id instanceof Token) - ) - throw new ServiceNotFoundError(serviceMetadata.id); - - // at this point we either have type in service registered, either identifier is a target type - let construtableType: Constructable | undefined; - - if (serviceMetadata && serviceMetadata.type) { - construtableType = serviceMetadata.type; - } else if (typeof serviceMetadata.id === 'function') { - construtableType = serviceMetadata.id as Constructable; + /** If both factory and type is missing, we cannot resolve the requested ID. */ + if (!serviceMetadata.factory && !serviceMetadata.type) { + throw new CannotInstantiateValueError(serviceMetadata.id); } - // setup constructor parameters for a newly initialized service - const paramTypes = - construtableType && Reflect && (Reflect as any).getMetadata - ? (Reflect as any).getMetadata('design:paramtypes', construtableType) - : undefined; - // TODO: remove as any, here it cannot be undefined but TS doesn't see this. - let params: any[] = paramTypes ? this.initializeParams(construtableType as any, paramTypes) : []; - - // if factory is set then use it to create service instance - let value: any; + /** + * If a factory is defined it takes priority over creating an instance via `new`. + * The return value of the factory is not checked, we believe by design that the user knows what he/she is doing. + */ if (serviceMetadata.factory) { - // filter out non-service parameters from created service constructor - // non-service parameters can be, lets say Car(name: string, isNew: boolean, engine: Engine) - // where name and isNew are non-service parameters and engine is a service parameter - params = params.filter(param => param !== undefined); - + /** + * If we received the factory in the [Constructable, "functionName"] format, we need to create the + * factory first and then call the specified function on it. + */ if (serviceMetadata.factory instanceof Array) { - // use special [Type, "create"] syntax to allow factory services - // in this case Type instance will be obtained from Container and its method "create" will be called let factoryInstance; + try { + /** Try to get the factory from TypeDI first, if failed, fall back to simply initiating the class. */ factoryInstance = this.get(serviceMetadata.factory[0]); } catch (error) { if (error instanceof ServiceNotFoundError) { factoryInstance = new serviceMetadata.factory[0](); + } else { + throw error; } } - // TODO: rethink this - // We need to pass `this` to make the following test pass: - // "should support function injection with Token dependencies" - // We should have the dependencies here, instead of passing the container to the factory. - // Maybe we should save the dependency ID list on the meta-data? - value = factoryInstance[serviceMetadata.factory[1]](...params, this); + value = factoryInstance[serviceMetadata.factory[1]](this, serviceMetadata.id); } else { - // regular factory function - value = serviceMetadata.factory(...params, this); + /** If only a simple function was provided we simply call it. */ + value = serviceMetadata.factory(this, serviceMetadata.id); } - } else { - // TODO: Commented it out as this makes no sense. - // params.unshift(null); + } + + /** + * If no factory was provided and only then, we create the instance from the type if it was set. + */ + if (!serviceMetadata.factory && serviceMetadata.type) { + const constructableTargetType: Constructable = serviceMetadata.type; + // setup constructor parameters for a newly initialized service + const paramTypes = (Reflect as any)?.getMetadata('design:paramtypes', constructableTargetType) || []; + const params = this.initializeParams(constructableTargetType, paramTypes); // "extra feature" - always pass container instance as the last argument to the service function // this allows us to support javascript where we don't have decorators and emitted metadata about dependencies // need to be injected, and user can use provided container to get instances he needs params.push(this); - if (!construtableType) throw new MissingProvidedServiceTypeError(serviceMetadata.id); + value = new constructableTargetType(...params); - // eslint-disable-next-line prefer-spread - value = new construtableType(...params); + // TODO: Calling this here, leads to infinite loop, because @Inject decorator registerds a handler + // TODO: which calls Container.get, which will check if the requested type has a value set and if not + // TODO: it will start the instantiation process over. So this is currently called outside of the if branch + // TODO: after the current value has been assigned to the serviceMetadata. + // this.applyPropertyHandlers(constructableTargetType, value as Constructable); + } else { + /** This branch should never execute, but better to be safe than sorry. */ + throw new CannotInstantiateValueError(serviceMetadata.id); } - if (serviceMetadata && !serviceMetadata.transient && value) serviceMetadata.value = value; + /** If this is not a transient service, and we resolved something, then we set it as the value. */ + if (!serviceMetadata.transient && value !== EMPTY_VALUE) { + serviceMetadata.value = value; + } - if (construtableType) this.applyPropertyHandlers(construtableType, value); + if (serviceMetadata.type) { + this.applyPropertyHandlers(serviceMetadata.type, value as Record); + } return value; } @@ -329,7 +328,7 @@ export class ContainerInstance { /** * Initializes all parameter types for a given target service class. */ - private initializeParams(target: Function, paramTypes: any[]): any[] { + private initializeParams(target: Function, paramTypes: any[]): unknown[] { return paramTypes.map((paramType, index) => { const paramHandler = Container.handlers.find(handler => { /** diff --git a/src/error/missing-provided-service-type.error.ts b/src/error/cannot-instantiate-value.error.ts similarity index 62% rename from src/error/missing-provided-service-type.error.ts rename to src/error/cannot-instantiate-value.error.ts index a540c61f..d382b090 100644 --- a/src/error/missing-provided-service-type.error.ts +++ b/src/error/cannot-instantiate-value.error.ts @@ -1,22 +1,26 @@ -import { Token } from '../token.class'; import { ServiceIdentifier } from '../types/service-identifier.type'; +import { Token } from '../token.class'; /** - * Thrown when service is registered without type. + * Thrown when DI cannot inject value into property decorated by @Inject decorator. */ -export class MissingProvidedServiceTypeError extends Error { - public name = 'MissingProvidedServiceTypeError'; +export class CannotInstantiateValueError extends Error { + public name = 'CannotInstantiateValueError'; /** Normalized identifier name used in the error message. */ private normalizedIdentifier: string = ''; get message(): string { - return `Cannot determine a class of the requesting service: "${this.normalizedIdentifier}".`; + return ( + `Cannot instantiate the requested value for the "${this.normalizedIdentifier}" identifier. ` + + `The related metadata doesn't contain a factory or a type to instantiate.` + ); } constructor(identifier: ServiceIdentifier) { super(); + // TODO: Extract this to a helper function and share between this and NotFoundError. if (typeof identifier === 'string') { this.normalizedIdentifier = identifier; } else if (identifier instanceof Token) { diff --git a/src/index.ts b/src/index.ts index 86745db9..6ea7ad54 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,12 @@ +/** + * We have a hard dependency on reflect-metadata package. + * Without the dependency lookup wont work. So we should warn the users + * when it's not loaded. + */ +// if(!Reflect || !(Reflect as any).getMetadata) { +// throw new Error('Reflect.getMetadata is not a function. Please import the "reflect-metadata" package at the first line of your application.'); +// } + import { Container } from './container.class'; export * from './decorators/inject-many.decorator'; From 8afb78e0f0c4aac9842c17b1e5fd836a5d0111a2 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 03:30:10 +0100 Subject: [PATCH 15/24] fix: throw CannotInstantiateValueError in the correct place --- src/container-instance.class.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/container-instance.class.ts b/src/container-instance.class.ts index e55b5384..bec9761f 100644 --- a/src/container-instance.class.ts +++ b/src/container-instance.class.ts @@ -308,9 +308,6 @@ export class ContainerInstance { // TODO: it will start the instantiation process over. So this is currently called outside of the if branch // TODO: after the current value has been assigned to the serviceMetadata. // this.applyPropertyHandlers(constructableTargetType, value as Constructable); - } else { - /** This branch should never execute, but better to be safe than sorry. */ - throw new CannotInstantiateValueError(serviceMetadata.id); } /** If this is not a transient service, and we resolved something, then we set it as the value. */ @@ -318,6 +315,11 @@ export class ContainerInstance { serviceMetadata.value = value; } + if (value === EMPTY_VALUE) { + /** This branch should never execute, but better to be safe than sorry. */ + throw new CannotInstantiateValueError(serviceMetadata.id); + } + if (serviceMetadata.type) { this.applyPropertyHandlers(serviceMetadata.type, value as Record); } From 90c4f7e9b8db3475faad3c7dd8933574608848d8 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 03:40:55 +0100 Subject: [PATCH 16/24] fix: export CannotInstantiateValueError correctly --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 6ea7ad54..006bd8d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,7 @@ export * from './decorators/inject.decorator'; export * from './decorators/service.decorator'; export * from './error/cannot-inject-value.error'; -export * from './error/missing-provided-service-type.error'; +export * from './error/cannot-instantiate-value.error'; export * from './error/service-not-found.error'; export { Handler } from './interfaces/handler.interface'; From bef90db772c810c01c2c3c873564d6889c8625b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 15 Jan 2021 09:02:17 +0000 Subject: [PATCH 17/24] build(deps-dev): bump @types/node from 14.14.20 to 14.14.21 (#184) --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c0833d48..83fe52c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1272,9 +1272,9 @@ "dev": true }, "@types/node": { - "version": "14.14.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.20.tgz", - "integrity": "sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A==", + "version": "14.14.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.21.tgz", + "integrity": "sha512-cHYfKsnwllYhjOzuC5q1VpguABBeecUp24yFluHpn/BQaVxB1CuQ1FSRZCzrPxrkIfWISXV2LbeoBthLWg0+0A==", "dev": true }, "@types/normalize-package-data": { diff --git a/package.json b/package.json index 000b8a59..b776270f 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@rollup/plugin-commonjs": "^17.0.0", "@rollup/plugin-node-resolve": "^11.0.1", "@types/jest": "^26.0.20", - "@types/node": "^14.14.20", + "@types/node": "^14.14.21", "@typescript-eslint/eslint-plugin": "^3.7.1", "@typescript-eslint/parser": "^3.10.1", "eslint": "^7.17.0", From 75689426e476252ef58475b74753bf9ad99fcf6f Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 14:04:28 +0100 Subject: [PATCH 18/24] fix: infer types properly when empty @Inject() decorator is used BREAKING CHANGE: Services with eager types will fail early if requested type doesn't exist. --- src/container-instance.class.ts | 9 ++-- src/decorators/inject-many.decorator.ts | 49 +++++++++----------- src/decorators/inject.decorator.ts | 51 ++++++++++----------- src/utils/resolve-to-identifier.util.ts | 59 +++++++++++++++++++++++++ test/decorators/Inject.spec.ts | 15 +++++++ test/github-issues/42/issue-42.spec.ts | 23 +++++----- 6 files changed, 137 insertions(+), 69 deletions(-) create mode 100644 src/utils/resolve-to-identifier.util.ts diff --git a/src/container-instance.class.ts b/src/container-instance.class.ts index bec9761f..6da03d29 100644 --- a/src/container-instance.class.ts +++ b/src/container-instance.class.ts @@ -79,10 +79,13 @@ export class ContainerInstance { * Gets all instances registered in the container of the given service identifier. * Used when service defined with multiple: true flag. */ + getMany(type: Constructable): T[]; + getMany(type: AbstractConstructable): T[]; getMany(id: string): T[]; getMany(id: Token): T[]; - getMany(id: string | Token): T[] { - return this.filterServices(id).map(service => this.getServiceValue(service)); + getMany(id: ServiceIdentifier): T[]; + getMany(identifier: ServiceIdentifier): T[] { + return this.filterServices(identifier).map(service => this.getServiceValue(service)); } /** @@ -212,7 +215,7 @@ export class ContainerInstance { /** * Finds registered service in the with a given service identifier. */ - private findService(identifier: ServiceIdentifier): ServiceMetadata | undefined { + private findService(identifier: ServiceIdentifier): ServiceMetadata | undefined { return this.services.find(service => { if (service.id) { if ( diff --git a/src/decorators/inject-many.decorator.ts b/src/decorators/inject-many.decorator.ts index e626958f..eb56a1a2 100644 --- a/src/decorators/inject-many.decorator.ts +++ b/src/decorators/inject-many.decorator.ts @@ -1,46 +1,41 @@ import { Container } from '../container.class'; import { Token } from '../token.class'; import { CannotInjectValueError } from '../error/cannot-inject-value.error'; +import { resolveToTypeWrapper } from '../utils/resolve-to-identifier.util'; +import { Constructable } from '../types/constructable.type'; +import { ServiceIdentifier } from '../types/service-identifier.type'; /** - * Injects a service into a class property or constructor parameter. + * Injects a list of services into a class property or constructor parameter. */ +export function InjectMany(): Function; export function InjectMany(type?: (type?: any) => Function): Function; - -/** - * Injects a service into a class property or constructor parameter. - */ export function InjectMany(serviceName?: string): Function; - -/** - * Injects a service into a class property or constructor parameter. - */ export function InjectMany(token: Token): Function; +export function InjectMany( + typeOrIdentifier?: ((type?: never) => Constructable) | ServiceIdentifier +): Function { + return function (target: Object, propertyName: string | Symbol, index?: number): void { + const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index); -/** - * Injects a service into a class property or constructor parameter. - */ -export function InjectMany(typeOrName?: ((type?: any) => Function) | string | Token): Function { - return function (target: any, propertyName: string, index?: number) { - if (!typeOrName) typeOrName = () => (Reflect as any).getMetadata('design:type', target, propertyName); + /** If no type was inferred, or the general Object type was inferred we throw an error. */ + if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) { + throw new CannotInjectValueError(target as Constructable, propertyName as string); + } Container.registerHandler({ - object: target, - propertyName: propertyName, + object: target as Constructable, + propertyName: propertyName as string, index: index, value: containerInstance => { - let identifier: any; - if (typeof typeOrName === 'string') { - identifier = typeOrName; - } else if (typeOrName instanceof Token) { - identifier = typeOrName; - } else { - identifier = typeOrName(); - } + const evaluatedLazyType = typeWrapper.lazyType(); - if (identifier === Object) throw new CannotInjectValueError(target, propertyName); + /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */ + if (evaluatedLazyType === undefined || evaluatedLazyType === Object) { + throw new CannotInjectValueError(target as Constructable, propertyName as string); + } - return containerInstance.getMany(identifier); + return containerInstance.getMany(evaluatedLazyType); }, }); }; diff --git a/src/decorators/inject.decorator.ts b/src/decorators/inject.decorator.ts index 38b51a09..137c52d4 100644 --- a/src/decorators/inject.decorator.ts +++ b/src/decorators/inject.decorator.ts @@ -1,46 +1,41 @@ import { Container } from '../container.class'; import { Token } from '../token.class'; import { CannotInjectValueError } from '../error/cannot-inject-value.error'; +import { ServiceIdentifier } from '../types/service-identifier.type'; +import { Constructable } from '../types/constructable.type'; +import { resolveToTypeWrapper } from '../utils/resolve-to-identifier.util'; /** * Injects a service into a class property or constructor parameter. */ -export function Inject(type?: (type?: any) => Function): Function; - -/** - * Injects a service into a class property or constructor parameter. - */ +export function Inject(): Function; +export function Inject(typeFn: (type?: never) => Constructable): Function; export function Inject(serviceName?: string): Function; +export function Inject(token: Token): Function; +export function Inject( + typeOrIdentifier?: ((type?: never) => Constructable) | ServiceIdentifier +): ParameterDecorator | PropertyDecorator { + return function (target: Object, propertyName: string | Symbol, index?: number): void { + const typeWrapper = resolveToTypeWrapper(typeOrIdentifier, target, propertyName, index); -/** - * Injects a service into a class property or constructor parameter. - */ -export function Inject(token: Token): Function; - -/** - * Injects a service into a class property or constructor parameter. - */ -export function Inject(typeOrName?: ((type?: any) => Function) | string | Token): Function { - return function (target: any, propertyName: string, index?: number) { - if (!typeOrName) typeOrName = () => (Reflect as any).getMetadata('design:type', target, propertyName); + /** If no type was inferred, or the general Object type was inferred we throw an error. */ + if (typeWrapper === undefined || typeWrapper.eagerType === undefined || typeWrapper.eagerType === Object) { + throw new CannotInjectValueError(target as Constructable, propertyName as string); + } Container.registerHandler({ - object: target, - propertyName: propertyName, + object: target as Constructable, + propertyName: propertyName as string, index: index, value: containerInstance => { - let identifier: any; - if (typeof typeOrName === 'string') { - identifier = typeOrName; - } else if (typeOrName instanceof Token) { - identifier = typeOrName; - } else { - identifier = typeOrName(); - } + const evaluatedLazyType = typeWrapper.lazyType(); - if (identifier === Object) throw new CannotInjectValueError(target, propertyName); + /** If no type was inferred lazily, or the general Object type was inferred we throw an error. */ + if (evaluatedLazyType === undefined || evaluatedLazyType === Object) { + throw new CannotInjectValueError(target as Constructable, propertyName as string); + } - return containerInstance.get(identifier); + return containerInstance.get(evaluatedLazyType); }, }); }; diff --git a/src/utils/resolve-to-identifier.util.ts b/src/utils/resolve-to-identifier.util.ts new file mode 100644 index 00000000..6ceb88ac --- /dev/null +++ b/src/utils/resolve-to-identifier.util.ts @@ -0,0 +1,59 @@ +import { Token } from '../token.class'; +import { Constructable } from '../types/constructable.type'; +import { ServiceIdentifier } from '../types/service-identifier.type'; + +/** + * Helper function used in inject decorators to resolve the received identifier to + * an eager type when possible or to a lazy type when cyclic dependencies are possibly involved. + * + * @param typeOrIdentifier a service identifier or a function returning a type acting as service identifier or nothing + * @param target the class definition of the target of the decorator + * @param propertyName the name of the property in case of a PropertyDecorator + * @param index the index of the parameter in the constructor in case of ParameterDecorator + */ +export function resolveToTypeWrapper( + typeOrIdentifier: ((type?: never) => Constructable) | ServiceIdentifier | undefined, + target: Object, + propertyName: string | Symbol, + index?: number +): { eagerType: ServiceIdentifier | null; lazyType: (type?: never) => ServiceIdentifier } { + /** + * ? We want to error out as soon as possible when looking up services to inject, however + * ? we cannot determine the type at decorator execution when cyclic dependencies are involved + * ? because calling the received `() => MyType` function right away would cause a JS error: + * ? "Cannot access 'MyType' before initialization", so we need to execute the function in the handler, + * ? when the classes are already created. To overcome this, we use a wrapper: + * ? - the lazyType is executed in the handler so we never have a JS error + * ? - the eagerType is checked when decorator is running and an error is raised if an unknown type is encountered + */ + let typeWrapper!: { eagerType: ServiceIdentifier | null; lazyType: (type?: never) => ServiceIdentifier }; + + /** If requested type is explicitly set via a string ID or token, we set it explicitly. */ + if ((typeOrIdentifier && typeof typeOrIdentifier === 'string') || typeOrIdentifier instanceof Token) { + typeWrapper = { eagerType: typeOrIdentifier, lazyType: () => typeOrIdentifier }; + } + + /** If requested type is explicitly set via a () => MyClassType format, we set it explicitly. */ + if (typeOrIdentifier && typeof typeOrIdentifier === 'function') { + /** We set eagerType to null, preventing the raising of the CannotInjectValueError in decorators. */ + typeWrapper = { eagerType: null, lazyType: () => (typeOrIdentifier as CallableFunction)() }; + } + + /** If no explicit type is set and handler registered for a class property, we need to get the property type. */ + if (!typeOrIdentifier && propertyName) { + const identifier = (Reflect as any).getMetadata('design:type', target, propertyName); + + typeWrapper = { eagerType: identifier, lazyType: () => identifier }; + } + + /** If no explicit type is set and handler registered for a constructor parameter, we need to get the parameter types. */ + if (!typeOrIdentifier && typeof index == 'number' && Number.isInteger(index)) { + const paramTypes: ServiceIdentifier[] = (Reflect as any).getMetadata('design:paramtypes', target, propertyName); + /** It's not guaranteed, that we find any types for the constructor. */ + const identifier = paramTypes?.[index]; + + typeWrapper = { eagerType: identifier, lazyType: () => identifier }; + } + + return typeWrapper; +} diff --git a/test/decorators/Inject.spec.ts b/test/decorators/Inject.spec.ts index d88dbc2a..6f5d191b 100644 --- a/test/decorators/Inject.spec.ts +++ b/test/decorators/Inject.spec.ts @@ -92,4 +92,19 @@ describe('Inject Decorator', function () { expect(carNames).toContain('Mercedes'); expect(carNames).toContain('Toyota'); }); + + it('should work with empty decorator on constructor parameter', function () { + @Service() + class InjectedClass {} + + @Service() + class TestService { + constructor(@Inject() public myClass: InjectedClass) {} + } + + const instance = Container.get(TestService); + + expect(instance).toBeInstanceOf(TestService); + expect(instance.myClass).toBeInstanceOf(InjectedClass); + }); }); diff --git a/test/github-issues/42/issue-42.spec.ts b/test/github-issues/42/issue-42.spec.ts index d30126bb..b6f0b710 100644 --- a/test/github-issues/42/issue-42.spec.ts +++ b/test/github-issues/42/issue-42.spec.ts @@ -2,6 +2,7 @@ import 'reflect-metadata'; import { Container } from '../../../src/container.class'; import { Service } from '../../../src/decorators/service.decorator'; import { Inject } from '../../../src/decorators/inject.decorator'; +import { CannotInjectValueError } from '../../../src/error/cannot-inject-value.error'; describe('github issues > #42 Exception not thrown on missing binding', function () { beforeEach(() => Container.reset()); @@ -11,18 +12,18 @@ describe('github issues > #42 Exception not thrown on missing binding', function create(): void; } - @Service() - class CoffeeMaker { - @Inject() // This is an incorrect usage of typedi because Factory is an interface - private factory: Factory; + expect(() => { + @Service() + class CoffeeMaker { + @Inject() // This is an incorrect usage of TypeDI because Factory is an interface + private factory: Factory; - make() { - this.factory.create(); + make() { + this.factory.create(); + } } - } - - expect(() => { - Container.get(CoffeeMaker); - }).toThrowError(Error); + // We doesn't even need to call `Container.get(CoffeeMaker);`, TypeDI will detect this error, while + // the JS code is parsed and decorators are executed. + }).toThrowError(CannotInjectValueError); }); }); From 4c6ee029414500adbbed1c534b73ff4cfb1686b9 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 14:13:17 +0100 Subject: [PATCH 19/24] refactor: collapse function overload on Service decorator --- src/decorators/service.decorator.ts | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/decorators/service.decorator.ts b/src/decorators/service.decorator.ts index 2c322d3c..c42e7969 100644 --- a/src/decorators/service.decorator.ts +++ b/src/decorators/service.decorator.ts @@ -8,26 +8,10 @@ import { Constructable } from '../types/constructable.type'; /** * Marks class as a service that can be injected using Container. */ -export function Service(): Function; - -/** - * Marks class as a service that can be injected using Container. - */ -export function Service(name: string): Function; - -/** - * Marks class as a service that can be injected using Container. - */ +export function Service(): Function; +export function Service(name: string): Function; export function Service(token: Token): Function; - -/** - * Marks class as a service that can be injected using Container. - */ export function Service(options?: ServiceOptions): Function; - -/** - * Marks class as a service that can be injected using container. - */ export function Service(optionsOrServiceIdentifier?: ServiceOptions | Token | string): ClassDecorator { return targetConstructor => { const serviceMetadata: ServiceMetadata = { From 6be83e63491fe3cd7c1d100e3ef86991a29022a0 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 14:14:25 +0100 Subject: [PATCH 20/24] refactor: change filename of helper function to it's definition name --- src/decorators/inject-many.decorator.ts | 2 +- src/decorators/inject.decorator.ts | 2 +- ...ve-to-identifier.util.ts => resolve-to-type-wrapper.util.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/utils/{resolve-to-identifier.util.ts => resolve-to-type-wrapper.util.ts} (100%) diff --git a/src/decorators/inject-many.decorator.ts b/src/decorators/inject-many.decorator.ts index eb56a1a2..21dab4b2 100644 --- a/src/decorators/inject-many.decorator.ts +++ b/src/decorators/inject-many.decorator.ts @@ -1,7 +1,7 @@ import { Container } from '../container.class'; import { Token } from '../token.class'; import { CannotInjectValueError } from '../error/cannot-inject-value.error'; -import { resolveToTypeWrapper } from '../utils/resolve-to-identifier.util'; +import { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util'; import { Constructable } from '../types/constructable.type'; import { ServiceIdentifier } from '../types/service-identifier.type'; diff --git a/src/decorators/inject.decorator.ts b/src/decorators/inject.decorator.ts index 137c52d4..9c26b3c6 100644 --- a/src/decorators/inject.decorator.ts +++ b/src/decorators/inject.decorator.ts @@ -3,7 +3,7 @@ import { Token } from '../token.class'; import { CannotInjectValueError } from '../error/cannot-inject-value.error'; import { ServiceIdentifier } from '../types/service-identifier.type'; import { Constructable } from '../types/constructable.type'; -import { resolveToTypeWrapper } from '../utils/resolve-to-identifier.util'; +import { resolveToTypeWrapper } from '../utils/resolve-to-type-wrapper.util'; /** * Injects a service into a class property or constructor parameter. diff --git a/src/utils/resolve-to-identifier.util.ts b/src/utils/resolve-to-type-wrapper.util.ts similarity index 100% rename from src/utils/resolve-to-identifier.util.ts rename to src/utils/resolve-to-type-wrapper.util.ts From b6f9188e865f2b84734440e1ff0f3097dbc8e495 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 14:46:01 +0100 Subject: [PATCH 21/24] refactor: simplyfy ContainerInstance implementation --- src/container-instance.class.ts | 46 +++++++-------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/src/container-instance.class.ts b/src/container-instance.class.ts index 6da03d29..42e2a422 100644 --- a/src/container-instance.class.ts +++ b/src/container-instance.class.ts @@ -85,7 +85,7 @@ export class ContainerInstance { getMany(id: Token): T[]; getMany(id: ServiceIdentifier): T[]; getMany(identifier: ServiceIdentifier): T[] { - return this.filterServices(identifier).map(service => this.getServiceValue(service)); + return this.findAllServices(identifier).map(service => this.getServiceValue(service)); } /** @@ -168,7 +168,7 @@ export class ContainerInstance { */ remove(...ids: ServiceIdentifier[]): this { ids.forEach(id => { - this.filterServices(id).forEach(service => { + this.findAllServices(id).forEach(service => { this.services.splice(this.services.indexOf(service), 1); }); }); @@ -199,43 +199,17 @@ export class ContainerInstance { } /** - * Filters registered service in the with a given service identifier. + * Returns all services registered with the given identifier. */ - private filterServices(identifier: ServiceIdentifier): ServiceMetadata[] { - return this.services.filter(service => { - if (service.id) return service.id === identifier; - - if (service.type && typeof identifier === 'function') - return service.type === identifier || identifier.prototype instanceof service.type; - - return false; - }); + private findAllServices(identifier: ServiceIdentifier): ServiceMetadata[] { + return this.services.filter(service => service.id === identifier); } /** * Finds registered service in the with a given service identifier. */ private findService(identifier: ServiceIdentifier): ServiceMetadata | undefined { - return this.services.find(service => { - if (service.id) { - if ( - identifier instanceof Object && - service.id instanceof Token && - (identifier as any).service instanceof Token - ) { - return service.id === (identifier as any).service; - } - - return service.id === identifier; - } - - // todo: not sure why it was here || identifier.prototype instanceof service.type; - if (service.type && typeof identifier === 'function') { - return service.type === identifier; - } - - return false; - }); + return this.services.find(service => service.id === identifier); } /** @@ -353,7 +327,7 @@ export class ContainerInstance { }); if (paramHandler) return paramHandler.value(this); - if (paramType && paramType.name && !this.isTypePrimitive(paramType.name)) { + if (paramType && paramType.name && !this.isPrimitiveParamType(paramType.name)) { return this.get(paramType); } @@ -362,10 +336,10 @@ export class ContainerInstance { } /** - * Checks if given type is primitive (e.g. string, boolean, number, object). + * Checks if given parameter type is primitive type or not. */ - private isTypePrimitive(param: string): boolean { - return ['string', 'boolean', 'number', 'object'].indexOf(param.toLowerCase()) !== -1; + private isPrimitiveParamType(paramTypeName: string): boolean { + return ['string', 'boolean', 'number', 'object'].includes(paramTypeName.toLowerCase()); } /** From 1938670761ae1f92598c9f6b59f0c69bb144a6d6 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 16:15:42 +0100 Subject: [PATCH 22/24] feat: add support for calling destroy function on services when they are removed --- src/container-instance.class.ts | 60 ++++++++---- src/container.class.ts | 7 +- src/decorators/service.decorator.ts | 14 +-- src/index.ts | 2 +- ...face..ts => service-metadata.interface.ts} | 3 +- src/interfaces/service-options.interface.ts | 56 ++--------- test/Container.spec.ts | 94 ++++++++++++++++++- 7 files changed, 157 insertions(+), 79 deletions(-) rename src/interfaces/{service-metadata.interface..ts => service-metadata.interface.ts} (91%) diff --git a/src/container-instance.class.ts b/src/container-instance.class.ts index 42e2a422..3699b8be 100644 --- a/src/container-instance.class.ts +++ b/src/container-instance.class.ts @@ -5,9 +5,9 @@ import { Token } from './token.class'; import { Constructable } from './types/constructable.type'; import { AbstractConstructable } from './types/abstract-constructable.type'; import { ServiceIdentifier } from './types/service-identifier.type'; -import { ServiceMetadata } from './interfaces/service-metadata.interface.'; -import { EMPTY_VALUE } from './empty.const'; +import { ServiceMetadata } from './interfaces/service-metadata.interface'; import { ServiceOptions } from './interfaces/service-options.interface'; +import { EMPTY_VALUE } from './empty.const'; /** * TypeDI can have multiple containers. @@ -164,14 +164,22 @@ export class ContainerInstance { } /** - * Removes services with a given service identifiers (tokens or types). + * Removes services with a given service identifiers. */ - remove(...ids: ServiceIdentifier[]): this { - ids.forEach(id => { - this.findAllServices(id).forEach(service => { - this.services.splice(this.services.indexOf(service), 1); + public remove(identifierOrIdentifierArray: ServiceIdentifier | ServiceIdentifier[]): this { + if (Array.isArray(identifierOrIdentifierArray)) { + identifierOrIdentifierArray.forEach(id => this.remove(id)); + } else { + this.services = this.services.filter(service => { + if (service.id === identifierOrIdentifierArray) { + this.destroyServiceInstance(service); + return false; + } + + return true; }); - }); + } + return this; } @@ -179,17 +187,12 @@ export class ContainerInstance { * Completely resets the container by removing all previously registered services from it. */ public reset(options: { strategy: 'resetValue' | 'resetServices' } = { strategy: 'resetValue' }): this { - // TODO: The resources should be freed up. switch (options.strategy) { case 'resetValue': - this.services = this.services.map(s => { - return { - ...s, - value: !!s.type ? EMPTY_VALUE : s.value, - }; - }); + this.services.forEach(service => this.destroyServiceInstance(service)); break; - case 'resetValue': + case 'resetServices': + this.services.forEach(service => this.destroyServiceInstance(service)); this.services = []; break; default: @@ -355,4 +358,29 @@ export class ContainerInstance { } }); } + + /** + * Checks if the given service metadata contains a destroyable service instance and destroys it in place. If the service + * contains a callable function named `destroy` it is called but not awaited and the return value is ignored.. + * + * @param serviceMetadata the service metadata containing the instance to destroy + * @param force when true the service will be always destroyed even if it's cannot be re-created + */ + private destroyServiceInstance(serviceMetadata: ServiceMetadata, force = false) { + /** We reset value only if we can re-create it (aka type or factory exists). */ + const shouldResetValue = force || !!serviceMetadata.type || !!serviceMetadata.factory; + + if (shouldResetValue) { + /** If we wound a function named destroy we call it without any params. */ + if (typeof (serviceMetadata?.value as Record)['destroy'] === 'function') { + try { + (serviceMetadata.value as { destroy: CallableFunction }).destroy(); + } catch (error) { + /** We simply ignore the errors from the destroy function. */ + } + } + + serviceMetadata.value = EMPTY_VALUE; + } + } } diff --git a/src/container.class.ts b/src/container.class.ts index 8d2394a0..4d1d7c5f 100644 --- a/src/container.class.ts +++ b/src/container.class.ts @@ -90,10 +90,11 @@ export class Container { } /** - * Removes services with a given service identifiers (tokens or types). + * Removes services with a given service identifiers. */ - static remove(...ids: ServiceIdentifier[]): Container { - this.globalInstance.remove(...ids); + static remove(identifierOrIdentifierArray: ServiceIdentifier | ServiceIdentifier[]): Container { + this.globalInstance.remove(identifierOrIdentifierArray); + return this; } diff --git a/src/decorators/service.decorator.ts b/src/decorators/service.decorator.ts index c42e7969..7f80f747 100644 --- a/src/decorators/service.decorator.ts +++ b/src/decorators/service.decorator.ts @@ -1,6 +1,6 @@ import { Container } from '../container.class'; import { Token } from '../token.class'; -import { ServiceMetadata } from '../interfaces/service-metadata.interface.'; +import { ServiceMetadata } from '../interfaces/service-metadata.interface'; import { ServiceOptions } from '../interfaces/service-options.interface'; import { EMPTY_VALUE } from '../empty.const'; import { Constructable } from '../types/constructable.type'; @@ -31,12 +31,12 @@ export function Service(optionsOrServiceIdentifier?: ServiceOptions | Toke serviceMetadata.id = optionsOrServiceIdentifier; } else if (optionsOrServiceIdentifier) { /** We received a ServiceOptions object. */ - serviceMetadata.id = optionsOrServiceIdentifier.id || targetConstructor; - serviceMetadata.factory = optionsOrServiceIdentifier.factory || undefined; - serviceMetadata.multiple = optionsOrServiceIdentifier.multiple || false; - serviceMetadata.global = optionsOrServiceIdentifier.global || false; - serviceMetadata.eager = optionsOrServiceIdentifier.eager || false; - serviceMetadata.transient = optionsOrServiceIdentifier.transient || false; + serviceMetadata.id = (optionsOrServiceIdentifier as ServiceMetadata).id || targetConstructor; + serviceMetadata.factory = (optionsOrServiceIdentifier as ServiceMetadata).factory || undefined; + serviceMetadata.multiple = (optionsOrServiceIdentifier as ServiceMetadata).multiple || false; + serviceMetadata.global = (optionsOrServiceIdentifier as ServiceMetadata).global || false; + serviceMetadata.eager = (optionsOrServiceIdentifier as ServiceMetadata).eager || false; + serviceMetadata.transient = (optionsOrServiceIdentifier as ServiceMetadata).transient || false; } Container.set(serviceMetadata); diff --git a/src/index.ts b/src/index.ts index 006bd8d4..2fb76a8c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,7 @@ export * from './error/cannot-instantiate-value.error'; export * from './error/service-not-found.error'; export { Handler } from './interfaces/handler.interface'; -export { ServiceMetadata } from './interfaces/service-metadata.interface.'; +export { ServiceMetadata } from './interfaces/service-metadata.interface'; export { ServiceOptions } from './interfaces/service-options.interface'; export { Constructable } from './types/constructable.type'; export { ServiceIdentifier } from './types/service-identifier.type'; diff --git a/src/interfaces/service-metadata.interface..ts b/src/interfaces/service-metadata.interface.ts similarity index 91% rename from src/interfaces/service-metadata.interface..ts rename to src/interfaces/service-metadata.interface.ts index 8f1be91d..c3d16fc7 100644 --- a/src/interfaces/service-metadata.interface..ts +++ b/src/interfaces/service-metadata.interface.ts @@ -1,11 +1,10 @@ import { Constructable } from '../types/constructable.type'; import { ServiceIdentifier } from '../types/service-identifier.type'; -import { ServiceOptions } from './service-options.interface'; /** * Service metadata is used to initialize service and store its state. */ -export interface ServiceMetadata extends ServiceOptions { +export interface ServiceMetadata { /** Unique identifier of the referenced service. */ id: ServiceIdentifier; diff --git a/src/interfaces/service-options.interface.ts b/src/interfaces/service-options.interface.ts index f73a11a4..199208f0 100644 --- a/src/interfaces/service-options.interface.ts +++ b/src/interfaces/service-options.interface.ts @@ -1,52 +1,10 @@ -import { Constructable } from '../types/constructable.type'; -import { ServiceIdentifier } from '../types/service-identifier.type'; +import { ServiceMetadata } from './service-metadata.interface'; /** - * Service options passed to the @Service() decorator. - * Allows to specify service id and/or factory used to create this service. + * The public ServiceOptions is partial object of ServiceMetadata and either one + * of the following is set: `type`, `factory`, `value` but not more than one. */ -export interface ServiceOptions { - /** - * Unique service id. - */ - id?: ServiceIdentifier; - - /** - * Class definition of the service what is used to initialize given service. - * This property maybe null if the value of the service is set manually. - * If id is not set then it serves as service id. - */ - type?: Constructable | null; - - /** - * Indicates if this service must be global and same instance must be used across all containers. - */ - global?: boolean; - - /** - * Indicates whether a new instance of this class must be created for each class injecting this class. - * Global option is ignored when this option is used. - */ - transient?: boolean; - - /** - * Allows to setup multiple instances the different classes under a single service id string or token. - */ - multiple?: boolean; - - /** - * Indicates whether a new instance should be created as soon as the class is registered. - * By default the registered classes are only instantiated when they are requested from the container. - */ - eager?: boolean; - - /** - * Factory used to produce this service. - */ - factory?: [Constructable, string] | CallableFunction | undefined; - - /** - * Instance of the target class. - */ - value?: unknown | Symbol; -} +export type ServiceOptions = + | Omit>, 'type' | 'factory'> + | Omit>, 'value' | 'factory'> + | Omit>, 'value' | 'type'>; diff --git a/test/Container.spec.ts b/test/Container.spec.ts index b6f094ad..894bb057 100644 --- a/test/Container.spec.ts +++ b/test/Container.spec.ts @@ -142,7 +142,7 @@ describe('Container', function () { expect(Container.get('test1-service')).toBe(test1Service); expect(Container.get('test2-service')).toBe(test2Service); - Container.remove('test1-service', 'test2-service'); + Container.remove(['test1-service', 'test2-service']); expect(Container.get(TestService)).toBe(testService); expect(() => Container.get('test1-service')).toThrowError(ServiceNotFoundError); @@ -287,4 +287,96 @@ describe('Container', function () { expect(Container.get(VehicleService).getColor()).toBe('yellow'); }); }); + + describe('Container.reset', () => { + it('should call destroy function on removed service', () => { + const destroyFnMock = jest.fn(); + const destroyPropertyFnMock = jest.fn(); + @Service() + class MyServiceA { + destroy() { + destroyFnMock(); + } + } + + @Service() + class MyServiceB { + public destroy = destroyPropertyFnMock; + } + + const instanceAOne = Container.get(MyServiceA); + const instanceBOne = Container.get(MyServiceB); + + Container.reset(); + + const instanceATwo = Container.get(MyServiceA); + const instanceBTwo = Container.get(MyServiceB); + + expect(destroyFnMock).toBeCalledTimes(1); + expect(destroyPropertyFnMock).toBeCalledTimes(1); + + expect(instanceAOne).toBeInstanceOf(MyServiceA); + expect(instanceATwo).toBeInstanceOf(MyServiceA); + expect(instanceBOne).toBeInstanceOf(MyServiceB); + expect(instanceBTwo).toBeInstanceOf(MyServiceB); + + expect(instanceAOne).not.toBe(instanceATwo); + expect(instanceBOne).not.toBe(instanceBTwo); + }); + + it('should be able to destroy services without destroy function', () => { + @Service() + class MyService {} + + const instanceA = Container.get(MyService); + + Container.reset(); + + const instanceB = Container.get(MyService); + + expect(instanceA).toBeInstanceOf(MyService); + expect(instanceB).toBeInstanceOf(MyService); + expect(instanceA).not.toBe(instanceB); + }); + }); + + describe('Container.remove', () => { + it('should call destroy function on removed service', () => { + const destroyFnMock = jest.fn(); + const destroyPropertyFnMock = jest.fn(); + @Service() + class MyServiceA { + destroy() { + destroyFnMock(); + } + } + + @Service() + class MyServiceB { + public destroy = destroyPropertyFnMock(); + } + + Container.get(MyServiceA); + Container.get(MyServiceB); + + expect(() => Container.remove(MyServiceA)).not.toThrowError(); + expect(() => Container.remove(MyServiceB)).not.toThrowError(); + + expect(destroyFnMock).toBeCalledTimes(1); + expect(destroyPropertyFnMock).toBeCalledTimes(1); + + expect(() => Container.get(MyServiceA)).toThrowError(ServiceNotFoundError); + expect(() => Container.get(MyServiceB)).toThrowError(ServiceNotFoundError); + }); + + it('should be able to destroy services without destroy function', () => { + @Service() + class MyService {} + + Container.get(MyService); + + expect(() => Container.remove(MyService)).not.toThrowError(); + expect(() => Container.get(MyService)).toThrowError(ServiceNotFoundError); + }); + }); }); From 5ba53e3d6dab4521608c04dc10e782cabaf52754 Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 16:29:52 +0100 Subject: [PATCH 23/24] docs: add changelog for 0.10.0 --- CHANGELOG.md | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f6825d1..1c24bb20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,21 @@ # Changelog -## 0.10.0 [BREAKING] - [NOT RELEASED] +## 0.10.0 [BREAKING] - 2021.01.15 ### BREAKING CHANGES +#### Container.remove signature change + +The `Container.remove` method from now accepts one ID or an array of IDs. + +```ts +// Old format +Container.remove(myServiceA, myServiceB); + +// New format +Container.remove([myServiceA, myServiceB]); +``` + #### Removed support for calling `Service([depA, depB], factory)` This was an undocumented way of calling the `Service` function directly instead of using it as a decorator. This option @@ -35,10 +47,18 @@ oldResult === newResult; // -> true, both equals to "TEST-VALUE" ### Added - added `eager` option to `ServiceOptions`, when enabled the class will be instantiated as soon as it's registered in the container +- added support for destroying removed services, when a service is removed and has a callable `destroy` property it will be called by TypeDI ### Changed -- removed old, undocumented way of calling `@Service` decorator directly +- [BREAKING] removed old, undocumented way of calling `@Service` decorator directly +- [BREAKING] renamed `MissingProvidedServiceTypeError` to `CannotInstantiateValueError` +- various internal refactors +- updated various dev dependencies + +### Fixed + +- generated sourcemaps contains the Typescript files preventing reference errors when using TypeDI with various build tools ## 0.9.1 - 2021.01.11 From 0dec04dec4a1127355784c3b148311f0ee2d746c Mon Sep 17 00:00:00 2001 From: Attila Olah Date: Fri, 15 Jan 2021 16:30:06 +0100 Subject: [PATCH 24/24] build: bump version to 0.10.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 83fe52c0..1a56ffeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "typedi", - "version": "0.9.1", + "version": "0.10.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b776270f..ef857e47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typedi", - "version": "0.9.1", + "version": "0.10.0", "description": "Dependency injection for TypeScript.", "author": "TypeStack contributors", "license": "MIT",