diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb4a78245..d9dd06b3f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,7 @@ jobs: - run: npm cache clean --force - run: npm ci - run: npm run build --if-present + - run: npm run lint Upload_Coverage_Report: name: Upload coverage report to codecov diff --git a/.npmignore b/.npmignore index d8064e0c2..238ec2f75 100644 --- a/.npmignore +++ b/.npmignore @@ -1,29 +1,23 @@ -src -**/*.js.map -test -typings -bundled -build -temp -coverage -docs -wiki -gulpfile.js -bower.json -mocha.opts -tsconfig.json -typings.json -CONTRIBUTING.md -ISSUE_TEMPLATE.md -PULL_REQUEST_TEMPLATE.md -CODE_OF_CONDUCT.md -tslint.json -wallaby.js .auditignore -.nyc_output .github .gitignore +.nyc_output .publishrc .travis.yml .vscode -type_definitions +**/*.js.map +build +CODE_OF_CONDUCT.md +CONTRIBUTING.md +coverage +docs +eslint.config.mjs +ISSUE_TEMPLATE.md +mocha.opts +prettier.config.mjs +PULL_REQUEST_TEMPLATE.md +src +temp +test +tsconfig.json +wiki diff --git a/README.md b/README.md index df70c8088..fbbdc36f1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ ![](https://raw.githubusercontent.com/inversify/inversify.github.io/master/img/cover.jpg)

- NPM version - NPM Downloads - Docs - Codecov -
-
- GitHub stars - - Discord Server + NPM version + NPM Downloads + Docs + Codecov +
+
+ GitHub stars + + Discord Server

# InversifyJS @@ -176,9 +176,9 @@ class Ninja implements Warrior { private _katana: Weapon; private _shuriken: ThrowableWeapon; - public constructor( - @inject(TYPES.Weapon) katana: Weapon, - @inject(TYPES.ThrowableWeapon) shuriken: ThrowableWeapon + constructor( + @inject(TYPES.Weapon) katana: Weapon, + @inject(TYPES.ThrowableWeapon) shuriken: ThrowableWeapon ) { this._katana = katana; this._shuriken = shuriken; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..5b0e25724 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,177 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import eslintPrettierConfig from 'eslint-plugin-prettier/recommended'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; + +/** + * @returns {import('typescript-eslint').ConfigWithExtends} + */ +function buildBaseConfig() { + return { + extends: [ + eslint.configs.recommended, + ...tseslint.configs.strictTypeChecked, + ], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + project: './tsconfig.json', + }, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + 'simple-import-sort': simpleImportSort, + }, + rules: { + '@typescript-eslint/consistent-type-definitions': ['error', 'interface'], + '@typescript-eslint/explicit-member-accessibility': [ + 'error', + { + overrides: { + constructors: 'no-public', + }, + }, + ], + '@typescript-eslint/member-ordering': ['warn'], + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: ['classProperty'], + format: ['strictCamelCase', 'UPPER_CASE', 'snake_case'], + leadingUnderscore: 'allow', + }, + { + selector: 'typeParameter', + format: ['StrictPascalCase'], + prefix: ['T'], + }, + { + selector: ['typeLike'], + format: ['StrictPascalCase'], + }, + { + selector: ['function', 'classMethod'], + format: ['strictCamelCase'], + leadingUnderscore: 'allow', + }, + { + selector: ['parameter'], + format: ['strictCamelCase'], + leadingUnderscore: 'allow', + }, + { + selector: ['variableLike'], + format: ['strictCamelCase', 'UPPER_CASE', 'snake_case'], + }, + ], + '@typescript-eslint/no-deprecated': 'error', + '@typescript-eslint/no-duplicate-type-constituents': 'off', + '@typescript-eslint/no-dynamic-delete': 'error', + '@typescript-eslint/no-extraneous-class': 'off', + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/no-empty-interface': 'warn', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-floating-promises': ['error'], + '@typescript-eslint/no-unsafe-enum-comparison': 'off', + 'no-magic-numbers': 'off', + '@typescript-eslint/no-magic-numbers': [ + 'warn', + { + ignore: [0, 1], + ignoreArrayIndexes: true, + ignoreEnums: true, + ignoreReadonlyClassProperties: true, + }, + ], + '@typescript-eslint/no-require-imports': 'error', + '@typescript-eslint/no-unnecessary-type-arguments': 'off', + '@typescript-eslint/no-unused-expressions': ['error'], + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-nullish-coalescing': ['off'], + '@typescript-eslint/prefer-optional-chain': 'off', + '@typescript-eslint/prefer-readonly': ['warn'], + '@typescript-eslint/promise-function-async': ['error'], + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/restrict-plus-operands': [ + 'error', + { + skipCompoundAssignments: false, + }, + ], + '@typescript-eslint/typedef': [ + 'error', + { + arrayDestructuring: true, + arrowParameter: true, + memberVariableDeclaration: true, + objectDestructuring: true, + parameter: true, + propertyDeclaration: true, + variableDeclaration: true, + }, + ], + '@typescript-eslint/unified-signatures': 'error', + '@typescript-eslint/strict-boolean-expressions': 'error', + '@typescript-eslint/switch-exhaustiveness-check': [ + 'error', + { + considerDefaultExhaustiveForUnions: true, + }, + ], + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + args: 'all', + argsIgnorePattern: '^_', + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], + 'simple-import-sort/imports': [ + 'error', + { + groups: [['^\\u0000'], ['^node:'], ['^@?\\w'], ['^'], ['^\\.']], + }, + ], + 'sort-keys': [ + 'error', + 'asc', + { + caseSensitive: false, + natural: true, + }, + ], + }, + }; +} + +const baseRules = buildBaseConfig(); + +const config = tseslint.config( + { + ...baseRules, + files: ['**/*.ts'], + ignores: ['**/*.test.ts'], + }, + { + ...baseRules, + files: ['**/*.test.ts'], + rules: { + ...(baseRules.rules ?? {}), + '@typescript-eslint/no-confusing-void-expression': 'off', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/no-magic-numbers': 'off', + }, + }, + /** @type {import('typescript-eslint').ConfigWithExtends} */ ( + eslintPrettierConfig + ), +); + +export default [...config]; diff --git a/package-lock.json b/package-lock.json index 7c39352de..3cebf59dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,17 @@ "version": "6.0.3", "license": "MIT", "devDependencies": { + "@eslint/js": "9.13.0", "@types/chai": "4.3.6", "@types/mocha": "10.0.9", "@types/sinon": "17.0.3", + "@typescript-eslint/eslint-plugin": "8.12.2", + "@typescript-eslint/parser": "8.12.2", "chai": "4.3.8", + "eslint": "9.13.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-simple-import-sort": "12.1.1", "mocha": "10.7.3", "nyc": "17.1.0", "prettier": "3.3.3", @@ -21,8 +28,8 @@ "rimraf": "6.0.1", "sinon": "19.0.2", "ts-node": "10.9.2", - "tslint": "6.1.3", "typescript": "5.6.3", + "typescript-eslint": "8.12.2", "updates": "16.4.0" } }, @@ -401,6 +408,252 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/core": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", + "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", + "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz", + "integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -642,6 +895,57 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -722,6 +1026,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/mocha": { "version": "10.0.9", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.9.tgz", @@ -736,22 +1054,349 @@ "dev": true, "peer": true }, - "node_modules/@types/sinon": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", - "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "node_modules/@types/sinon": { + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-17.0.3.tgz", + "integrity": "sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz", + "integrity": "sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/type-utils": "8.12.2", + "@typescript-eslint/utils": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.12.2.tgz", + "integrity": "sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz", + "integrity": "sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz", + "integrity": "sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.12.2", + "@typescript-eslint/utils": "8.12.2", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/types": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.12.2.tgz", + "integrity": "sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz", + "integrity": "sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/visitor-keys": "8.12.2", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.12.2.tgz", + "integrity": "sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.12.2", + "@typescript-eslint/types": "8.12.2", + "@typescript-eslint/typescript-estree": "8.12.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz", + "integrity": "sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.12.2", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", - "dependencies": { - "@types/sinonjs__fake-timers": "*" + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -765,6 +1410,23 @@ "node": ">=8" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -974,12 +1636,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1024,15 +1687,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cache-base": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", @@ -1068,6 +1722,16 @@ "node": ">=8" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1412,12 +2076,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -1550,6 +2208,13 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/default-require-extensions": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", @@ -1614,96 +2279,435 @@ "integrity": "sha1-Qe9KXy0DZCmDurtPZKS1drEVUhU=", "dev": true, "dependencies": { - "chalk": "^1.1.1", - "elegant-spinner": "^1.0.1", - "log-update": "^1.0.2", - "os-family": "^1.0.0" + "chalk": "^1.1.1", + "elegant-spinner": "^1.0.1", + "log-update": "^1.0.2", + "os-family": "^1.0.0" + } + }, + "node_modules/elegant-status/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/elegant-status/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/elegant-status/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/elegant-status/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/elegant-status/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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 + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", + "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/core": "^0.7.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.13.0", + "@eslint/plugin-kit": "^0.2.0", + "@humanfs/node": "^0.16.5", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.1.0", + "eslint-visitor-keys": "^4.1.0", + "espree": "^10.2.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-simple-import-sort": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz", + "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=5.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/elegant-status/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/elegant-status/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/elegant-status/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, - "node_modules/elegant-status/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, + "license": "MIT" + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "ansi-regex": "^2.0.0" + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/elegant-status/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=0.8.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/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 + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } }, - "node_modules/es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">=6" + "node": ">=4.0" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=0.8.0" + "node": ">=0.10.0" } }, "node_modules/exit-hook": { @@ -1928,6 +2932,75 @@ "node": ">=0.10.0" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -1940,11 +3013,25 @@ "node": ">=4" } }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -1994,6 +3081,27 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" + }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2068,12 +3176,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2169,17 +3271,12 @@ "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } + "license": "MIT" }, "node_modules/has-ansi": { "version": "2.0.0", @@ -2202,6 +3299,16 @@ "node": ">=0.10.0" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -2284,6 +3391,43 @@ "node": ">=0.10.0" } }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -2384,18 +3528,6 @@ "is-ci": "bin.js" } }, - "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-data-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", @@ -2669,27 +3801,6 @@ "node": ">=8" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", @@ -2803,13 +3914,34 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", @@ -2831,6 +3963,16 @@ "dev": true, "license": "MIT" }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -2840,6 +3982,20 @@ "node": ">=0.10.0" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2874,6 +4030,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.toarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", @@ -2998,6 +4161,16 @@ "node": ">=0.10.0" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -3125,15 +4298,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -3157,18 +4321,6 @@ "node": ">=0.10.0" } }, - "node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mocha": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", @@ -3333,15 +4485,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/mocha/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3438,6 +4581,13 @@ "node": ">=0.10.0" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/nise": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz", @@ -3918,6 +5068,24 @@ "node": ">=4" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/os-family": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/os-family/-/os-family-1.1.0.tgz", @@ -4025,6 +5193,19 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -4061,12 +5242,6 @@ "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, "node_modules/path-scurry": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", @@ -4227,6 +5402,16 @@ "node": ">=0.10.0" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/prettier": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", @@ -4243,6 +5428,19 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -4345,6 +5543,37 @@ "node": ">=4" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -4432,23 +5661,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.8.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -4487,6 +5699,17 @@ "node": ">=0.12" } }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", @@ -4596,6 +5819,30 @@ "node": ">=0.12.0" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -4768,27 +6015,6 @@ "node": ">=0.3.1" } }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -5274,18 +6500,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 0.4" + "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/unts" } }, + "node_modules/synckit/node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "dev": true, + "license": "0BSD" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -5300,6 +6551,13 @@ "node": ">=8" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -5360,6 +6618,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -5372,10 +6631,24 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -5420,18 +6693,6 @@ } } }, - "node_modules/ts-node/node_modules/acorn": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", - "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/ts-node/node_modules/acorn-walk": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", @@ -5456,65 +6717,17 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, - "node_modules/tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" + "prelude-ls": "^1.2.1" }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "node_modules/tslint/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" + "node": ">= 0.8.0" } }, "node_modules/type-detect": { @@ -5558,6 +6771,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.12.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.12.2.tgz", + "integrity": "sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.12.2", + "@typescript-eslint/parser": "8.12.2", + "@typescript-eslint/utils": "8.12.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -5674,6 +6911,16 @@ "node": ">=18" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -5724,6 +6971,16 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/workerpool": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", diff --git a/package.json b/package.json index 455a54288..ff12b2e1e 100644 --- a/package.json +++ b/package.json @@ -5,10 +5,17 @@ }, "description": "A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.", "devDependencies": { + "@eslint/js": "9.13.0", "@types/chai": "4.3.6", "@types/mocha": "10.0.9", "@types/sinon": "17.0.3", + "@typescript-eslint/eslint-plugin": "8.12.2", + "@typescript-eslint/parser": "8.12.2", "chai": "4.3.8", + "eslint": "9.13.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-prettier": "5.2.1", + "eslint-plugin-simple-import-sort": "12.1.1", "mocha": "10.7.3", "nyc": "17.1.0", "prettier": "3.3.3", @@ -17,8 +24,8 @@ "rimraf": "6.0.1", "sinon": "19.0.2", "ts-node": "10.9.2", - "tslint": "6.1.3", "typescript": "5.6.3", + "typescript-eslint": "8.12.2", "updates": "16.4.0" }, "engines": {}, @@ -49,10 +56,10 @@ "build:es6": "tsc -p src/tsconfig-es6.json", "build:lib": "tsc -p src/tsconfig.json", "clean": "rimraf amd es es6 lib", - "format": "prettier --write ./src/*.ts ./src/**/*.ts", + "format": "prettier --write ./src/*.ts ./src/**/*.ts ./test/*.ts ./test/**/*.ts", + "lint": "eslint ./src ./test", "postupdate": "git diff-files --quiet package-lock.json || npm test", "prepublish": "npm run build && publish-please guard", - "pretest": "tslint --project .", "publish-please": "publish-please", "test": "nyc --reporter=lcov --require ts-node/register mocha test/**/*.test.ts --reporter spec --retries 3 --require 'node_modules/reflect-metadata/Reflect.js' --exit", "update": "updates --update --minor && npm install" diff --git a/prettier.config.mjs b/prettier.config.mjs index dabd894d8..69c1a7331 100644 --- a/prettier.config.mjs +++ b/prettier.config.mjs @@ -5,7 +5,7 @@ export default { semi: true, singleQuote: true, bracketSpacing: true, - arrowParens: "always", - endOfLine: "lf", - trailingComma: "all", + arrowParens: 'always', + endOfLine: 'lf', + trailingComma: 'all', }; diff --git a/src/annotation/decorator_utils.ts b/src/annotation/decorator_utils.ts index ba45791dd..cf10ada31 100644 --- a/src/annotation/decorator_utils.ts +++ b/src/annotation/decorator_utils.ts @@ -3,10 +3,10 @@ import * as METADATA_KEY from '../constants/metadata_keys'; import { interfaces } from '../interfaces/interfaces'; import { getFirstArrayDuplicate } from '../utils/js'; -function targetIsConstructorFunction( +function targetIsConstructorFunction( target: DecoratorTarget, ): target is ConstructorFunction { - return (target as ConstructorFunction).prototype !== undefined; + return (target as Partial>).prototype !== undefined; } type Prototype = { @@ -16,8 +16,8 @@ type Prototype = { } & { constructor: NewableFunction }; interface ConstructorFunction> { - new (...args: unknown[]): T; prototype: Prototype; + new (...args: unknown[]): T; } export type DecoratorTarget = @@ -69,7 +69,10 @@ function _ensureNoMetadataKeyDuplicates( let metadatas: interfaces.Metadata[] = []; if (Array.isArray(metadata)) { metadatas = metadata; - const duplicate = getFirstArrayDuplicate(metadatas.map((md) => md.key)); + const duplicate: string | number | symbol | undefined = + getFirstArrayDuplicate( + metadatas.map((md: interfaces.Metadata) => md.key), + ); if (duplicate !== undefined) { throw new Error( `${ERROR_MSGS.DUPLICATED_METADATA} ${duplicate.toString()}`, @@ -99,17 +102,17 @@ function _tagParameterOrProperty( paramsOrPropertiesMetadata = Reflect.getMetadata( metadataKey, annotationTarget, - ); + ) as interfaces.MetadataMap; } let paramOrPropertyMetadata: interfaces.Metadata[] | undefined = - paramsOrPropertiesMetadata[key as string]; + paramsOrPropertiesMetadata[key]; if (paramOrPropertyMetadata === undefined) { paramOrPropertyMetadata = []; } else { for (const m of paramOrPropertyMetadata) { - if (metadatas.some((md) => md.key === m.key)) { + if (metadatas.some((md: interfaces.Metadata) => md.key === m.key)) { throw new Error( `${ERROR_MSGS.DUPLICATED_METADATA} ${m.key.toString()}`, ); diff --git a/src/annotation/inject.ts b/src/annotation/inject.ts index 392c85df0..c6702efca 100644 --- a/src/annotation/inject.ts +++ b/src/annotation/inject.ts @@ -1,6 +1,14 @@ import * as METADATA_KEY from '../constants/metadata_keys'; +import { DecoratorTarget } from './decorator_utils'; import { injectBase } from './inject_base'; +import { ServiceIdentifierOrFunc } from './lazy_service_identifier'; -const inject = injectBase(METADATA_KEY.INJECT_TAG); +const inject: ( + serviceIdentifier: ServiceIdentifierOrFunc, +) => ( + target: DecoratorTarget, + targetKey?: string | symbol, + indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, +) => void = injectBase(METADATA_KEY.INJECT_TAG); export { inject }; diff --git a/src/annotation/inject_base.ts b/src/annotation/inject_base.ts index ff5536d36..dc8d71444 100644 --- a/src/annotation/inject_base.ts +++ b/src/annotation/inject_base.ts @@ -3,22 +3,34 @@ import { Metadata } from '../planning/metadata'; import { createTaggedDecorator, DecoratorTarget } from './decorator_utils'; import { ServiceIdentifierOrFunc } from './lazy_service_identifier'; -export function injectBase(metadataKey: string) { +export function injectBase( + metadataKey: string, +): ( + serviceIdentifier: ServiceIdentifierOrFunc, +) => ( + target: DecoratorTarget, + targetKey?: string | symbol, + indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, +) => void { return (serviceIdentifier: ServiceIdentifierOrFunc) => { return ( target: DecoratorTarget, targetKey?: string | symbol, indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, ) => { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (serviceIdentifier === undefined) { - const className = + const className: string = typeof target === 'function' ? target.name : target.constructor.name; throw new Error(UNDEFINED_INJECT_ANNOTATION(className)); } - return createTaggedDecorator( - new Metadata(metadataKey, serviceIdentifier), - )(target, targetKey, indexOrPropertyDescriptor); + + createTaggedDecorator(new Metadata(metadataKey, serviceIdentifier))( + target, + targetKey, + indexOrPropertyDescriptor, + ); }; }; } diff --git a/src/annotation/injectable.ts b/src/annotation/injectable.ts index 6d0d66f40..7cfd8f255 100644 --- a/src/annotation/injectable.ts +++ b/src/annotation/injectable.ts @@ -2,6 +2,7 @@ import * as ERRORS_MSGS from '../constants/error_msgs'; import * as METADATA_KEY from '../constants/metadata_keys'; function injectable() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any return function unknown>( target: T, ) { @@ -9,8 +10,10 @@ function injectable() { throw new Error(ERRORS_MSGS.DUPLICATED_INJECTABLE_DECORATOR); } - const types = - Reflect.getMetadata(METADATA_KEY.DESIGN_PARAM_TYPES, target) || []; + const types: NewableFunction[] = + (Reflect.getMetadata(METADATA_KEY.DESIGN_PARAM_TYPES, target) as + | NewableFunction[] + | undefined) || []; Reflect.defineMetadata(METADATA_KEY.PARAM_TYPES, types, target); return target; diff --git a/src/annotation/lazy_service_identifier.ts b/src/annotation/lazy_service_identifier.ts index 53f28e4a8..aa8317a0f 100644 --- a/src/annotation/lazy_service_identifier.ts +++ b/src/annotation/lazy_service_identifier.ts @@ -5,8 +5,8 @@ export type ServiceIdentifierOrFunc = | LazyServiceIdentifier; export class LazyServiceIdentifier { - private _cb: () => interfaces.ServiceIdentifier; - public constructor(cb: () => interfaces.ServiceIdentifier) { + private readonly _cb: () => interfaces.ServiceIdentifier; + constructor(cb: () => interfaces.ServiceIdentifier) { this._cb = cb; } diff --git a/src/annotation/multi_inject.ts b/src/annotation/multi_inject.ts index ec234781b..ad32a81db 100644 --- a/src/annotation/multi_inject.ts +++ b/src/annotation/multi_inject.ts @@ -1,6 +1,14 @@ import * as METADATA_KEY from '../constants/metadata_keys'; +import { DecoratorTarget } from './decorator_utils'; import { injectBase } from './inject_base'; +import { ServiceIdentifierOrFunc } from './lazy_service_identifier'; -const multiInject = injectBase(METADATA_KEY.MULTI_INJECT_TAG); +const multiInject: ( + serviceIdentifier: ServiceIdentifierOrFunc, +) => ( + target: DecoratorTarget, + targetKey?: string | symbol, + indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, +) => void = injectBase(METADATA_KEY.MULTI_INJECT_TAG); export { multiInject }; diff --git a/src/annotation/post_construct.ts b/src/annotation/post_construct.ts index 77d825694..7dc0ffc17 100644 --- a/src/annotation/post_construct.ts +++ b/src/annotation/post_construct.ts @@ -2,7 +2,12 @@ import * as ERRORS_MSGS from '../constants/error_msgs'; import * as METADATA_KEY from '../constants/metadata_keys'; import { propertyEventDecorator } from './property_event_decorator'; -const postConstruct = propertyEventDecorator( +const postConstruct: () => ( + target: { + constructor: NewableFunction; + }, + propertyKey: string, +) => void = propertyEventDecorator( METADATA_KEY.POST_CONSTRUCT, ERRORS_MSGS.MULTIPLE_POST_CONSTRUCT_METHODS, ); diff --git a/src/annotation/pre_destroy.ts b/src/annotation/pre_destroy.ts index ded1713b6..88bec0947 100644 --- a/src/annotation/pre_destroy.ts +++ b/src/annotation/pre_destroy.ts @@ -2,7 +2,12 @@ import * as ERRORS_MSGS from '../constants/error_msgs'; import * as METADATA_KEY from '../constants/metadata_keys'; import { propertyEventDecorator } from './property_event_decorator'; -const preDestroy = propertyEventDecorator( +const preDestroy: () => ( + target: { + constructor: NewableFunction; + }, + propertyKey: string, +) => void = propertyEventDecorator( METADATA_KEY.PRE_DESTROY, ERRORS_MSGS.MULTIPLE_PRE_DESTROY_METHODS, ); diff --git a/src/annotation/property_event_decorator.ts b/src/annotation/property_event_decorator.ts index 8f1c4b675..b1fa95679 100644 --- a/src/annotation/property_event_decorator.ts +++ b/src/annotation/property_event_decorator.ts @@ -3,7 +3,7 @@ import { Metadata } from '../planning/metadata'; function propertyEventDecorator(eventKey: string, errorMessage: string) { return () => { return (target: { constructor: NewableFunction }, propertyKey: string) => { - const metadata = new Metadata(eventKey, propertyKey); + const metadata: Metadata = new Metadata(eventKey, propertyKey); if (Reflect.hasOwnMetadata(eventKey, target.constructor)) { throw new Error(errorMessage); diff --git a/src/annotation/tagged.ts b/src/annotation/tagged.ts index 32431f483..465428ff3 100644 --- a/src/annotation/tagged.ts +++ b/src/annotation/tagged.ts @@ -2,10 +2,7 @@ import { Metadata } from '../planning/metadata'; import { createTaggedDecorator } from './decorator_utils'; // Used to add custom metadata which is used to resolve metadata-based contextual bindings. -function tagged( - metadataKey: string | number | symbol, - metadataValue: unknown, -) { +function tagged(metadataKey: string | number | symbol, metadataValue: unknown) { return createTaggedDecorator(new Metadata(metadataKey, metadataValue)); } diff --git a/src/annotation/target_name.ts b/src/annotation/target_name.ts index d639cf661..a0e59ea73 100644 --- a/src/annotation/target_name.ts +++ b/src/annotation/target_name.ts @@ -1,6 +1,6 @@ import * as METADATA_KEY from '../constants/metadata_keys'; import { Metadata } from '../planning/metadata'; -import { tagParameter, DecoratorTarget } from './decorator_utils'; +import { DecoratorTarget, tagParameter } from './decorator_utils'; function targetName(name: string) { return function ( @@ -8,7 +8,7 @@ function targetName(name: string) { targetKey: string | undefined, index: number, ) { - const metadata = new Metadata(METADATA_KEY.NAME_TAG, name); + const metadata: Metadata = new Metadata(METADATA_KEY.NAME_TAG, name); tagParameter(target, targetKey, index, metadata); }; } diff --git a/src/annotation/unmanaged.ts b/src/annotation/unmanaged.ts index 465f049c7..a9f2415b0 100644 --- a/src/annotation/unmanaged.ts +++ b/src/annotation/unmanaged.ts @@ -1,6 +1,6 @@ import * as METADATA_KEY from '../constants/metadata_keys'; import { Metadata } from '../planning/metadata'; -import { tagParameter, DecoratorTarget } from './decorator_utils'; +import { DecoratorTarget, tagParameter } from './decorator_utils'; function unmanaged() { return function ( @@ -8,7 +8,7 @@ function unmanaged() { targetKey: string | undefined, index: number, ) { - const metadata = new Metadata(METADATA_KEY.UNMANAGED_TAG, true); + const metadata: Metadata = new Metadata(METADATA_KEY.UNMANAGED_TAG, true); tagParameter(target, targetKey, index, metadata); }; } diff --git a/src/bindings/binding.ts b/src/bindings/binding.ts index 49984dcd2..98468791b 100644 --- a/src/bindings/binding.ts +++ b/src/bindings/binding.ts @@ -44,7 +44,7 @@ class Binding implements interfaces.Binding { // On deactivation handler (invoked just before an instance is unbinded and removed from container) public onDeactivation: interfaces.BindingDeactivation | null; - public constructor( + constructor( serviceIdentifier: interfaces.ServiceIdentifier, scope: interfaces.BindingScope, ) { @@ -53,7 +53,7 @@ class Binding implements interfaces.Binding { this.serviceIdentifier = serviceIdentifier; this.scope = scope; this.type = BindingTypeEnum.Invalid; - this.constraint = (request: interfaces.Request | null) => true; + this.constraint = (_request: interfaces.Request | null) => true; this.implementationType = null; this.cache = null; this.factory = null; @@ -64,7 +64,10 @@ class Binding implements interfaces.Binding { } public clone(): interfaces.Binding { - const clone = new Binding(this.serviceIdentifier, this.scope); + const clone: Binding = new Binding( + this.serviceIdentifier, + this.scope, + ); clone.activated = clone.scope === BindingScopeEnum.Singleton ? this.activated : false; clone.implementationType = this.implementationType; diff --git a/src/bindings/binding_count.ts b/src/bindings/binding_count.ts index 8697ea88c..ee9e81df8 100644 --- a/src/bindings/binding_count.ts +++ b/src/bindings/binding_count.ts @@ -1,5 +1,5 @@ -export const BindingCount = { - MultipleBindingsAvailable: 2, - NoBindingsAvailable: 0, - OnlyOneBindingAvailable: 1, -}; +export enum BindingCount { + MultipleBindingsAvailable = 2, + NoBindingsAvailable = 0, + OnlyOneBindingAvailable = 1, +} diff --git a/src/constants/error_msgs.ts b/src/constants/error_msgs.ts index b628b567e..cd8ba8b0b 100644 --- a/src/constants/error_msgs.ts +++ b/src/constants/error_msgs.ts @@ -1,81 +1,96 @@ -export const DUPLICATED_INJECTABLE_DECORATOR = +export const DUPLICATED_INJECTABLE_DECORATOR: string = 'Cannot apply @injectable decorator multiple times.'; -export const DUPLICATED_METADATA = +export const DUPLICATED_METADATA: string = 'Metadata key was used more than once in a parameter:'; -export const NULL_ARGUMENT = 'NULL argument'; -export const KEY_NOT_FOUND = 'Key Not Found'; -export const AMBIGUOUS_MATCH = 'Ambiguous match found for serviceIdentifier:'; -export const CANNOT_UNBIND = 'Could not unbind serviceIdentifier:'; -export const NOT_REGISTERED = +export const NULL_ARGUMENT: string = 'NULL argument'; +export const KEY_NOT_FOUND: string = 'Key Not Found'; +export const AMBIGUOUS_MATCH: string = + 'Ambiguous match found for serviceIdentifier:'; +export const CANNOT_UNBIND: string = 'Could not unbind serviceIdentifier:'; +export const NOT_REGISTERED: string = 'No matching bindings found for serviceIdentifier:'; -export const MISSING_INJECTABLE_ANNOTATION = +export const MISSING_INJECTABLE_ANNOTATION: string = 'Missing required @injectable annotation in:'; -export const MISSING_INJECT_ANNOTATION = +export const MISSING_INJECT_ANNOTATION: string = 'Missing required @inject or @multiInject annotation in:'; -export const UNDEFINED_INJECT_ANNOTATION = (name: string) => +export const UNDEFINED_INJECT_ANNOTATION: (name: string) => string = ( + name: string, +) => `@inject called with undefined this could mean that the class ${name} has ` + 'a circular dependency problem. You can use a LazyServiceIdentifer to ' + 'overcome this limitation.'; -export const CIRCULAR_DEPENDENCY = 'Circular dependency found:'; -export const NOT_IMPLEMENTED = +export const CIRCULAR_DEPENDENCY: string = 'Circular dependency found:'; +export const NOT_IMPLEMENTED: string = 'Sorry, this feature is not fully implemented yet.'; -export const INVALID_BINDING_TYPE = 'Invalid binding type:'; -export const NO_MORE_SNAPSHOTS_AVAILABLE = 'No snapshot available to restore.'; -export const INVALID_MIDDLEWARE_RETURN = +export const INVALID_BINDING_TYPE: string = 'Invalid binding type:'; +export const NO_MORE_SNAPSHOTS_AVAILABLE: string = + 'No snapshot available to restore.'; +export const INVALID_MIDDLEWARE_RETURN: string = 'Invalid return type in middleware. Middleware must return!'; -export const INVALID_FUNCTION_BINDING = +export const INVALID_FUNCTION_BINDING: string = 'Value provided to function binding must be a function!'; -export const LAZY_IN_SYNC = (key: unknown) => +export const LAZY_IN_SYNC: (key: unknown) => string = (key: unknown) => `You are attempting to construct ${keyToString(key)} in a synchronous way ` + 'but it has asynchronous dependencies.'; -export const INVALID_TO_SELF_VALUE = +export const INVALID_TO_SELF_VALUE: string = 'The toSelf function can only be applied when a constructor is ' + 'used as service identifier'; -export const INVALID_DECORATOR_OPERATION = +export const INVALID_DECORATOR_OPERATION: string = 'The @inject @multiInject @tagged and @named decorators ' + 'must be applied to the parameters of a class constructor or a class property.'; -export const ARGUMENTS_LENGTH_MISMATCH = (...values: unknown[]) => +export const ARGUMENTS_LENGTH_MISMATCH: (name: string) => string = ( + name: string, +) => 'The number of constructor arguments in the derived class ' + - `${values[0]} must be >= than the number of constructor arguments of its base class.`; + `${name} must be >= than the number of constructor arguments of its base class.`; -export const CONTAINER_OPTIONS_MUST_BE_AN_OBJECT = +export const CONTAINER_OPTIONS_MUST_BE_AN_OBJECT: string = 'Invalid Container constructor argument. Container options ' + 'must be an object.'; -export const CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE = +export const CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE: string = 'Invalid Container option. Default scope must ' + 'be a string ("singleton" or "transient").'; -export const CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE = +export const CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE: string = 'Invalid Container option. Auto bind injectable must ' + 'be a boolean'; -export const CONTAINER_OPTIONS_INVALID_SKIP_BASE_CHECK = +export const CONTAINER_OPTIONS_INVALID_SKIP_BASE_CHECK: string = 'Invalid Container option. Skip base check must ' + 'be a boolean'; -export const MULTIPLE_PRE_DESTROY_METHODS = +export const MULTIPLE_PRE_DESTROY_METHODS: string = 'Cannot apply @preDestroy decorator multiple times in the same class'; -export const MULTIPLE_POST_CONSTRUCT_METHODS = +export const MULTIPLE_POST_CONSTRUCT_METHODS: string = 'Cannot apply @postConstruct decorator multiple times in the same class'; -export const ASYNC_UNBIND_REQUIRED = +export const ASYNC_UNBIND_REQUIRED: string = 'Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)'; -export const POST_CONSTRUCT_ERROR = (clazz: string, errorMessage: string) => +export const POST_CONSTRUCT_ERROR: ( + clazz: string, + errorMessage: string, +) => string = (clazz: string, errorMessage: string): string => `@postConstruct error in class ${clazz}: ${errorMessage}`; -export const PRE_DESTROY_ERROR = (clazz: string, errorMessage: string) => +export const PRE_DESTROY_ERROR: ( + clazz: string, + errorMessage: string, +) => string = (clazz: string, errorMessage: string): string => `@preDestroy error in class ${clazz}: ${errorMessage}`; -export const ON_DEACTIVATION_ERROR = (clazz: string, errorMessage: string) => +export const ON_DEACTIVATION_ERROR: ( + clazz: string, + errorMessage: string, +) => string = (clazz: string, errorMessage: string): string => `onDeactivation() error in class ${clazz}: ${errorMessage}`; -export const CIRCULAR_DEPENDENCY_IN_FACTORY = ( +export const CIRCULAR_DEPENDENCY_IN_FACTORY: ( factoryType: string, serviceIdentifier: string, -) => +) => string = (factoryType: string, serviceIdentifier: string): string => `It looks like there is a circular dependency in one of the '${factoryType}' bindings. Please investigate bindings with ` + `service identifier '${serviceIdentifier}'.`; -export const STACK_OVERFLOW = 'Maximum call stack size exceeded'; +export const STACK_OVERFLOW: string = 'Maximum call stack size exceeded'; function keyToString(key: unknown): string { if (typeof key === 'function') { @@ -84,5 +99,6 @@ function keyToString(key: unknown): string { if (typeof key === 'symbol') { return key.toString(); } + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions return `'${key}'`; } diff --git a/src/constants/literal_types.ts b/src/constants/literal_types.ts index 10ab76ce8..6319087dd 100644 --- a/src/constants/literal_types.ts +++ b/src/constants/literal_types.ts @@ -1,11 +1,13 @@ import { interfaces } from '../interfaces/interfaces'; +// eslint-disable-next-line @typescript-eslint/naming-convention const BindingScopeEnum: interfaces.BindingScopeEnum = { Request: 'Request', Singleton: 'Singleton', Transient: 'Transient', }; +// eslint-disable-next-line @typescript-eslint/naming-convention const BindingTypeEnum: interfaces.BindingTypeEnum = { ConstantValue: 'ConstantValue', Constructor: 'Constructor', @@ -17,6 +19,7 @@ const BindingTypeEnum: interfaces.BindingTypeEnum = { Provider: 'Provider', }; +// eslint-disable-next-line @typescript-eslint/naming-convention const TargetTypeEnum: interfaces.TargetTypeEnum = { ClassProperty: 'ClassProperty', ConstructorArgument: 'ConstructorArgument', diff --git a/src/constants/metadata_keys.ts b/src/constants/metadata_keys.ts index f6e7b55ec..1589adf63 100644 --- a/src/constants/metadata_keys.ts +++ b/src/constants/metadata_keys.ts @@ -1,38 +1,38 @@ // Used for named bindings -export const NAMED_TAG = 'named'; +export const NAMED_TAG: string = 'named'; // The name of the target at design time -export const NAME_TAG = 'name'; +export const NAME_TAG: string = 'name'; // The for unmanaged injections (in base classes when using inheritance) -export const UNMANAGED_TAG = 'unmanaged'; +export const UNMANAGED_TAG: string = 'unmanaged'; // The for optional injections -export const OPTIONAL_TAG = 'optional'; +export const OPTIONAL_TAG: string = 'optional'; // The type of the binding at design time -export const INJECT_TAG = 'inject'; +export const INJECT_TAG: string = 'inject'; // The type of the binding at design type for multi-injections -export const MULTI_INJECT_TAG = 'multi_inject'; +export const MULTI_INJECT_TAG: string = 'multi_inject'; // used to store constructor arguments tags -export const TAGGED = 'inversify:tagged'; +export const TAGGED: string = 'inversify:tagged'; // used to store class properties tags -export const TAGGED_PROP = 'inversify:tagged_props'; +export const TAGGED_PROP: string = 'inversify:tagged_props'; // used to store types to be injected -export const PARAM_TYPES = 'inversify:paramtypes'; +export const PARAM_TYPES: string = 'inversify:paramtypes'; // used to access design time types -export const DESIGN_PARAM_TYPES = 'design:paramtypes'; +export const DESIGN_PARAM_TYPES: string = 'design:paramtypes'; // used to identify postConstruct functions -export const POST_CONSTRUCT = 'post_construct'; +export const POST_CONSTRUCT: string = 'post_construct'; // used to identify preDestroy functions -export const PRE_DESTROY = 'pre_destroy'; +export const PRE_DESTROY: string = 'pre_destroy'; function getNonCustomTagKeys(): string[] { return [ diff --git a/src/container/container.ts b/src/container/container.ts index 40b194d7f..22c800206 100644 --- a/src/container/container.ts +++ b/src/container/container.ts @@ -35,46 +35,14 @@ class Container implements interfaces.Container { private _deactivations: interfaces.Lookup< interfaces.BindingDeactivation >; - private _snapshots: interfaces.ContainerSnapshot[]; + private readonly _snapshots: interfaces.ContainerSnapshot[]; private _metadataReader: interfaces.MetadataReader; private _moduleActivationStore: interfaces.ModuleActivationStore; - public static merge( - container1: interfaces.Container, - container2: interfaces.Container, - ...containers: interfaces.Container[] - ): interfaces.Container { - const container = new Container(); - const targetContainers: interfaces.Lookup>[] = [ - container1, - container2, - ...containers, - ].map((targetContainer) => getBindingDictionary(targetContainer)); - const bindingDictionary: interfaces.Lookup> = - getBindingDictionary(container); - - function copyDictionary( - origin: interfaces.Lookup>, - destination: interfaces.Lookup>, - ) { - origin.traverse((_key, value) => { - value.forEach((binding) => { - destination.add(binding.serviceIdentifier, binding.clone()); - }); - }); - } - - targetContainers.forEach((targetBindingDictionary) => { - copyDictionary(targetBindingDictionary, bindingDictionary); - }); - - return container; - } - - public constructor(containerOptions?: interfaces.ContainerOptions) { - const options = containerOptions || {}; + constructor(containerOptions?: interfaces.ContainerOptions) { + const options: interfaces.ContainerOptions = containerOptions || {}; if (typeof options !== 'object') { - throw new Error(`${ERROR_MSGS.CONTAINER_OPTIONS_MUST_BE_AN_OBJECT}`); + throw new Error(ERROR_MSGS.CONTAINER_OPTIONS_MUST_BE_AN_OBJECT); } if (options.defaultScope === undefined) { @@ -84,23 +52,21 @@ class Container implements interfaces.Container { options.defaultScope !== BindingScopeEnum.Transient && options.defaultScope !== BindingScopeEnum.Request ) { - throw new Error(`${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE}`); + throw new Error(ERROR_MSGS.CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE); } if (options.autoBindInjectable === undefined) { options.autoBindInjectable = false; } else if (typeof options.autoBindInjectable !== 'boolean') { throw new Error( - `${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE}`, + ERROR_MSGS.CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE, ); } if (options.skipBaseClassChecks === undefined) { options.skipBaseClassChecks = false; } else if (typeof options.skipBaseClassChecks !== 'boolean') { - throw new Error( - `${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_SKIP_BASE_CHECK}`, - ); + throw new Error(ERROR_MSGS.CONTAINER_OPTIONS_INVALID_SKIP_BASE_CHECK); } this.options = { @@ -120,17 +86,57 @@ class Container implements interfaces.Container { this._moduleActivationStore = new ModuleActivationStore(); } - public load(...modules: interfaces.ContainerModule[]) { + public static merge( + container1: interfaces.Container, + container2: interfaces.Container, + ...containers: interfaces.Container[] + ): interfaces.Container { + const container: Container = new Container(); + const targetContainers: interfaces.Lookup>[] = [ + container1, + container2, + ...containers, + ].map((targetContainer: interfaces.Container) => + getBindingDictionary(targetContainer), + ); + const bindingDictionary: interfaces.Lookup> = + getBindingDictionary(container); + + function copyDictionary( + origin: interfaces.Lookup>, + destination: interfaces.Lookup>, + ) { + origin.traverse( + (_key: interfaces.ServiceIdentifier, value: interfaces.Binding[]) => { + value.forEach((binding: interfaces.Binding) => { + destination.add(binding.serviceIdentifier, binding.clone()); + }); + }, + ); + } + + targetContainers.forEach( + (targetBindingDictionary: interfaces.Lookup) => { + copyDictionary(targetBindingDictionary, bindingDictionary); + }, + ); + + return container; + } + + public load(...modules: interfaces.ContainerModule[]): void { + // eslint-disable-next-line @typescript-eslint/typedef const getHelpers = this._getContainerModuleHelpersFactory(); for (const currentModule of modules) { + // eslint-disable-next-line @typescript-eslint/typedef const containerModuleHelpers = getHelpers(currentModule.id); currentModule.registry( - containerModuleHelpers.bindFunction as interfaces.Bind, + containerModuleHelpers.bindFunction, containerModuleHelpers.unbindFunction, containerModuleHelpers.isboundFunction, - containerModuleHelpers.rebindFunction as interfaces.Rebind, + containerModuleHelpers.rebindFunction, containerModuleHelpers.unbindAsyncFunction, containerModuleHelpers.onActivationFunction as interfaces.Container['onActivation'], containerModuleHelpers.onDeactivationFunction as interfaces.Container['onDeactivation'], @@ -139,16 +145,18 @@ class Container implements interfaces.Container { } public async loadAsync(...modules: interfaces.AsyncContainerModule[]) { + // eslint-disable-next-line @typescript-eslint/typedef const getHelpers = this._getContainerModuleHelpersFactory(); for (const currentModule of modules) { + // eslint-disable-next-line @typescript-eslint/typedef const containerModuleHelpers = getHelpers(currentModule.id); await currentModule.registry( - containerModuleHelpers.bindFunction as interfaces.Bind, + containerModuleHelpers.bindFunction, containerModuleHelpers.unbindFunction, containerModuleHelpers.isboundFunction, - containerModuleHelpers.rebindFunction as interfaces.Rebind, + containerModuleHelpers.rebindFunction, containerModuleHelpers.unbindAsyncFunction, containerModuleHelpers.onActivationFunction as interfaces.Container['onActivation'], containerModuleHelpers.onDeactivationFunction as interfaces.Container['onDeactivation'], @@ -157,8 +165,10 @@ class Container implements interfaces.Container { } public unload(...modules: interfaces.ContainerModuleBase[]): void { - modules.forEach((module) => { - const deactivations = this._removeModuleBindings(module.id); + modules.forEach((module: interfaces.ContainerModuleBase) => { + const deactivations: interfaces.Binding[] = this._removeModuleBindings( + module.id, + ); this._deactivateSingletons(deactivations); this._removeModuleHandlers(module.id); @@ -169,7 +179,9 @@ class Container implements interfaces.Container { ...modules: interfaces.ContainerModuleBase[] ): Promise { for (const module of modules) { - const deactivations = this._removeModuleBindings(module.id); + const deactivations: interfaces.Binding[] = this._removeModuleBindings( + module.id, + ); await this._deactivateSingletonsAsync(deactivations); this._removeModuleHandlers(module.id); @@ -180,8 +192,9 @@ class Container implements interfaces.Container { public bind( serviceIdentifier: interfaces.ServiceIdentifier, ): interfaces.BindingToSyntax { - const scope = this.options.defaultScope || BindingScopeEnum.Transient; - const binding = new Binding(serviceIdentifier, scope); + const scope: interfaces.BindingScope = + this.options.defaultScope || BindingScopeEnum.Transient; + const binding: Binding = new Binding(serviceIdentifier, scope); this._bindingDictionary.add(serviceIdentifier, binding as Binding); return new BindingToSyntax(binding); } @@ -203,7 +216,8 @@ class Container implements interfaces.Container { // Removes a type binding from the registry by its key public unbind(serviceIdentifier: interfaces.ServiceIdentifier): void { if (this._bindingDictionary.hasKey(serviceIdentifier)) { - const bindings = this._bindingDictionary.get(serviceIdentifier); + const bindings: interfaces.Binding[] = + this._bindingDictionary.get(serviceIdentifier); this._deactivateSingletons(bindings); } @@ -215,7 +229,8 @@ class Container implements interfaces.Container { serviceIdentifier: interfaces.ServiceIdentifier, ): Promise { if (this._bindingDictionary.hasKey(serviceIdentifier)) { - const bindings = this._bindingDictionary.get(serviceIdentifier); + const bindings: interfaces.Binding[] = + this._bindingDictionary.get(serviceIdentifier); await this._deactivateSingletonsAsync(bindings); } @@ -225,9 +240,11 @@ class Container implements interfaces.Container { // Removes all the type bindings from the registry public unbindAll(): void { - this._bindingDictionary.traverse((_key, value) => { - this._deactivateSingletons(value); - }); + this._bindingDictionary.traverse( + (_key: interfaces.ServiceIdentifier, value: interfaces.Binding[]) => { + this._deactivateSingletons(value); + }, + ); this._bindingDictionary = new Lookup>(); } @@ -235,9 +252,11 @@ class Container implements interfaces.Container { public async unbindAllAsync(): Promise { const promises: Promise[] = []; - this._bindingDictionary.traverse((_key, value) => { - promises.push(this._deactivateSingletonsAsync(value)); - }); + this._bindingDictionary.traverse( + (_key: interfaces.ServiceIdentifier, value: interfaces.Binding[]) => { + promises.push(this._deactivateSingletonsAsync(value)); + }, + ); await Promise.all(promises); @@ -268,7 +287,7 @@ class Container implements interfaces.Container { public isBound( serviceIdentifier: interfaces.ServiceIdentifier, ): boolean { - let bound = this._bindingDictionary.hasKey(serviceIdentifier); + let bound: boolean = this._bindingDictionary.hasKey(serviceIdentifier); if (!bound && this.parent) { bound = this.parent.isBound(serviceIdentifier); } @@ -295,13 +314,19 @@ class Container implements interfaces.Container { key: string | number | symbol, value: unknown, ): boolean { - let bound = false; + let bound: boolean = false; // verify if there are bindings available for serviceIdentifier on current binding dictionary if (this._bindingDictionary.hasKey(serviceIdentifier)) { - const bindings = this._bindingDictionary.get(serviceIdentifier); - const request = createMockRequest(this, serviceIdentifier, key, value); - bound = bindings.some((b) => b.constraint(request)); + const bindings: interfaces.Binding[] = + this._bindingDictionary.get(serviceIdentifier); + const request: interfaces.Request = createMockRequest( + this, + serviceIdentifier, + key, + value, + ); + bound = bindings.some((b: interfaces.Binding) => b.constraint(request)); } // verify if there is a parent container that could solve the request @@ -325,7 +350,8 @@ class Container implements interfaces.Container { } public restore(): void { - const snapshot = this._snapshots.pop(); + const snapshot: interfaces.ContainerSnapshot | undefined = + this._snapshots.pop(); if (snapshot === undefined) { throw new Error(ERROR_MSGS.NO_MORE_SNAPSHOTS_AVAILABLE); } @@ -339,7 +365,7 @@ class Container implements interfaces.Container { public createChild( containerOptions?: interfaces.ContainerOptions, ): Container { - const child = new Container(containerOptions || this.options); + const child: Container = new Container(containerOptions || this.options); child.parent = this; return child; } @@ -348,7 +374,10 @@ class Container implements interfaces.Container { const initial: interfaces.Next = this._middleware ? this._middleware : this._planAndResolve(); - this._middleware = middlewares.reduce((prev, curr) => curr(prev), initial); + this._middleware = middlewares.reduce( + (prev: interfaces.Next, curr: interfaces.Middleware) => curr(prev), + initial, + ); } public applyCustomMetadataReader(metadataReader: interfaces.MetadataReader) { @@ -359,7 +388,7 @@ class Container implements interfaces.Container { // The runtime identifier must be associated with only one binding // use getAll when the runtime identifier is associated with multiple bindings public get(serviceIdentifier: interfaces.ServiceIdentifier): T { - const getArgs = this._getNotAllArgs(serviceIdentifier, false); + const getArgs: GetArgs = this._getNotAllArgs(serviceIdentifier, false); return this._getButThrowIfAsync(getArgs) as T; } @@ -367,7 +396,7 @@ class Container implements interfaces.Container { public async getAsync( serviceIdentifier: interfaces.ServiceIdentifier, ): Promise { - const getArgs = this._getNotAllArgs(serviceIdentifier, false); + const getArgs: GetArgs = this._getNotAllArgs(serviceIdentifier, false); return this._get(getArgs) as Promise | T; } @@ -377,7 +406,12 @@ class Container implements interfaces.Container { key: string | number | symbol, value: unknown, ): T { - const getArgs = this._getNotAllArgs(serviceIdentifier, false, key, value); + const getArgs: GetArgs = this._getNotAllArgs( + serviceIdentifier, + false, + key, + value, + ); return this._getButThrowIfAsync(getArgs) as T; } @@ -387,7 +421,12 @@ class Container implements interfaces.Container { key: string | number | symbol, value: unknown, ): Promise { - const getArgs = this._getNotAllArgs(serviceIdentifier, false, key, value); + const getArgs: GetArgs = this._getNotAllArgs( + serviceIdentifier, + false, + key, + value, + ); return this._get(getArgs) as Promise | T; } @@ -399,7 +438,7 @@ class Container implements interfaces.Container { return this.getTagged(serviceIdentifier, METADATA_KEY.NAMED_TAG, named); } - public getNamedAsync( + public async getNamedAsync( serviceIdentifier: interfaces.ServiceIdentifier, named: string | number | symbol, ): Promise { @@ -413,15 +452,15 @@ class Container implements interfaces.Container { // Resolves a dependency by its runtime identifier // The runtime identifier can be associated with one or multiple bindings public getAll(serviceIdentifier: interfaces.ServiceIdentifier): T[] { - const getArgs = this._getAllArgs(serviceIdentifier); + const getArgs: GetArgs = this._getAllArgs(serviceIdentifier); return this._getButThrowIfAsync(getArgs) as T[]; } - public getAllAsync( + public async getAllAsync( serviceIdentifier: interfaces.ServiceIdentifier, ): Promise { - const getArgs = this._getAllArgs(serviceIdentifier); + const getArgs: GetArgs = this._getAllArgs(serviceIdentifier); return this._getAll(getArgs); } @@ -431,17 +470,27 @@ class Container implements interfaces.Container { key: string | number | symbol, value: unknown, ): T[] { - const getArgs = this._getNotAllArgs(serviceIdentifier, true, key, value); + const getArgs: GetArgs = this._getNotAllArgs( + serviceIdentifier, + true, + key, + value, + ); return this._getButThrowIfAsync(getArgs) as T[]; } - public getAllTaggedAsync( + public async getAllTaggedAsync( serviceIdentifier: interfaces.ServiceIdentifier, key: string | number | symbol, value: unknown, ): Promise { - const getArgs = this._getNotAllArgs(serviceIdentifier, true, key, value); + const getArgs: GetArgs = this._getNotAllArgs( + serviceIdentifier, + true, + key, + value, + ); return this._getAll(getArgs); } @@ -457,7 +506,7 @@ class Container implements interfaces.Container { ); } - public getAllNamedAsync( + public async getAllNamedAsync( serviceIdentifier: interfaces.ServiceIdentifier, named: string | number | symbol, ): Promise { @@ -469,31 +518,34 @@ class Container implements interfaces.Container { } public resolve(constructorFunction: interfaces.Newable) { - const isBound = this.isBound(constructorFunction); + const isBound: boolean = this.isBound(constructorFunction); if (!isBound) { this.bind(constructorFunction).toSelf(); } - const resolved = this.get(constructorFunction); + const resolved: T = this.get(constructorFunction); if (!isBound) { this.unbind(constructorFunction); } return resolved; } - private _preDestroy( + private _preDestroy( constructor: NewableFunction, - instance: T, + instance: unknown, ): Promise | void { if (Reflect.hasMetadata(METADATA_KEY.PRE_DESTROY, constructor)) { const data: interfaces.Metadata = Reflect.getMetadata( METADATA_KEY.PRE_DESTROY, constructor, - ); - return (instance as interfaces.Instance)[data.value as string]?.(); + ) as interfaces.Metadata; + + return (instance as interfaces.Instance)[ + data.value as string + ]?.(); } } private _removeModuleHandlers(moduleId: number): void { - const moduleActivationsHandlers = + const moduleActivationsHandlers: interfaces.ModuleActivationHandlers = this._moduleActivationStore.remove(moduleId); this._activations.removeIntersection( @@ -508,7 +560,7 @@ class Container implements interfaces.Container { moduleId: number, ): interfaces.Binding[] { return this._bindingDictionary.removeByCondition( - (binding) => binding.moduleId === moduleId, + (binding: interfaces.Binding) => binding.moduleId === moduleId, ); } @@ -516,19 +568,21 @@ class Container implements interfaces.Container { binding: Binding, instance: T, ): void | Promise { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const constructor: NewableFunction = + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access Object.getPrototypeOf(instance).constructor; try { if (this._deactivations.hasKey(binding.serviceIdentifier)) { - const result = this._deactivateContainer( + const result: void | Promise = this._deactivateContainer( instance, this._deactivations.get(binding.serviceIdentifier).values(), ); if (isPromise(result)) { return this._handleDeactivationError( - result.then(() => + result.then(async () => this._propagateContainerDeactivationThenBindingAndPreDestroyAsync( binding, instance, @@ -540,7 +594,7 @@ class Container implements interfaces.Container { } } - const propagateDeactivationResult = + const propagateDeactivationResult: void | Promise = this._propagateContainerDeactivationThenBindingAndPreDestroy( binding, instance, @@ -577,19 +631,22 @@ class Container implements interfaces.Container { } } - private _deactivateContainer( - instance: T, + private _deactivateContainer( + instance: unknown, deactivationsIterator: IterableIterator< interfaces.BindingDeactivation >, ): void | Promise { - let deactivation = deactivationsIterator.next(); + let deactivation: IteratorResult = + deactivationsIterator.next(); - while (deactivation.value) { - const result = deactivation.value(instance); + while (typeof deactivation.value === 'function') { + const result: unknown = ( + deactivation.value as (instance: unknown) => void | Promise + )(instance); if (isPromise(result)) { - return result.then(() => + return result.then(async () => this._deactivateContainerAsync(instance, deactivationsIterator), ); } @@ -598,22 +655,28 @@ class Container implements interfaces.Container { } } - private async _deactivateContainerAsync( - instance: T, + private async _deactivateContainerAsync( + instance: unknown, deactivationsIterator: IterableIterator< interfaces.BindingDeactivation >, ): Promise { - let deactivation = deactivationsIterator.next(); + let deactivation: IteratorResult = + deactivationsIterator.next(); - while (deactivation.value) { - await deactivation.value(instance); + while (typeof deactivation.value === 'function') { + await (deactivation.value as (instance: unknown) => Promise)( + instance, + ); deactivation = deactivationsIterator.next(); } } private _getContainerModuleHelpersFactory() { - const setModuleId = ( + const setModuleId: ( + bindingToSyntax: interfaces.BindingToSyntax, + moduleId: interfaces.ContainerModuleBase['id'], + ) => void = ( bindingToSyntax: interfaces.BindingToSyntax, moduleId: interfaces.ContainerModuleBase['id'], ) => { @@ -627,38 +690,58 @@ class Container implements interfaces.Container { )._binding.moduleId = moduleId; }; - const getBindFunction = - (moduleId: interfaces.ContainerModuleBase['id']) => - (serviceIdentifier: interfaces.ServiceIdentifier) => { - const bindingToSyntax = this.bind(serviceIdentifier); + const getBindFunction: ( + moduleId: interfaces.ContainerModuleBase['id'], + ) => interfaces.Bind = + (moduleId: interfaces.ContainerModuleBase['id']) => + (serviceIdentifier: interfaces.ServiceIdentifier) => { + const bindingToSyntax: interfaces.BindingToSyntax = + this.bind(serviceIdentifier); setModuleId(bindingToSyntax, moduleId); - return bindingToSyntax as BindingToSyntax; + return bindingToSyntax; }; - const getUnbindFunction = - () => (serviceIdentifier: interfaces.ServiceIdentifier) => { - return this.unbind(serviceIdentifier); - }; + const getUnbindFunction: () => ( + serviceIdentifier: interfaces.ServiceIdentifier, + ) => void = () => (serviceIdentifier: interfaces.ServiceIdentifier) => { + this.unbind(serviceIdentifier); + }; - const getUnbindAsyncFunction = - () => (serviceIdentifier: interfaces.ServiceIdentifier) => { + const getUnbindAsyncFunction: () => ( + serviceIdentifier: interfaces.ServiceIdentifier, + ) => Promise = + () => + async ( + serviceIdentifier: interfaces.ServiceIdentifier, + ): Promise => { return this.unbindAsync(serviceIdentifier); }; - const getIsboundFunction = - () => (serviceIdentifier: interfaces.ServiceIdentifier) => { + const getIsboundFunction: () => ( + serviceIdentifier: interfaces.ServiceIdentifier, + ) => boolean = + () => + (serviceIdentifier: interfaces.ServiceIdentifier): boolean => { return this.isBound(serviceIdentifier); }; - const getRebindFunction = - (moduleId: interfaces.ContainerModuleBase['id']) => - (serviceIdentifier: interfaces.ServiceIdentifier) => { - const bindingToSyntax = this.rebind(serviceIdentifier); + const getRebindFunction: ( + moduleId: interfaces.ContainerModuleBase['id'], + ) => interfaces.Rebind = + (moduleId: interfaces.ContainerModuleBase['id']) => + (serviceIdentifier: interfaces.ServiceIdentifier) => { + const bindingToSyntax: interfaces.BindingToSyntax = + this.rebind(serviceIdentifier); setModuleId(bindingToSyntax, moduleId); - return bindingToSyntax as BindingToSyntax; + return bindingToSyntax; }; - const getOnActivationFunction = + const getOnActivationFunction: ( + moduleId: interfaces.ContainerModuleBase['id'], + ) => ( + serviceIdentifier: interfaces.ServiceIdentifier, + onActivation: interfaces.BindingActivation, + ) => void = (moduleId: interfaces.ContainerModuleBase['id']) => ( serviceIdentifier: interfaces.ServiceIdentifier, @@ -672,7 +755,12 @@ class Container implements interfaces.Container { this.onActivation(serviceIdentifier, onActivation); }; - const getOnDeactivationFunction = + const getOnDeactivationFunction: ( + moduleId: interfaces.ContainerModuleBase['id'], + ) => ( + serviceIdentifier: interfaces.ServiceIdentifier, + onDeactivation: interfaces.BindingDeactivation, + ) => void = (moduleId: interfaces.ContainerModuleBase['id']) => ( serviceIdentifier: interfaces.ServiceIdentifier, @@ -692,11 +780,11 @@ class Container implements interfaces.Container { onActivationFunction: getOnActivationFunction(mId), onDeactivationFunction: getOnDeactivationFunction(mId), rebindFunction: getRebindFunction(mId), - unbindFunction: getUnbindFunction(), unbindAsyncFunction: getUnbindAsyncFunction(), + unbindFunction: getUnbindFunction(), }); } - private _getAll(getArgs: GetArgs): Promise { + private async _getAll(getArgs: GetArgs): Promise { return Promise.all(this._get(getArgs) as (Promise | T)[]); } // Prepares arguments required for resolution and @@ -705,11 +793,11 @@ class Container implements interfaces.Container { private _get(getArgs: GetArgs): interfaces.ContainerResolution { const planAndResolveArgs: interfaces.NextArgs = { ...getArgs, - contextInterceptor: (context) => context, + contextInterceptor: (context: interfaces.Context) => context, targetType: TargetTypeEnum.Variable, }; if (this._middleware) { - const middlewareResult = this._middleware(planAndResolveArgs); + const middlewareResult: unknown = this._middleware(planAndResolveArgs); if (middlewareResult === undefined || middlewareResult === null) { throw new Error(ERROR_MSGS.INVALID_MIDDLEWARE_RETURN); } @@ -720,7 +808,7 @@ class Container implements interfaces.Container { } private _getButThrowIfAsync(getArgs: GetArgs): T | T[] { - const result = this._get(getArgs); + const result: interfaces.ContainerResolution = this._get(getArgs); if (isPromiseOrContainsPromise(result)) { throw new Error(ERROR_MSGS.LAZY_IN_SYNC(getArgs.serviceIdentifier)); @@ -750,8 +838,8 @@ class Container implements interfaces.Container { const getNotAllArgs: GetArgs = { avoidConstraints: false, isMultiInject, - serviceIdentifier, key, + serviceIdentifier, value, }; @@ -764,9 +852,11 @@ class Container implements interfaces.Container { private _planAndResolve(): ( args: interfaces.NextArgs, ) => interfaces.ContainerResolution { - return (args: interfaces.NextArgs) => { + return ( + args: interfaces.NextArgs, + ): T | Promise | (T | Promise)[] => { // create a plan - let context = plan( + let context: interfaces.Context = plan( this._metadataReader, this, args.isMultiInject, @@ -781,7 +871,7 @@ class Container implements interfaces.Container { context = args.contextInterceptor(context); // resolve plan - const result = resolve(context); + const result: T | Promise | (T | Promise)[] = resolve(context); return result; }; @@ -795,7 +885,7 @@ class Container implements interfaces.Container { } if (isPromise(binding.cache)) { - return binding.cache.then((resolved) => + return binding.cache.then((resolved: unknown): void | Promise => this._deactivate(binding, resolved), ); } @@ -805,7 +895,7 @@ class Container implements interfaces.Container { private _deactivateSingletons(bindings: Binding[]): void { for (const binding of bindings) { - const result = this._deactivateIfSingleton(binding); + const result: void | Promise = this._deactivateIfSingleton(binding); if (isPromise(result)) { throw new Error(ERROR_MSGS.ASYNC_UNBIND_REQUIRED); @@ -816,7 +906,12 @@ class Container implements interfaces.Container { private async _deactivateSingletonsAsync( bindings: Binding[], ): Promise { - await Promise.all(bindings.map((b) => this._deactivateIfSingleton(b))); + await Promise.all( + bindings.map( + async (b: interfaces.Binding): Promise => + this._deactivateIfSingleton(b), + ), + ); } private _propagateContainerDeactivationThenBindingAndPreDestroy( @@ -856,7 +951,7 @@ class Container implements interfaces.Container { ): void { try { this._bindingDictionary.remove(serviceIdentifier); - } catch (e) { + } catch (_e: unknown) { throw new Error( `${ERROR_MSGS.CANNOT_UNBIND} ${getServiceIdentifierAsString(serviceIdentifier)}`, ); @@ -869,10 +964,12 @@ class Container implements interfaces.Container { constructor: NewableFunction, ): void | Promise { if (typeof binding.onDeactivation === 'function') { - const result = binding.onDeactivation(instance); + const result: void | Promise = binding.onDeactivation(instance); if (isPromise(result)) { - return result.then(() => this._preDestroy(constructor, instance)); + return result.then((): void | Promise => + this._preDestroy(constructor, instance), + ); } } diff --git a/src/container/container_module.ts b/src/container/container_module.ts index f722ae449..997fd4c4a 100644 --- a/src/container/container_module.ts +++ b/src/container/container_module.ts @@ -5,7 +5,7 @@ export class ContainerModule implements interfaces.ContainerModule { public id: number; public registry: interfaces.ContainerModuleCallBack; - public constructor(registry: interfaces.ContainerModuleCallBack) { + constructor(registry: interfaces.ContainerModuleCallBack) { this.id = id(); this.registry = registry; } @@ -15,7 +15,7 @@ export class AsyncContainerModule implements interfaces.AsyncContainerModule { public id: number; public registry: interfaces.AsyncContainerModuleCallBack; - public constructor(registry: interfaces.AsyncContainerModuleCallBack) { + constructor(registry: interfaces.AsyncContainerModuleCallBack) { this.id = id(); this.registry = registry; } diff --git a/src/container/container_snapshot.ts b/src/container/container_snapshot.ts index d26bb1bd4..f8d299823 100644 --- a/src/container/container_snapshot.ts +++ b/src/container/container_snapshot.ts @@ -16,7 +16,7 @@ class ContainerSnapshot implements interfaces.ContainerSnapshot { deactivations: interfaces.Lookup>, moduleActivationStore: interfaces.ModuleActivationStore, ) { - const snapshot = new ContainerSnapshot(); + const snapshot: ContainerSnapshot = new ContainerSnapshot(); snapshot.bindings = bindings; snapshot.middleware = middleware; snapshot.deactivations = deactivations; diff --git a/src/container/lookup.ts b/src/container/lookup.ts index 5190555ab..dd7d87d28 100644 --- a/src/container/lookup.ts +++ b/src/container/lookup.ts @@ -4,9 +4,9 @@ import { isClonable } from '../utils/clonable'; class Lookup implements interfaces.Lookup { // dictionary used store multiple values for each key - private _map: Map; + private readonly _map: Map; - public constructor() { + constructor() { this._map = new Map(); } @@ -16,15 +16,13 @@ class Lookup implements interfaces.Lookup { // adds a new entry to _map public add(serviceIdentifier: interfaces.ServiceIdentifier, value: T): void { - if (serviceIdentifier === null || serviceIdentifier === undefined) { - throw new Error(ERROR_MSGS.NULL_ARGUMENT); - } + this._checkNonNulish(serviceIdentifier); if (value === null || value === undefined) { throw new Error(ERROR_MSGS.NULL_ARGUMENT); } - const entry = this._map.get(serviceIdentifier); + const entry: T[] | undefined = this._map.get(serviceIdentifier); if (entry !== undefined) { entry.push(value); } else { @@ -34,11 +32,9 @@ class Lookup implements interfaces.Lookup { // gets the value of a entry by its key (serviceIdentifier) public get(serviceIdentifier: interfaces.ServiceIdentifier): T[] { - if (serviceIdentifier === null || serviceIdentifier === undefined) { - throw new Error(ERROR_MSGS.NULL_ARGUMENT); - } + this._checkNonNulish(serviceIdentifier); - const entry = this._map.get(serviceIdentifier); + const entry: T[] | undefined = this._map.get(serviceIdentifier); if (entry !== undefined) { return entry; @@ -49,9 +45,7 @@ class Lookup implements interfaces.Lookup { // removes a entry from _map by its key (serviceIdentifier) public remove(serviceIdentifier: interfaces.ServiceIdentifier): void { - if (serviceIdentifier === null || serviceIdentifier === undefined) { - throw new Error(ERROR_MSGS.NULL_ARGUMENT); - } + this._checkNonNulish(serviceIdentifier); if (!this._map.delete(serviceIdentifier)) { throw new Error(ERROR_MSGS.KEY_NOT_FOUND); @@ -64,14 +58,16 @@ class Lookup implements interfaces.Lookup { serviceIdentifier: interfaces.ServiceIdentifier, value: T[], ) => { - const lookupActivations = lookup.hasKey(serviceIdentifier) + const lookupActivations: T[] | undefined = lookup.hasKey( + serviceIdentifier, + ) ? lookup.get(serviceIdentifier) : undefined; if (lookupActivations !== undefined) { - const filteredValues = value.filter( - (lookupValue) => + const filteredValues: T[] = value.filter( + (lookupValue: T) => !lookupActivations.some( - (moduleActivation) => lookupValue === moduleActivation, + (moduleActivation: T) => lookupValue === moduleActivation, ), ); @@ -83,11 +79,11 @@ class Lookup implements interfaces.Lookup { public removeByCondition(condition: (item: T) => boolean): T[] { const removals: T[] = []; - this._map.forEach((entries, key) => { + this._map.forEach((entries: T[], key: interfaces.ServiceIdentifier) => { const updatedEntries: T[] = []; for (const entry of entries) { - const remove = condition(entry); + const remove: boolean = condition(entry); if (remove) { removals.push(entry); } else { @@ -103,9 +99,7 @@ class Lookup implements interfaces.Lookup { // returns true if _map contains a key (serviceIdentifier) public hasKey(serviceIdentifier: interfaces.ServiceIdentifier): boolean { - if (serviceIdentifier === null || serviceIdentifier === undefined) { - throw new Error(ERROR_MSGS.NULL_ARGUMENT); - } + this._checkNonNulish(serviceIdentifier); return this._map.has(serviceIdentifier); } @@ -113,10 +107,12 @@ class Lookup implements interfaces.Lookup { // returns a new Lookup instance; note: this is not a deep clone, only Lookup related data structure (dictionary) is // cloned, content remains the same public clone(): interfaces.Lookup { - const copy = new Lookup(); + const copy: Lookup = new Lookup(); - this._map.forEach((value, key) => { - value.forEach((b) => copy.add(key, isClonable(b) ? b.clone() : b)); + this._map.forEach((value: T[], key: interfaces.ServiceIdentifier) => { + value.forEach((b: T) => { + copy.add(key, isClonable(b) ? b.clone() : b); + }); }); return copy; @@ -125,11 +121,17 @@ class Lookup implements interfaces.Lookup { public traverse( func: (key: interfaces.ServiceIdentifier, value: T[]) => void, ): void { - this._map.forEach((value, key) => { + this._map.forEach((value: T[], key: interfaces.ServiceIdentifier) => { func(key, value); }); } + private _checkNonNulish(value: unknown): void { + if (value == null) { + throw new Error(ERROR_MSGS.NULL_ARGUMENT); + } + } + private _setValue( serviceIdentifier: interfaces.ServiceIdentifier, value: T[], diff --git a/src/container/module_activation_store.ts b/src/container/module_activation_store.ts index cd3eb3504..2f0d87118 100644 --- a/src/container/module_activation_store.ts +++ b/src/container/module_activation_store.ts @@ -2,15 +2,20 @@ import { interfaces } from '../interfaces/interfaces'; import { Lookup } from './lookup'; export class ModuleActivationStore implements interfaces.ModuleActivationStore { - private _map = new Map(); + private readonly _map: Map = + new Map(); public remove(moduleId: number): interfaces.ModuleActivationHandlers { - if (this._map.has(moduleId)) { - const handlers = this._map.get(moduleId); - this._map.delete(moduleId); - return handlers!; + const handlers: interfaces.ModuleActivationHandlers | undefined = + this._map.get(moduleId); + + if (handlers === undefined) { + return this._getEmptyHandlersStore(); } - return this._getEmptyHandlersStore(); + + this._map.delete(moduleId); + + return handlers; } public addDeactivation( @@ -36,14 +41,19 @@ export class ModuleActivationStore implements interfaces.ModuleActivationStore { } public clone(): interfaces.ModuleActivationStore { - const clone = new ModuleActivationStore(); + const clone: ModuleActivationStore = new ModuleActivationStore(); - this._map.forEach((handlersStore, moduleId) => { - clone._map.set(moduleId, { - onActivations: handlersStore.onActivations.clone(), - onDeactivations: handlersStore.onDeactivations.clone(), - }); - }); + this._map.forEach( + ( + handlersStore: interfaces.ModuleActivationHandlers, + moduleId: number, + ) => { + clone._map.set(moduleId, { + onActivations: handlersStore.onActivations.clone(), + onDeactivations: handlersStore.onDeactivations.clone(), + }); + }, + ); return clone; } diff --git a/src/interfaces/interfaces.ts b/src/interfaces/interfaces.ts index 3edb9134d..b7e1f459a 100644 --- a/src/interfaces/interfaces.ts +++ b/src/interfaces/interfaces.ts @@ -1,5 +1,7 @@ +/* eslint-disable @typescript-eslint/naming-convention */ import { FactoryType } from '../utils/factory_type'; +// eslint-disable-next-line @typescript-eslint/no-namespace namespace interfaces { export type DynamicValue = (context: interfaces.Context) => T | Promise; export type ContainerResolution = T | Promise | (T | Promise)[]; @@ -47,6 +49,7 @@ namespace interfaces { Variable: interfaces.TargetType; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Newable = new (...args: any[]) => T; export type Instance = T & Record void>; @@ -128,7 +131,9 @@ namespace interfaces { } export type Provider = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any ...args: any[] + // eslint-disable-next-line @typescript-eslint/no-explicit-any ) => ((...args: any[]) => Promise) | Promise; export type ProviderCreator = (context: Context) => Provider; @@ -143,6 +148,7 @@ namespace interfaces { value?: unknown; } + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents export type Next = (args: NextArgs) => unknown | unknown[]; export type Middleware = (next: Next) => Next; @@ -480,8 +486,8 @@ namespace interfaces { } export interface ConstraintFunction { - metaData?: Metadata; (request: Request | null): boolean; + metaData?: Metadata; } export interface MetadataReader { diff --git a/src/inversify.ts b/src/inversify.ts index a1d1119c0..ddafcf926 100644 --- a/src/inversify.ts +++ b/src/inversify.ts @@ -1,4 +1,5 @@ import * as keys from './constants/metadata_keys'; +// eslint-disable-next-line @typescript-eslint/typedef export const METADATA_KEY = keys; export { Container } from './container/container'; export { diff --git a/src/planning/context.ts b/src/planning/context.ts index 4af56a614..fb4bfc859 100644 --- a/src/planning/context.ts +++ b/src/planning/context.ts @@ -7,7 +7,7 @@ class Context implements interfaces.Context { public plan!: interfaces.Plan; public currentRequest!: interfaces.Request; - public constructor(container: interfaces.Container) { + constructor(container: interfaces.Container) { this.id = id(); this.container = container; } diff --git a/src/planning/metadata.ts b/src/planning/metadata.ts index b1d5f4ff5..5e504db82 100644 --- a/src/planning/metadata.ts +++ b/src/planning/metadata.ts @@ -5,7 +5,7 @@ class Metadata implements interfaces.Metadata { public key: string | number | symbol; public value: unknown; - public constructor(key: string | number | symbol, value: unknown) { + constructor(key: string | number | symbol, value: unknown) { this.key = key; this.value = value; } diff --git a/src/planning/metadata_reader.ts b/src/planning/metadata_reader.ts index fe36683f9..7c1f9d679 100644 --- a/src/planning/metadata_reader.ts +++ b/src/planning/metadata_reader.ts @@ -6,20 +6,20 @@ class MetadataReader implements interfaces.MetadataReader { constructorFunc: NewableFunction, ): interfaces.ConstructorMetadata { // TypeScript compiler generated annotations - const compilerGeneratedMetadata = Reflect.getMetadata( - METADATA_KEY.PARAM_TYPES, - constructorFunc, - ); + const compilerGeneratedMetadata: NewableFunction[] | undefined = + Reflect.getMetadata(METADATA_KEY.PARAM_TYPES, constructorFunc) as + | NewableFunction[] + | undefined; // User generated constructor annotations - const userGeneratedMetadata = Reflect.getMetadata( - METADATA_KEY.TAGGED, - constructorFunc, - ); + const userGeneratedMetadata: interfaces.MetadataMap | undefined = + Reflect.getMetadata(METADATA_KEY.TAGGED, constructorFunc) as + | interfaces.MetadataMap + | undefined; return { compilerGeneratedMetadata, - userGeneratedMetadata: userGeneratedMetadata || {}, + userGeneratedMetadata: userGeneratedMetadata ?? {}, }; } @@ -27,8 +27,11 @@ class MetadataReader implements interfaces.MetadataReader { constructorFunc: NewableFunction, ): interfaces.MetadataMap { // User generated properties annotations - const userGeneratedMetadata = - Reflect.getMetadata(METADATA_KEY.TAGGED_PROP, constructorFunc) || []; + const userGeneratedMetadata: interfaces.MetadataMap | undefined = + (Reflect.getMetadata(METADATA_KEY.TAGGED_PROP, constructorFunc) as + | interfaces.MetadataMap + | undefined) ?? {}; + return userGeneratedMetadata; } } diff --git a/src/planning/plan.ts b/src/planning/plan.ts index af33ed545..421534d47 100644 --- a/src/planning/plan.ts +++ b/src/planning/plan.ts @@ -4,7 +4,7 @@ class Plan implements interfaces.Plan { public parentContext: interfaces.Context; public rootRequest: interfaces.Request; - public constructor( + constructor( parentContext: interfaces.Context, rootRequest: interfaces.Request, ) { diff --git a/src/planning/planner.ts b/src/planning/planner.ts index 4d1b48d60..74c15b144 100644 --- a/src/planning/planner.ts +++ b/src/planning/planner.ts @@ -39,11 +39,11 @@ function _createTarget( key?: string | number | symbol, value?: unknown, ): interfaces.Target { - const metadataKey = isMultiInject + const metadataKey: string = isMultiInject ? METADATA_KEY.MULTI_INJECT_TAG : METADATA_KEY.INJECT_TAG; - const injectMetadata = new Metadata(metadataKey, serviceIdentifier); - const target = new Target( + const injectMetadata: Metadata = new Metadata(metadataKey, serviceIdentifier); + const target: Target = new Target( targetType, name, serviceIdentifier, @@ -51,7 +51,7 @@ function _createTarget( ); if (key !== undefined) { - const tagMetadata = new Metadata(key, value); + const tagMetadata: Metadata = new Metadata(key, value); target.metadata.push(tagMetadata); } @@ -65,13 +65,16 @@ function _getActiveBindings( parentRequest: interfaces.Request | null, target: interfaces.Target, ): interfaces.Binding[] { - let bindings = getBindings(context.container, target.serviceIdentifier); + let bindings: interfaces.Binding[] = getBindings( + context.container, + target.serviceIdentifier, + ); let activeBindings: interfaces.Binding[] = []; // automatic binding if ( bindings.length === BindingCount.NoBindingsAvailable && - context.container.options.autoBindInjectable && + context.container.options.autoBindInjectable === true && typeof target.serviceIdentifier === 'function' && metadataReader.getConstructorMetadata(target.serviceIdentifier) .compilerGeneratedMetadata @@ -83,8 +86,8 @@ function _getActiveBindings( // multiple bindings available if (!avoidConstraints) { // apply constraints if available to reduce the number of active bindings - activeBindings = bindings.filter((binding) => { - const request = new Request( + activeBindings = bindings.filter((binding: interfaces.Binding) => { + const request: Request = new Request( binding.serviceIdentifier, context, parentRequest, @@ -121,9 +124,9 @@ function _validateActiveBindingCount( if (target.isOptional()) { return bindings; } else { - const serviceIdentifierString = + const serviceIdentifierString: string = getServiceIdentifierAsString(serviceIdentifier); - let msg = ERROR_MSGS.NOT_REGISTERED; + let msg: string = ERROR_MSGS.NOT_REGISTERED; msg += listMetadataForTarget(serviceIdentifierString, target); msg += listRegisteredBindingsForServiceIdentifier( container, @@ -138,9 +141,9 @@ function _validateActiveBindingCount( case BindingCount.MultipleBindingsAvailable: default: if (!target.isArray()) { - const serviceIdentifierString = + const serviceIdentifierString: string = getServiceIdentifierAsString(serviceIdentifier); - let msg = `${ERROR_MSGS.AMBIGUOUS_MATCH} ${serviceIdentifierString}`; + let msg: string = `${ERROR_MSGS.AMBIGUOUS_MATCH} ${serviceIdentifierString}`; msg += listRegisteredBindingsForServiceIdentifier( container, serviceIdentifierString, @@ -181,7 +184,7 @@ function _createSubRequests( target, ); - const thePlan = new Plan(context, childRequest); + const thePlan: Plan = new Plan(context, childRequest); context.addPlan(thePlan); } else { activeBindings = _getActiveBindings( @@ -198,7 +201,7 @@ function _createSubRequests( ); } - activeBindings.forEach((binding) => { + activeBindings.forEach((binding: interfaces.Binding) => { let subChildRequest: interfaces.Request | null = null; if (target.isArray()) { @@ -208,7 +211,7 @@ function _createSubRequests( target, ); } else { - if (binding.cache) { + if (binding.cache !== null) { return; } subChildRequest = childRequest; @@ -218,22 +221,22 @@ function _createSubRequests( binding.type === BindingTypeEnum.Instance && binding.implementationType !== null ) { - const dependencies = getDependencies( + const dependencies: interfaces.Target[] = getDependencies( metadataReader, binding.implementationType as NewableFunction, ); - if (!context.container.options.skipBaseClassChecks) { + if (context.container.options.skipBaseClassChecks !== true) { // Throw if a derived class does not implement its constructor explicitly // We do this to prevent errors when a base class (parent) has dependencies // and one of the derived classes (children) has no dependencies - const baseClassDependencyCount = getBaseClassDependencyCount( + const baseClassDependencyCount: number = getBaseClassDependencyCount( metadataReader, binding.implementationType as NewableFunction, ); if (dependencies.length < baseClassDependencyCount) { - const error = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH( + const error: string = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH( getFunctionName(binding.implementationType as NewableFunction), ); throw new Error(error); @@ -259,7 +262,8 @@ function getBindings( serviceIdentifier: interfaces.ServiceIdentifier, ): interfaces.Binding[] { let bindings: interfaces.Binding[] = []; - const bindingDictionary = getBindingDictionary(container); + const bindingDictionary: interfaces.Lookup = + getBindingDictionary(container); if (bindingDictionary.hasKey(serviceIdentifier)) { bindings = bindingDictionary.get( @@ -281,10 +285,10 @@ function plan( serviceIdentifier: interfaces.ServiceIdentifier, key?: string | number | symbol, value?: unknown, - avoidConstraints = false, + avoidConstraints: boolean = false, ): interfaces.Context { - const context = new Context(container); - const target = _createTarget( + const context: Context = new Context(container); + const target: interfaces.Target = _createTarget( isMultiInject, targetType, serviceIdentifier, @@ -317,14 +321,20 @@ function createMockRequest( key: string | number | symbol, value: unknown, ): interfaces.Request { - const target = new Target( + const target: Target = new Target( TargetTypeEnum.Variable, '', serviceIdentifier, new Metadata(key, value), ); - const context = new Context(container); - const request = new Request(serviceIdentifier, context, null, [], target); + const context: Context = new Context(container); + const request: Request = new Request( + serviceIdentifier, + context, + null, + [], + target, + ); return request; } diff --git a/src/planning/queryable_string.ts b/src/planning/queryable_string.ts index 067d25ba4..c9a63ea87 100644 --- a/src/planning/queryable_string.ts +++ b/src/planning/queryable_string.ts @@ -1,9 +1,9 @@ import { interfaces } from '../interfaces/interfaces'; class QueryableString implements interfaces.QueryableString { - private str: string; + private readonly str: string; - public constructor(str: string) { + constructor(str: string) { this.str = str; } @@ -12,13 +12,17 @@ class QueryableString implements interfaces.QueryableString { } public endsWith(searchString: string): boolean { - let reverseString = ''; - const reverseSearchString = searchString.split('').reverse().join(''); + let reverseString: string = ''; + const reverseSearchString: string = searchString + .split('') + .reverse() + .join(''); reverseString = this.str.split('').reverse().join(''); return this.startsWith.call({ str: reverseString }, reverseSearchString); } public contains(searchString: string): boolean { + // eslint-disable-next-line @typescript-eslint/no-magic-numbers return this.str.indexOf(searchString) !== -1; } diff --git a/src/planning/reflection_utils.ts b/src/planning/reflection_utils.ts index 15dc4d896..7dd45f4be 100644 --- a/src/planning/reflection_utils.ts +++ b/src/planning/reflection_utils.ts @@ -11,7 +11,7 @@ function getDependencies( metadataReader: interfaces.MetadataReader, func: NewableFunction, ): interfaces.Target[] { - const constructorName = getFunctionName(func); + const constructorName: string = getFunctionName(func); return getTargets(metadataReader, constructorName, func, false); } @@ -21,31 +21,35 @@ function getTargets( func: NewableFunction, isBaseClass: boolean, ): interfaces.Target[] { - const metadata = metadataReader.getConstructorMetadata(func); + const metadata: interfaces.ConstructorMetadata = + metadataReader.getConstructorMetadata(func); // TypeScript compiler generated annotations - const serviceIdentifiers = metadata.compilerGeneratedMetadata; + const serviceIdentifiers: NewableFunction[] | undefined = + metadata.compilerGeneratedMetadata; // All types resolved must be annotated with @injectable if (serviceIdentifiers === undefined) { - const msg = `${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} ${constructorName}.`; + const msg: string = `${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} ${constructorName}.`; throw new Error(msg); } // User generated annotations - const constructorArgsMetadata = metadata.userGeneratedMetadata; + const constructorArgsMetadata: interfaces.MetadataMap = + metadata.userGeneratedMetadata; - const keys = Object.keys(constructorArgsMetadata); - const hasUserDeclaredUnknownInjections = func.length === 0 && keys.length > 0; - const hasOptionalParameters = keys.length > func.length; + const keys: string[] = Object.keys(constructorArgsMetadata); + const hasUserDeclaredUnknownInjections: boolean = + func.length === 0 && keys.length > 0; + const hasOptionalParameters: boolean = keys.length > func.length; - const iterations = + const iterations: number = hasUserDeclaredUnknownInjections || hasOptionalParameters ? keys.length : func.length; // Target instances that represent constructor arguments to be injected - const constructorTargets = getConstructorArgsAsTargets( + const constructorTargets: interfaces.Target[] = getConstructorArgsAsTargets( isBaseClass, constructorName, serviceIdentifiers, @@ -54,13 +58,16 @@ function getTargets( ); // Target instances that represent properties to be injected - const propertyTargets = getClassPropsAsTargets( + const propertyTargets: interfaces.Target[] = getClassPropsAsTargets( metadataReader, func, constructorName, ); - const targets = [...constructorTargets, ...propertyTargets]; + const targets: interfaces.Target[] = [ + ...constructorTargets, + ...propertyTargets, + ]; return targets; } @@ -72,17 +79,22 @@ function getConstructorArgsAsTarget( constructorArgsMetadata: interfaces.MetadataMap, ): Target | null { // Create map from array of metadata for faster access to metadata - const targetMetadata = constructorArgsMetadata[index.toString()] || []; + const targetMetadata: interfaces.Metadata[] = + constructorArgsMetadata[index.toString()] || []; + // eslint-disable-next-line @typescript-eslint/typedef const metadata = formatTargetMetadata(targetMetadata); - const isManaged = metadata.unmanaged !== true; + const isManaged: boolean = metadata.unmanaged !== true; // Take types to be injected from user-generated metadata // if not available use compiler-generated metadata - let serviceIdentifier = serviceIdentifiers[index]; - const injectIdentifier = metadata.inject || metadata.multiInject; - serviceIdentifier = ( - injectIdentifier ? injectIdentifier : serviceIdentifier - ) as interfaces.ServiceIdentifier | undefined; + let serviceIdentifier: interfaces.ServiceIdentifier | undefined = + serviceIdentifiers[index]; + const injectIdentifier: unknown = metadata.inject ?? metadata.multiInject; + serviceIdentifier = + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + (injectIdentifier ? injectIdentifier : serviceIdentifier) as + | interfaces.ServiceIdentifier + | undefined; // we unwrap LazyServiceIdentifier wrappers to allow circular dependencies on symbols if (serviceIdentifier instanceof LazyServiceIdentifier) { @@ -92,17 +104,17 @@ function getConstructorArgsAsTarget( // Types Object and Function are too ambiguous to be resolved // user needs to generate metadata manually for those if (isManaged) { - const isObject = serviceIdentifier === Object; - const isFunction = serviceIdentifier === Function; - const isUndefined = serviceIdentifier === undefined; - const isUnknownType = isObject || isFunction || isUndefined; + const isObject: boolean = serviceIdentifier === Object; + const isFunction: boolean = serviceIdentifier === Function; + const isUndefined: boolean = serviceIdentifier === undefined; + const isUnknownType: boolean = isObject || isFunction || isUndefined; if (!isBaseClass && isUnknownType) { - const msg = `${ERROR_MSGS.MISSING_INJECT_ANNOTATION} argument ${index} in class ${constructorName}.`; + const msg: string = `${ERROR_MSGS.MISSING_INJECT_ANNOTATION} argument ${index.toString()} in class ${constructorName}.`; throw new Error(msg); } - const target = new Target( + const target: Target = new Target( TargetTypeEnum.ConstructorArgument, metadata.targetName as string | symbol, serviceIdentifier as interfaces.ServiceIdentifier, @@ -122,15 +134,15 @@ function getConstructorArgsAsTargets( iterations: number, ): interfaces.Target[] { const targets: interfaces.Target[] = []; - for (let i = 0; i < iterations; i++) { - const index = i; - const target = getConstructorArgsAsTarget( - index, + for (let i: number = 0; i < iterations; i++) { + const target: Target | null = getConstructorArgsAsTarget( + i, isBaseClass, constructorName, serviceIdentifiers, constructorArgsMetadata, ); + if (target !== null) { targets.push(target); } @@ -140,16 +152,19 @@ function getConstructorArgsAsTargets( } function _getServiceIdentifierForProperty( - inject: string | symbol | unknown, - multiInject: object | unknown, + inject: interfaces.ServiceIdentifier | undefined, + multiInject: interfaces.ServiceIdentifier | undefined, propertyName: string | symbol, className: string, -) { - const serviceIdentifier = inject || multiInject; +): interfaces.ServiceIdentifier { + const serviceIdentifier: interfaces.ServiceIdentifier | undefined = + inject ?? multiInject; + if (serviceIdentifier === undefined) { - const msg = `${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} for property ${String(propertyName)} in class ${className}.`; + const msg: string = `${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} for property ${String(propertyName)} in class ${className}.`; throw new Error(msg); } + return serviceIdentifier; } @@ -158,47 +173,53 @@ function getClassPropsAsTargets( constructorFunc: NewableFunction, constructorName: string, ) { - const classPropsMetadata = + const classPropsMetadata: interfaces.MetadataMap = metadataReader.getPropertiesMetadata(constructorFunc); let targets: interfaces.Target[] = []; - const symbolKeys = Object.getOwnPropertySymbols(classPropsMetadata); + const symbolKeys: symbol[] = Object.getOwnPropertySymbols(classPropsMetadata); const stringKeys: (string | symbol)[] = Object.keys(classPropsMetadata); const keys: (string | symbol)[] = stringKeys.concat(symbolKeys); for (const key of keys) { // the metadata for the property being injected - const targetMetadata = classPropsMetadata[key] as interfaces.Metadata[]; + const targetMetadata: interfaces.Metadata[] = classPropsMetadata[ + key + ] as interfaces.Metadata[]; // the metadata formatted for easier access + // eslint-disable-next-line @typescript-eslint/typedef const metadata = formatTargetMetadata(targetMetadata); - const identifier = metadata.targetName || key; + const identifier: unknown = metadata.targetName ?? key; // Take types to be injected from user-generated metadata - const serviceIdentifier = _getServiceIdentifierForProperty( - metadata.inject, - metadata.multiInject, - key, - constructorName, - ); + const serviceIdentifier: interfaces.ServiceIdentifier = + _getServiceIdentifierForProperty( + metadata.inject as interfaces.ServiceIdentifier | undefined, + metadata.multiInject as interfaces.ServiceIdentifier | undefined, + key, + constructorName, + ); // The property target - const target = new Target( + const target: Target = new Target( TargetTypeEnum.ClassProperty, identifier as string | symbol, - serviceIdentifier as interfaces.ServiceIdentifier, + serviceIdentifier, ); + target.metadata = targetMetadata; targets.push(target); } // Check if base class has injected properties - const baseConstructor = Object.getPrototypeOf( + const baseConstructor: NewableFunction = Object.getPrototypeOf( constructorFunc.prototype, - ).constructor; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ).constructor as NewableFunction; - if (baseConstructor !== Object) { - const baseTargets = getClassPropsAsTargets( + if (baseConstructor !== (Object as unknown as NewableFunction)) { + const baseTargets: interfaces.Target[] = getClassPropsAsTargets( metadataReader, baseConstructor, constructorName, @@ -214,13 +235,14 @@ function getBaseClassDependencyCount( metadataReader: interfaces.MetadataReader, func: NewableFunction, ): number { - const baseConstructor = Object.getPrototypeOf(func.prototype).constructor; - - if (baseConstructor !== Object) { + const baseConstructor: NewableFunction = + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + Object.getPrototypeOf(func.prototype).constructor as NewableFunction; + if (baseConstructor !== (Object as unknown as NewableFunction)) { // get targets for base class - const baseConstructorName = getFunctionName(baseConstructor); + const baseConstructorName: string = getFunctionName(baseConstructor); - const targets = getTargets( + const targets: interfaces.Target[] = getTargets( metadataReader, baseConstructorName, baseConstructor, @@ -228,14 +250,20 @@ function getBaseClassDependencyCount( ); // get unmanaged metadata - const metadata = targets.map((t) => - t.metadata.filter((m) => m.key === METADATA_KEY.UNMANAGED_TAG), + const metadata: interfaces.Metadata[][] = targets.map( + (t: interfaces.Target) => + t.metadata.filter( + (m: interfaces.Metadata) => m.key === METADATA_KEY.UNMANAGED_TAG, + ), ); // Compare the number of constructor arguments with the number of // unmanaged dependencies unmanaged dependencies are not required - const unmanagedCount = ([] as Metadata[]).concat.apply([], metadata).length; - const dependencyCount = targets.length - unmanagedCount; + const unmanagedCount: number = ([] as Metadata[]).concat.apply( + [], + metadata, + ).length; + const dependencyCount: number = targets.length - unmanagedCount; if (dependencyCount > 0) { return dependencyCount; diff --git a/src/planning/request.ts b/src/planning/request.ts index fb5ab7418..1b7fa6abf 100644 --- a/src/planning/request.ts +++ b/src/planning/request.ts @@ -11,10 +11,11 @@ class Request implements interfaces.Request { public target: interfaces.Target; public requestScope: interfaces.RequestScope | null; - public constructor( + constructor( serviceIdentifier: interfaces.ServiceIdentifier, parentContext: interfaces.Context, parentRequest: interfaces.Request | null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any bindings: interfaces.Binding | interfaces.Binding[], target: interfaces.Target, ) { @@ -35,7 +36,7 @@ class Request implements interfaces.Request { bindings: interfaces.Binding | interfaces.Binding[], target: interfaces.Target, ): interfaces.Request { - const child = new Request( + const child: Request = new Request( serviceIdentifier, this.parentContext, this, diff --git a/src/planning/target.ts b/src/planning/target.ts index 776a03d37..34a187e01 100644 --- a/src/planning/target.ts +++ b/src/planning/target.ts @@ -12,9 +12,9 @@ class Target implements interfaces.Target { public name: interfaces.QueryableString; public identifier: string | symbol; public key!: string | symbol; - public metadata!: Metadata[]; + public metadata: Metadata[]; - public constructor( + constructor( type: interfaces.TargetType, identifier: string | symbol, serviceIdentifier: interfaces.ServiceIdentifier, @@ -23,13 +23,13 @@ class Target implements interfaces.Target { this.id = id(); this.type = type; this.serviceIdentifier = serviceIdentifier; - const queryableName = + const queryableName: string = typeof identifier === 'symbol' ? getSymbolDescription(identifier) : identifier; this.name = new QueryableString(queryableName || ''); this.identifier = identifier; - this.metadata = new Array(); + this.metadata = new Array(); let metadataItem: interfaces.Metadata | null = null; @@ -69,8 +69,10 @@ class Target implements interfaces.Target { } public isTagged(): boolean { - return this.metadata.some((metadata) => - METADATA_KEY.NON_CUSTOM_TAG_KEYS.every((key) => metadata.key !== key), + return this.metadata.some((metadata: interfaces.Metadata) => + METADATA_KEY.NON_CUSTOM_TAG_KEYS.every( + (key: string) => metadata.key !== key, + ), ); } @@ -81,7 +83,7 @@ class Target implements interfaces.Target { public getNamedTag(): interfaces.Metadata | null { if (this.isNamed()) { return this.metadata.filter( - (m) => m.key === METADATA_KEY.NAMED_TAG, + (m: interfaces.Metadata) => m.key === METADATA_KEY.NAMED_TAG, )[0] as interfaces.Metadata; } return null; @@ -89,8 +91,10 @@ class Target implements interfaces.Target { public getCustomTags(): interfaces.Metadata[] | null { if (this.isTagged()) { - return this.metadata.filter((metadata) => - METADATA_KEY.NON_CUSTOM_TAG_KEYS.every((key) => metadata.key !== key), + return this.metadata.filter((metadata: interfaces.Metadata) => + METADATA_KEY.NON_CUSTOM_TAG_KEYS.every( + (key: string) => metadata.key !== key, + ), ); } else { return null; diff --git a/src/resolution/instantiation.ts b/src/resolution/instantiation.ts index 66f9a1d96..a435aa42f 100644 --- a/src/resolution/instantiation.ts +++ b/src/resolution/instantiation.ts @@ -29,9 +29,9 @@ function _resolveRequests( resolveRequest: interfaces.ResolveRequestHandler, ): ResolvedRequests { return childRequests.reduce( - (resolvedRequests, childRequest) => { - const injection = resolveRequest(childRequest); - const targetType = childRequest.target.type; + (resolvedRequests: ResolvedRequests, childRequest: interfaces.Request) => { + const injection: unknown = resolveRequest(childRequest); + const targetType: interfaces.TargetType = childRequest.target.type; if (targetType === TargetTypeEnum.ConstructorArgument) { resolvedRequests.constructorInjections.push(injection); } else { @@ -45,9 +45,9 @@ function _resolveRequests( }, { constructorInjections: [], + isAsync: false, propertyInjections: [], propertyRequests: [], - isAsync: false, }, ); } @@ -60,7 +60,10 @@ function _createInstance( let result: T | Promise; if (childRequests.length > 0) { - const resolved = _resolveRequests(childRequests, resolveRequest); + const resolved: ResolvedRequests = _resolveRequests( + childRequests, + resolveRequest, + ); const createInstanceWithInjectionsArg: CreateInstanceWithInjectionArg = { ...resolved, constr, @@ -82,10 +85,10 @@ function _createInstance( function createInstanceWithInjections( args: CreateInstanceWithInjectionArg, ): T { - const instance = new args.constr(...args.constructorInjections); + const instance: T = new args.constr(...args.constructorInjections); args.propertyRequests.forEach((r: interfaces.Request, index: number) => { - const property = r.target.identifier; - const injection = args.propertyInjections[index]; + const property: string | symbol = r.target.identifier; + const injection: unknown = args.propertyInjections[index]; if (!r.target.isOptional() || injection !== undefined) { (instance as Record)[property] = injection; } @@ -96,10 +99,10 @@ function createInstanceWithInjections( async function createInstanceWithInjectionsAsync( args: CreateInstanceWithInjectionArg, ): Promise { - const constructorInjections = await possiblyWaitInjections( + const constructorInjections: unknown[] = await possiblyWaitInjections( args.constructorInjections, ); - const propertyInjections = await possiblyWaitInjections( + const propertyInjections: unknown[] = await possiblyWaitInjections( args.propertyInjections, ); return createInstanceWithInjections({ @@ -125,7 +128,10 @@ function _getInstanceAfterPostConstruct( constr: interfaces.Newable, result: T, ): T | Promise { - const postConstructResult = _postConstruct(constr, result); + const postConstructResult: void | Promise = _postConstruct( + constr, + result, + ); if (isPromise(postConstructResult)) { return postConstructResult.then(() => result); @@ -142,7 +148,7 @@ function _postConstruct( const data: Metadata = Reflect.getMetadata( METADATA_KEY.POST_CONSTRUCT, constr, - ); + ) as Metadata; try { return (instance as interfaces.Instance)[data.value as string]?.(); } catch (e) { @@ -166,7 +172,7 @@ function _throwIfHandlingDeactivation( binding: interfaces.Binding, constr: interfaces.Newable, ): void { - const scopeErrorMessage = `Class cannot be instantiated in ${ + const scopeErrorMessage: string = `Class cannot be instantiated in ${ binding.scope === BindingScopeEnum.Request ? 'request' : 'transient' } scope.`; if (typeof binding.onDeactivation === 'function') { @@ -186,10 +192,14 @@ function resolveInstance( ): T | Promise { _validateInstanceResolution(binding, constr); - const result = _createInstance(constr, childRequests, resolveRequest); + const result: T | Promise = _createInstance( + constr, + childRequests, + resolveRequest, + ); if (isPromise(result)) { - return result.then((resolvedResult) => + return result.then((resolvedResult: T): T | Promise => _getInstanceAfterPostConstruct(constr, resolvedResult), ); } else { diff --git a/src/resolution/resolver.ts b/src/resolution/resolver.ts index d243c0ba5..5a324d664 100644 --- a/src/resolution/resolver.ts +++ b/src/resolution/resolver.ts @@ -4,25 +4,33 @@ import { interfaces } from '../interfaces/interfaces'; import { getBindingDictionary } from '../planning/planner'; import { saveToScope, tryGetFromScope } from '../scope/scope'; import { isPromise } from '../utils/async'; -import { getFactoryDetails, ensureFullyBound } from '../utils/binding_utils'; +import { ensureFullyBound, getFactoryDetails } from '../utils/binding_utils'; import { tryAndThrowErrorIfStackOverflow } from '../utils/exceptions'; import { resolveInstance } from './instantiation'; -const _resolveRequest = +// eslint-disable-next-line @typescript-eslint/naming-convention +const _resolveRequest: ( + requestScope: interfaces.RequestScope, +) => ( + request: interfaces.Request, +) => undefined | T | Promise | (T | Promise)[] = (requestScope: interfaces.RequestScope) => ( request: interfaces.Request, ): undefined | T | Promise | (T | Promise)[] => { request.parentContext.setCurrentRequest(request); - const bindings = request.bindings; - const childRequests = request.childRequests; + const bindings: interfaces.Binding[] = request.bindings; + const childRequests: interfaces.Request[] = request.childRequests; - const targetIsAnArray = request.target && request.target.isArray(); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions + const targetIsAnArray: boolean = request.target && request.target.isArray(); - const targetParentIsNotAnArray = + const targetParentIsNotAnArray: boolean = !request.parentRequest || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions !request.parentRequest.target || + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, @typescript-eslint/strict-boolean-expressions !request.target || !request.parentRequest.target.matchesArray( request.target.serviceIdentifier, @@ -30,32 +38,39 @@ const _resolveRequest = if (targetIsAnArray && targetParentIsNotAnArray) { // Create an array instead of creating an instance - return childRequests.map((childRequest: interfaces.Request) => { - const _f = _resolveRequest(requestScope); - return _f(childRequest) as T | Promise; - }); + return childRequests.map( + (childRequest: interfaces.Request): T | Promise => { + const resolveRequest: (request: interfaces.Request) => unknown = + _resolveRequest(requestScope); + return resolveRequest(childRequest) as T | Promise; + }, + ); } else { if (request.target.isOptional() && bindings.length === 0) { return undefined; } - const binding = bindings[0]; + const binding: interfaces.Binding | undefined = bindings[0]; return _resolveBinding( requestScope, request, - binding as unknown as interfaces.Binding, + binding as interfaces.Binding as interfaces.Binding, ); } }; -const _resolveFactoryFromBinding = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _resolveFactoryFromBinding: ( + binding: interfaces.Binding, + context: interfaces.Context, +) => T | Promise = ( binding: interfaces.Binding, context: interfaces.Context, ): T | Promise => { - const factoryDetails = getFactoryDetails(binding); + const factoryDetails: interfaces.FactoryDetails = getFactoryDetails(binding); return tryAndThrowErrorIfStackOverflow( - () => + (): T | Promise => (factoryDetails.factory as interfaces.FactoryTypeFunction).bind( binding, )(context), @@ -63,19 +78,25 @@ const _resolveFactoryFromBinding = ( new Error( ERROR_MSGS.CIRCULAR_DEPENDENCY_IN_FACTORY( factoryDetails.factoryType, + // eslint-disable-next-line @typescript-eslint/no-base-to-string context.currentRequest.serviceIdentifier.toString(), ), ), ); }; -const _getResolvedFromBinding = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _getResolvedFromBinding: ( + requestScope: interfaces.RequestScope, + request: interfaces.Request, + binding: interfaces.Binding, +) => T | Promise = ( requestScope: interfaces.RequestScope, request: interfaces.Request, binding: interfaces.Binding, ): T | Promise => { let result: T | Promise | undefined; - const childRequests = request.childRequests; + const childRequests: interfaces.Request[] = request.childRequests; ensureFullyBound(binding); @@ -99,15 +120,20 @@ const _getResolvedFromBinding = ( result = _resolveFactoryFromBinding(binding, request.parentContext); } - return result as T | Promise; + return result; }; -const _resolveInScope = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _resolveInScope: ( + requestScope: interfaces.RequestScope, + binding: interfaces.Binding, + resolveFromBinding: () => T | Promise, +) => T | Promise = ( requestScope: interfaces.RequestScope, binding: interfaces.Binding, resolveFromBinding: () => T | Promise, ): T | Promise => { - let result = tryGetFromScope(requestScope, binding); + let result: T | Promise | null = tryGetFromScope(requestScope, binding); if (result !== null) { return result; } @@ -116,15 +142,24 @@ const _resolveInScope = ( return result; }; -const _resolveBinding = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _resolveBinding: ( + requestScope: interfaces.RequestScope, + request: interfaces.Request, + binding: interfaces.Binding, +) => T | Promise = ( requestScope: interfaces.RequestScope, request: interfaces.Request, binding: interfaces.Binding, ): T | Promise => { - return _resolveInScope(requestScope, binding, () => { - let result = _getResolvedFromBinding(requestScope, request, binding); + return _resolveInScope(requestScope, binding, (): T | Promise => { + let result: T | Promise = _getResolvedFromBinding( + requestScope, + request, + binding, + ); if (isPromise(result)) { - result = result.then((resolved) => + result = result.then((resolved: T): T | Promise => _onActivation(request, binding, resolved), ); } else { @@ -139,23 +174,26 @@ function _onActivation( binding: interfaces.Binding, resolved: T, ): T | Promise { - let result = _bindingActivation(request.parentContext, binding, resolved); - - const containersIterator = _getContainersIterator( - request.parentContext.container, + let result: T | Promise = _bindingActivation( + request.parentContext, + binding, + resolved, ); + const containersIterator: Iterator = + _getContainersIterator(request.parentContext.container); + let container: interfaces.Container; - let containersIteratorResult = containersIterator.next(); + let containersIteratorResult: IteratorResult = + containersIterator.next(); do { - container = containersIteratorResult.value; - const context = request.parentContext; - const serviceIdentifier = request.serviceIdentifier; - const activationsIterator = _getContainerActivationsForService( - container, - serviceIdentifier, - ); + container = containersIteratorResult.value as interfaces.Container; + const context: interfaces.Context = request.parentContext; + const serviceIdentifier: interfaces.ServiceIdentifier = + request.serviceIdentifier; + const activationsIterator: ArrayIterator = + _getContainerActivationsForService(container, serviceIdentifier); if (isPromise(result)) { result = _activateContainerAsync( @@ -182,7 +220,12 @@ function _onActivation( return result; } -const _bindingActivation = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _bindingActivation: ( + context: interfaces.Context, + binding: interfaces.Binding, + previousResult: T, +) => T | Promise = ( context: interfaces.Context, binding: interfaces.Binding, previousResult: T, @@ -199,14 +242,20 @@ const _bindingActivation = ( return result; }; -const _activateContainer = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _activateContainer: ( + activationsIterator: Iterator>, + context: interfaces.Context, + result: T, +) => T | Promise = ( activationsIterator: Iterator>, context: interfaces.Context, result: T, ): T | Promise => { - let activation = activationsIterator.next(); + let activation: IteratorResult> = + activationsIterator.next(); - while (!activation.done) { + while (activation.done !== true) { result = activation.value(context, result) as T; if (isPromise(result)) { @@ -219,15 +268,21 @@ const _activateContainer = ( return result; }; -const _activateContainerAsync = async ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _activateContainerAsync: ( + activationsIterator: Iterator>, + context: interfaces.Context, + resultPromise: Promise, +) => Promise = async ( activationsIterator: Iterator>, context: interfaces.Context, resultPromise: Promise, ): Promise => { - let result = await resultPromise; - let activation = activationsIterator.next(); + let result: Awaited = await resultPromise; + let activation: IteratorResult> = + activationsIterator.next(); - while (!activation.done) { + while (activation.done !== true) { result = await activation.value(context, result); activation = activationsIterator.next(); @@ -236,12 +291,16 @@ const _activateContainerAsync = async ( return result; }; -const _getContainerActivationsForService = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _getContainerActivationsForService: ( + container: interfaces.Container, + serviceIdentifier: interfaces.ServiceIdentifier, +) => ArrayIterator> = ( container: interfaces.Container, serviceIdentifier: interfaces.ServiceIdentifier, ) => { // smell accessing _activations, but similar pattern is done in planner.getBindingDictionary() - const activations = ( + const activations: interfaces.Lookup = ( container as unknown as { _activations: interfaces.Lookup>; } @@ -252,12 +311,15 @@ const _getContainerActivationsForService = ( : [].values(); }; -const _getContainersIterator = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _getContainersIterator: ( + container: interfaces.Container, +) => Iterator = ( container: interfaces.Container, ): Iterator => { const containersStack: interfaces.Container[] = [container]; - let parent = container.parent; + let parent: interfaces.Container | null = container.parent; while (parent !== null) { containersStack.push(parent); @@ -266,7 +328,8 @@ const _getContainersIterator = ( } const getNextContainer: () => IteratorResult = () => { - const nextContainer = containersStack.pop(); + const nextContainer: interfaces.Container | undefined = + containersStack.pop(); if (nextContainer !== undefined) { return { done: false, value: nextContainer }; @@ -285,10 +348,16 @@ const _getContainersIterator = ( function resolve( context: interfaces.Context, ): T | Promise | (T | Promise)[] { - const _f = _resolveRequest( + const resolveRequestFunction: ( + request: interfaces.Request, + ) => T | Promise | (T | Promise)[] | undefined = _resolveRequest( context.plan.rootRequest.requestScope as interfaces.RequestScope, ); - return _f(context.plan.rootRequest) as T | Promise | (T | Promise)[]; + + return resolveRequestFunction(context.plan.rootRequest) as + | T + | Promise + | (T | Promise)[]; } export { resolve }; diff --git a/src/scope/scope.ts b/src/scope/scope.ts index 9ea0b97a7..16a0d478b 100644 --- a/src/scope/scope.ts +++ b/src/scope/scope.ts @@ -2,11 +2,15 @@ import { BindingScopeEnum } from '../constants/literal_types'; import type { interfaces } from '../interfaces/interfaces'; import { isPromise } from '../utils/async'; -export const tryGetFromScope = ( +export const tryGetFromScope: ( + requestScope: interfaces.RequestScope, + binding: interfaces.Binding, +) => T | Promise | null = ( requestScope: interfaces.RequestScope, binding: interfaces.Binding, ): T | Promise | null => { if (binding.scope === BindingScopeEnum.Singleton && binding.activated) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return binding.cache!; } @@ -19,7 +23,11 @@ export const tryGetFromScope = ( return null; }; -export const saveToScope = ( +export const saveToScope: ( + requestScope: interfaces.RequestScope, + binding: interfaces.Binding, + result: T | Promise, +) => void = ( requestScope: interfaces.RequestScope, binding: interfaces.Binding, result: T | Promise, @@ -33,7 +41,12 @@ export const saveToScope = ( } }; -const _saveToRequestScope = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _saveToRequestScope: ( + requestScope: interfaces.RequestScope, + binding: interfaces.Binding, + result: T | Promise, +) => void = ( requestScope: interfaces.RequestScope, binding: interfaces.Binding, result: T | Promise, @@ -43,7 +56,11 @@ const _saveToRequestScope = ( } }; -const _saveToSingletonScope = ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _saveToSingletonScope: ( + binding: interfaces.Binding, + result: T | Promise, +) => void = ( binding: interfaces.Binding, result: T | Promise, ): void => { @@ -56,12 +73,16 @@ const _saveToSingletonScope = ( } }; -const _saveAsyncResultToSingletonScope = async ( +// eslint-disable-next-line @typescript-eslint/naming-convention +const _saveAsyncResultToSingletonScope: ( + binding: interfaces.Binding, + asyncResult: Promise, +) => Promise = async ( binding: interfaces.Binding, asyncResult: Promise, ): Promise => { try { - const result = await asyncResult; + const result: Awaited = await asyncResult; binding.cache = result; } catch (ex: unknown) { diff --git a/src/syntax/binding_in_syntax.ts b/src/syntax/binding_in_syntax.ts index 0582c9b75..b4a54dbb0 100644 --- a/src/syntax/binding_in_syntax.ts +++ b/src/syntax/binding_in_syntax.ts @@ -3,9 +3,9 @@ import { interfaces } from '../interfaces/interfaces'; import { BindingWhenOnSyntax } from './binding_when_on_syntax'; class BindingInSyntax implements interfaces.BindingInSyntax { - private _binding: interfaces.Binding; + private readonly _binding: interfaces.Binding; - public constructor(binding: interfaces.Binding) { + constructor(binding: interfaces.Binding) { this._binding = binding; } diff --git a/src/syntax/binding_in_when_on_syntax.ts b/src/syntax/binding_in_when_on_syntax.ts index cff7e94b3..e66b8d684 100644 --- a/src/syntax/binding_in_when_on_syntax.ts +++ b/src/syntax/binding_in_when_on_syntax.ts @@ -9,12 +9,12 @@ class BindingInWhenOnSyntax interfaces.BindingWhenSyntax, interfaces.BindingOnSyntax { - private _bindingInSyntax: interfaces.BindingInSyntax; - private _bindingWhenSyntax: interfaces.BindingWhenSyntax; - private _bindingOnSyntax: interfaces.BindingOnSyntax; - private _binding: interfaces.Binding; + private readonly _bindingInSyntax: interfaces.BindingInSyntax; + private readonly _bindingWhenSyntax: interfaces.BindingWhenSyntax; + private readonly _bindingOnSyntax: interfaces.BindingOnSyntax; + private readonly _binding: interfaces.Binding; - public constructor(binding: interfaces.Binding) { + constructor(binding: interfaces.Binding) { this._binding = binding; this._bindingWhenSyntax = new BindingWhenSyntax(this._binding); this._bindingOnSyntax = new BindingOnSyntax(this._binding); diff --git a/src/syntax/binding_on_syntax.ts b/src/syntax/binding_on_syntax.ts index 6e8c480e2..813bcd92d 100644 --- a/src/syntax/binding_on_syntax.ts +++ b/src/syntax/binding_on_syntax.ts @@ -2,9 +2,9 @@ import { interfaces } from '../interfaces/interfaces'; import { BindingWhenSyntax } from './binding_when_syntax'; class BindingOnSyntax implements interfaces.BindingOnSyntax { - private _binding: interfaces.Binding; + private readonly _binding: interfaces.Binding; - public constructor(binding: interfaces.Binding) { + constructor(binding: interfaces.Binding) { this._binding = binding; } diff --git a/src/syntax/binding_to_syntax.ts b/src/syntax/binding_to_syntax.ts index 316e4b10b..88669f45a 100644 --- a/src/syntax/binding_to_syntax.ts +++ b/src/syntax/binding_to_syntax.ts @@ -8,9 +8,9 @@ class BindingToSyntax implements interfaces.BindingToSyntax { // TODO: Implement an internal type `_BindingToSyntax` wherein this member // can be public. Let `BindingToSyntax` be the presentational type that // depends on it, and does not expose this member as public. - private _binding: interfaces.Binding; + private readonly _binding: interfaces.Binding; - public constructor(binding: interfaces.Binding) { + constructor(binding: interfaces.Binding) { this._binding = binding; } @@ -24,9 +24,10 @@ class BindingToSyntax implements interfaces.BindingToSyntax { public toSelf(): interfaces.BindingInWhenOnSyntax { if (typeof this._binding.serviceIdentifier !== 'function') { - throw new Error(`${ERROR_MSGS.INVALID_TO_SELF_VALUE}`); + throw new Error(ERROR_MSGS.INVALID_TO_SELF_VALUE); } - const self = this._binding.serviceIdentifier; + + const self: interfaces.Newable = this._binding.serviceIdentifier; return this.to(self); } @@ -72,7 +73,8 @@ class BindingToSyntax implements interfaces.BindingToSyntax { if (typeof func !== 'function') { throw new Error(ERROR_MSGS.INVALID_FUNCTION_BINDING); } - const bindingWhenOnSyntax = this.toConstantValue(func); + const bindingWhenOnSyntax: interfaces.BindingWhenOnSyntax = + this.toConstantValue(func); this._binding.type = BindingTypeEnum.Function; this._binding.scope = BindingScopeEnum.Singleton; return bindingWhenOnSyntax; @@ -82,8 +84,9 @@ class BindingToSyntax implements interfaces.BindingToSyntax { serviceIdentifier: interfaces.ServiceIdentifier, ): interfaces.BindingWhenOnSyntax { this._binding.type = BindingTypeEnum.Factory; - this._binding.factory = (context) => { - const autofactory = () => context.container.get(serviceIdentifier); + this._binding.factory = (context: interfaces.Context) => { + const autofactory: () => T2 = () => + context.container.get(serviceIdentifier); return autofactory; }; this._binding.scope = BindingScopeEnum.Singleton; @@ -94,7 +97,7 @@ class BindingToSyntax implements interfaces.BindingToSyntax { serviceIdentifier: interfaces.ServiceIdentifier, ): BindingWhenOnSyntax { this._binding.type = BindingTypeEnum.Factory; - this._binding.factory = (context) => { + this._binding.factory = (context: interfaces.Context) => { return (named: unknown) => context.container.getNamed(serviceIdentifier, named as string); }; @@ -113,7 +116,9 @@ class BindingToSyntax implements interfaces.BindingToSyntax { public toService( service: string | symbol | interfaces.Newable | interfaces.Abstract, ): void { - this.toDynamicValue((context) => context.container.get(service)); + this.toDynamicValue((context: interfaces.Context) => + context.container.get(service), + ); } } diff --git a/src/syntax/binding_when_on_syntax.ts b/src/syntax/binding_when_on_syntax.ts index a8fa46d61..97a81cdcd 100644 --- a/src/syntax/binding_when_on_syntax.ts +++ b/src/syntax/binding_when_on_syntax.ts @@ -5,11 +5,11 @@ import { BindingWhenSyntax } from './binding_when_syntax'; class BindingWhenOnSyntax implements interfaces.BindingWhenSyntax, interfaces.BindingOnSyntax { - private _bindingWhenSyntax: interfaces.BindingWhenSyntax; - private _bindingOnSyntax: interfaces.BindingOnSyntax; - private _binding: interfaces.Binding; + private readonly _bindingWhenSyntax: interfaces.BindingWhenSyntax; + private readonly _bindingOnSyntax: interfaces.BindingOnSyntax; + private readonly _binding: interfaces.Binding; - public constructor(binding: interfaces.Binding) { + constructor(binding: interfaces.Binding) { this._binding = binding; this._bindingWhenSyntax = new BindingWhenSyntax(this._binding); this._bindingOnSyntax = new BindingOnSyntax(this._binding); diff --git a/src/syntax/binding_when_syntax.ts b/src/syntax/binding_when_syntax.ts index 049a5c0c2..ecbcea7f0 100644 --- a/src/syntax/binding_when_syntax.ts +++ b/src/syntax/binding_when_syntax.ts @@ -8,9 +8,9 @@ import { } from './constraint_helpers'; class BindingWhenSyntax implements interfaces.BindingWhenSyntax { - private _binding: interfaces.Binding; + private readonly _binding: interfaces.Binding; - public constructor(binding: interfaces.Binding) { + constructor(binding: interfaces.Binding) { this._binding = binding; } @@ -34,7 +34,8 @@ class BindingWhenSyntax implements interfaces.BindingWhenSyntax { return false; } - const targetIsDefault = + const targetIsDefault: boolean = + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition request.target !== null && !request.target.isNamed() && !request.target.isTagged(); diff --git a/src/syntax/constraint_helpers.ts b/src/syntax/constraint_helpers.ts index 4dd32a554..a485dfddb 100644 --- a/src/syntax/constraint_helpers.ts +++ b/src/syntax/constraint_helpers.ts @@ -2,11 +2,14 @@ import * as METADATA_KEY from '../constants/metadata_keys'; import { interfaces } from '../interfaces/interfaces'; import { Metadata } from '../planning/metadata'; -const traverseAncerstors = ( +const traverseAncerstors: ( + request: interfaces.Request, + constraint: interfaces.ConstraintFunction, +) => boolean = ( request: interfaces.Request, constraint: interfaces.ConstraintFunction, ): boolean => { - const parent = request.parentRequest; + const parent: interfaces.Request | null = request.parentRequest; if (parent !== null) { return constraint(parent) ? true : traverseAncerstors(parent, constraint); } else { @@ -16,12 +19,15 @@ const traverseAncerstors = ( // This helpers use currying to help you to generate constraints -const taggedConstraint = +const taggedConstraint: ( + key: string | number | symbol, +) => (value: unknown) => interfaces.ConstraintFunction = (key: string | number | symbol) => (value: unknown) => { const constraint: interfaces.ConstraintFunction = ( request: interfaces.Request | null, ) => request !== null && + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition request.target !== null && request.target.matchesTag(key)(value); @@ -30,9 +36,12 @@ const taggedConstraint = return constraint; }; -const namedConstraint = taggedConstraint(METADATA_KEY.NAMED_TAG); +const namedConstraint: (value: unknown) => interfaces.ConstraintFunction = + taggedConstraint(METADATA_KEY.NAMED_TAG); -const typeConstraint = +const typeConstraint: ( + type: NewableFunction | string, +) => (request: interfaces.Request | null) => boolean = (type: NewableFunction | string) => (request: interfaces.Request | null) => { // Using index 0 because constraints are applied // to one binding at a time (see Planner class) @@ -41,11 +50,11 @@ const typeConstraint = if (request !== null) { binding = request.bindings[0] as interfaces.Binding; if (typeof type === 'string') { - const serviceIdentifier = binding.serviceIdentifier; - return serviceIdentifier === type; + return binding.serviceIdentifier === type; } else { - const constructor = (request.bindings[0] as interfaces.Binding) - .implementationType; + const constructor: unknown = ( + request.bindings[0] as interfaces.Binding + ).implementationType; return type === constructor; } } diff --git a/src/utils/async.ts b/src/utils/async.ts index d313bf844..afd0122e9 100644 --- a/src/utils/async.ts +++ b/src/utils/async.ts @@ -1,5 +1,5 @@ function isPromise(object: unknown): object is Promise { - const isObjectOrFunction = + const isObjectOrFunction: boolean = (typeof object === 'object' && object !== null) || typeof object === 'function'; diff --git a/src/utils/binding_utils.ts b/src/utils/binding_utils.ts index 87bc3466c..abd4bbfbe 100644 --- a/src/utils/binding_utils.ts +++ b/src/utils/binding_utils.ts @@ -1,20 +1,28 @@ -import { getServiceIdentifierAsString } from '../utils/serialization'; -import type { interfaces } from '../interfaces/interfaces'; import * as ERROR_MSGS from '../constants/error_msgs'; import { BindingTypeEnum } from '../constants/literal_types'; +import type { interfaces } from '../interfaces/interfaces'; +import { getServiceIdentifierAsString } from '../utils/serialization'; import { FactoryType } from './factory_type'; -export const multiBindToService = +export const multiBindToService: ( + container: interfaces.Container, +) => ( + service: interfaces.ServiceIdentifier, +) => (...types: interfaces.ServiceIdentifier[]) => void = (container: interfaces.Container) => (service: interfaces.ServiceIdentifier) => - (...types: interfaces.ServiceIdentifier[]) => - types.forEach((t) => container.bind(t).toService(service)); + (...types: interfaces.ServiceIdentifier[]) => { + types.forEach((t: interfaces.ServiceIdentifier) => { + container.bind(t).toService(service); + }); + }; -export const ensureFullyBound = ( +export const ensureFullyBound: ( binding: interfaces.Binding, -): void => { +) => void = (binding: interfaces.Binding): void => { let boundValue: unknown = null; + // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check switch (binding.type) { case BindingTypeEnum.ConstantValue: case BindingTypeEnum.Function: @@ -37,7 +45,7 @@ export const ensureFullyBound = ( if (boundValue === null) { // The user probably created a binding but didn't finish it // e.g. container.bind('Something'); missing BindingToSyntax - const serviceIdentifierAsString = getServiceIdentifierAsString( + const serviceIdentifierAsString: string = getServiceIdentifierAsString( binding.serviceIdentifier, ); throw new Error( @@ -46,7 +54,9 @@ export const ensureFullyBound = ( } }; -export const getFactoryDetails = ( +export const getFactoryDetails: ( + binding: interfaces.Binding, +) => interfaces.FactoryDetails = ( binding: interfaces.Binding, ): interfaces.FactoryDetails => { switch (binding.type) { diff --git a/src/utils/exceptions.ts b/src/utils/exceptions.ts index 17e9ab319..a5c12df1b 100644 --- a/src/utils/exceptions.ts +++ b/src/utils/exceptions.ts @@ -7,16 +7,17 @@ export function isStackOverflowException(error: unknown): error is RangeError { ); } -export const tryAndThrowErrorIfStackOverflow = ( +export const tryAndThrowErrorIfStackOverflow: ( fn: () => T, errorCallback: () => Error, -) => { +) => T = (fn: () => T, errorCallback: () => Error) => { try { return fn(); - } catch (error) { + } catch (error: unknown) { if (isStackOverflowException(error)) { - error = errorCallback(); + throw errorCallback(); } + throw error; } }; diff --git a/src/utils/id.ts b/src/utils/id.ts index 5eb12e5dc..22af61163 100644 --- a/src/utils/id.ts +++ b/src/utils/id.ts @@ -1,4 +1,4 @@ -let idCounter = 0; +let idCounter: number = 0; function id(): number { return idCounter++; diff --git a/src/utils/js.ts b/src/utils/js.ts index 9cda777d9..a89f29061 100644 --- a/src/utils/js.ts +++ b/src/utils/js.ts @@ -1,5 +1,5 @@ export function getFirstArrayDuplicate(array: T[]): T | undefined { - const seenValues = new Set(); + const seenValues: Set = new Set(); for (const entry of array) { if (seenValues.has(entry)) { diff --git a/src/utils/serialization.ts b/src/utils/serialization.ts index fd6a2d7c9..448f5d0da 100644 --- a/src/utils/serialization.ts +++ b/src/utils/serialization.ts @@ -5,14 +5,11 @@ function getServiceIdentifierAsString( serviceIdentifier: interfaces.ServiceIdentifier, ): string { if (typeof serviceIdentifier === 'function') { - const _serviceIdentifier = serviceIdentifier; - return _serviceIdentifier.name; + return serviceIdentifier.name; } else if (typeof serviceIdentifier === 'symbol') { return serviceIdentifier.toString(); } else { - // string - const _serviceIdentifier = serviceIdentifier; - return _serviceIdentifier as string; + return serviceIdentifier as string; } } @@ -24,15 +21,18 @@ function listRegisteredBindingsForServiceIdentifier( serviceIdentifier: interfaces.ServiceIdentifier, ) => interfaces.Binding[], ): string { - let registeredBindingsList = ''; - const registeredBindings = getBindings(container, serviceIdentifier); + let registeredBindingsList: string = ''; + const registeredBindings: interfaces.Binding[] = getBindings( + container, + serviceIdentifier, + ); if (registeredBindings.length !== 0) { registeredBindingsList = '\nRegistered bindings:'; registeredBindings.forEach((binding: interfaces.Binding) => { // Use 'Object as name of constant value injections' - let name = 'Object'; + let name: string = 'Object'; // Use function name if available if (binding.implementationType !== null) { @@ -44,6 +44,7 @@ function listRegisteredBindingsForServiceIdentifier( registeredBindingsList = `${registeredBindingsList}\n ${name}`; if (binding.constraint.metaData) { + // eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions registeredBindingsList = `${registeredBindingsList} - ${binding.constraint.metaData}`; } }); @@ -70,7 +71,7 @@ function dependencyChainToString(request: interfaces.Request) { req: interfaces.Request, result: string[] = [], ): string[] { - const serviceIdentifier = getServiceIdentifierAsString( + const serviceIdentifier: string = getServiceIdentifierAsString( req.serviceIdentifier, ); result.push(serviceIdentifier); @@ -80,14 +81,14 @@ function dependencyChainToString(request: interfaces.Request) { return result; } - const stringArr = _createStringArr(request); + const stringArr: string[] = _createStringArr(request); return stringArr.reverse().join(' --> '); } function circularDependencyToException(request: interfaces.Request) { - request.childRequests.forEach((childRequest) => { + request.childRequests.forEach((childRequest: interfaces.Request) => { if (alreadyDependencyChain(childRequest, childRequest.serviceIdentifier)) { - const services = dependencyChainToString(childRequest); + const services: string = dependencyChainToString(childRequest); throw new Error(`${ERROR_MSGS.CIRCULAR_DEPENDENCY} ${services}`); } else { circularDependencyToException(childRequest); @@ -100,17 +101,20 @@ function listMetadataForTarget( target: interfaces.Target, ): string { if (target.isTagged() || target.isNamed()) { - let m = ''; + let m: string = ''; - const namedTag = target.getNamedTag(); - const otherTags = target.getCustomTags(); + const namedTag: interfaces.Metadata | null = target.getNamedTag(); + const otherTags: interfaces.Metadata[] | null = + target.getCustomTags(); if (namedTag !== null) { + // eslint-disable-next-line @typescript-eslint/no-base-to-string m += namedTag.toString() + '\n'; } if (otherTags !== null) { - otherTags.forEach((tag) => { + otherTags.forEach((tag: interfaces.Metadata) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string m += tag.toString() + '\n'; }); } @@ -121,17 +125,21 @@ function listMetadataForTarget( } } -function getFunctionName(func: { name: string | null }): string { - if (func.name) { +function getFunctionName(func: { name: string | null | undefined }): string { + if (func.name != null && func.name !== '') { return func.name; } else { - const name = func.toString(); - const match = name.match(/^function\s*([^\s(]+)/); - return match ? (match[1] as string) : `Anonymous function: ${name}`; + // eslint-disable-next-line @typescript-eslint/no-base-to-string + const name: string = func.toString(); + const match: RegExpMatchArray | null = name.match(/^function\s*([^\s(]+)/); + return match === null + ? `Anonymous function: ${name}` + : (match[1] as string); } } -function getSymbolDescription(symbol: Symbol) { +function getSymbolDescription(symbol: symbol) { + // eslint-disable-next-line @typescript-eslint/no-magic-numbers return symbol.toString().slice(7, -1); } diff --git a/test/annotation/decorator_utils.test.ts b/test/annotation/decorator_utils.test.ts index 0ecc237b2..a4dd23a00 100644 --- a/test/annotation/decorator_utils.test.ts +++ b/test/annotation/decorator_utils.test.ts @@ -1,12 +1,18 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; -import { createTaggedDecorator, tagParameter, tagProperty } from '../../src/annotation/decorator_utils' +import { + createTaggedDecorator, + DecoratorTarget, + tagParameter, + tagProperty, +} from '../../src/annotation/decorator_utils'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; -import { Container, inject, injectable } from '../../src/inversify'; +import { Container, inject, injectable, interfaces } from '../../src/inversify'; +import { Metadata } from '../../src/planning/metadata'; describe('createTaggedDecorator', () => { - let sandbox: sinon.SinonSandbox + let sandbox: sinon.SinonSandbox; beforeEach(function () { sandbox = sinon.createSandbox(); }); @@ -16,35 +22,54 @@ describe('createTaggedDecorator', () => { }); it('should pass to tagParameter for parameter decorators', () => { - class Target { } - const metadata = { key: '1', value: '2' }; - const decorator = createTaggedDecorator(metadata); - const spiedTagParameter = sandbox.spy(tagParameter); + class Target {} + const metadata: Metadata = { key: '1', value: '2' }; + const decorator: ( + target: DecoratorTarget, + targetKey?: string | symbol, + indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, + ) => void = createTaggedDecorator(metadata); + const spiedTagParameter: sinon.SinonSpy< + Parameters, + ReturnType + > = sandbox.spy(tagParameter); decorator(Target, undefined, 1); expect(spiedTagParameter.calledWithExactly(Target, undefined, 1, metadata)); }); it('should pass to tagProperty for property decorators', () => { - class Target { } - const metadata = { key: '2', value: '2' }; - const decorator = createTaggedDecorator(metadata); - const spiedTagProperty = sandbox.spy(tagProperty); + class Target {} + const metadata: Metadata = { key: '2', value: '2' }; + const decorator: ( + target: DecoratorTarget, + targetKey?: string | symbol, + indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, + ) => void = createTaggedDecorator(metadata); + const spiedTagProperty: sinon.SinonSpy< + Parameters, + ReturnType + > = sandbox.spy(tagProperty); decorator(Target.prototype, 'PropertyName'); - expect(spiedTagProperty.calledWithExactly(Target, 'PropertyName', metadata)); + expect( + spiedTagProperty.calledWithExactly(Target, 'PropertyName', metadata), + ); }); it('should enable constraining to multiple metadata with a single decorator', () => { function multipleMetadataDecorator(key1Value: string, key2Value: string) { - return createTaggedDecorator([{ key: 'key1', value: key1Value }, { key: 'key2', value: key2Value }]); + return createTaggedDecorator([ + { key: 'key1', value: key1Value }, + { key: 'key2', value: key2Value }, + ]); } interface Thing { - type: string + type: string; } @injectable() class Thing1 implements Thing { - type = 'Thing1' + public readonly type: string = 'Thing1'; } @injectable() @@ -52,50 +77,59 @@ describe('createTaggedDecorator', () => { public thingyType!: string; @multipleMetadataDecorator('Key1Value', 'Key2Value') @inject('Thing') - set thingy(thingy: Thing) { - this.thingyType = thingy.type + public set thingy(thingy: Thing) { + this.thingyType = thingy.type; } } - const container = new Container(); - container.bind('Thing').to(Thing1).when(request => { - const metadatas = request.target.metadata; - const key1Metadata = metadatas[1]; - const key2Metadata = metadatas[2]; - return key1Metadata?.value === 'Key1Value' && key2Metadata?.value === 'Key2Value'; - }); + const container: Container = new Container(); + container + .bind('Thing') + .to(Thing1) + .when((request: interfaces.Request) => { + const metadatas: interfaces.Metadata[] = request.target.metadata; + const key1Metadata: interfaces.Metadata | undefined = metadatas[1]; + const key2Metadata: interfaces.Metadata | undefined = metadatas[2]; + return ( + key1Metadata?.value === 'Key1Value' && + key2Metadata?.value === 'Key2Value' + ); + }); container.resolve(Root); }); - }); describe('tagParameter', () => { it('should throw if multiple metadata with same key', () => { - class Target { } - expect( - () => tagParameter(Target, undefined, 1, [{ key: 'Duplicate', value: '1' }, { key: 'Duplicate', value: '2' }]) - ).to.throw(`${ERROR_MSGS.DUPLICATED_METADATA} Duplicate`); + class Target {} + expect(() => { + tagParameter(Target, undefined, 1, [ + { key: 'Duplicate', value: '1' }, + { key: 'Duplicate', value: '2' }, + ]); + }).to.throw(`${ERROR_MSGS.DUPLICATED_METADATA} Duplicate`); }); }); describe('tagProperty', () => { it('should throw if multiple metadata with same key', () => { - class Target { } - expect( - () => tagProperty(Target.prototype, 'Property', [{ key: 'Duplicate', value: '1' }, { key: 'Duplicate', value: '2' }]) - ).to.throw(`${ERROR_MSGS.DUPLICATED_METADATA} Duplicate`); + class Target {} + expect(() => { + tagProperty(Target.prototype, 'Property', [ + { key: 'Duplicate', value: '1' }, + { key: 'Duplicate', value: '2' }, + ]); + }).to.throw(`${ERROR_MSGS.DUPLICATED_METADATA} Duplicate`); }); it('should throw for static properties', () => { - class Target { } + class Target {} // does not throw - tagProperty(Target.prototype, 'Property', { key: 'key', value: 'value' }) - - expect( - () => tagProperty(Target, 'StaticProperty', { key: 'key', value: 'value' }) - ).to.throw(ERROR_MSGS.INVALID_DECORATOR_OPERATION); + tagProperty(Target.prototype, 'Property', { key: 'key', value: 'value' }); + expect(() => { + tagProperty(Target, 'StaticProperty', { key: 'key', value: 'value' }); + }).to.throw(ERROR_MSGS.INVALID_DECORATOR_OPERATION); }); - -}); \ No newline at end of file +}); diff --git a/test/annotation/inject.test.ts b/test/annotation/inject.test.ts index 7627e68ce..b02ad3509 100644 --- a/test/annotation/inject.test.ts +++ b/test/annotation/inject.test.ts @@ -1,39 +1,46 @@ +// eslint-disable-next-line @typescript-eslint/naming-convention declare function __decorate( decorators: ClassDecorator[], target: NewableFunction, key?: string | symbol, - descriptor?: PropertyDescriptor | undefined + descriptor?: PropertyDescriptor | undefined, ): void; -declare function __param(paramIndex: number, decorator: ParameterDecorator): ClassDecorator; +// eslint-disable-next-line @typescript-eslint/naming-convention +declare function __param( + paramIndex: number, + decorator: ParameterDecorator, +): ClassDecorator; import { expect } from 'chai'; + import { decorate } from '../../src/annotation/decorator_utils'; import { inject } from '../../src/annotation/inject'; -import { LazyServiceIdentifier, ServiceIdentifierOrFunc } from '../../src/annotation/lazy_service_identifier'; +import { + LazyServiceIdentifier, + ServiceIdentifierOrFunc, +} from '../../src/annotation/lazy_service_identifier'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; import { interfaces } from '../../src/interfaces/interfaces'; import { multiInject } from '../../src/inversify'; -interface Katana { } -interface Shuriken { } -interface Sword { } -class Katana implements Katana { } -class Shuriken implements Shuriken { } -class Sword implements Sword { } +class Katana {} +class Shuriken {} +class Sword {} -const lazySwordId = new LazyServiceIdentifier(() => 'Sword'); +const lazySwordId: LazyServiceIdentifier = new LazyServiceIdentifier( + () => 'Sword', +); class DecoratedWarrior { + private readonly _primaryWeapon: Katana; + private readonly _secondaryWeapon: Shuriken; + private readonly _tertiaryWeapon: Sword; - private _primaryWeapon: Katana; - private _secondaryWeapon: Shuriken; - private _tertiaryWeapon: Sword; - - public constructor( + constructor( @inject('Katana') primary: Katana, @inject('Shuriken') secondary: Shuriken, - @inject(lazySwordId) tertiary: Shuriken + @inject(lazySwordId) tertiary: Shuriken, ) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; @@ -44,169 +51,215 @@ class DecoratedWarrior { return { primaryWeapon: this._primaryWeapon, secondaryWeapon: this._secondaryWeapon, - tertiaryWeapon: this._tertiaryWeapon + tertiaryWeapon: this._tertiaryWeapon, }; } - } class InvalidDecoratorUsageWarrior { + private readonly _primaryWeapon: Katana; + private readonly _secondaryWeapon: Shuriken; - private _primaryWeapon: Katana; - private _secondaryWeapon: Shuriken; - - public constructor( - primary: Katana, - secondary: Shuriken - ) { - + constructor(primary: Katana, secondary: Shuriken) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; } - public test(a: string) { /*...*/ } + public test(_a: string) {} public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } - } describe('@inject', () => { - it('Should generate metadata for named parameters', () => { - - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, DecoratedWarrior); + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + DecoratedWarrior, + ) as interfaces.MetadataMap; expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql(METADATA_KEY.INJECT_TAG); - expect(m1.value).to.be.eql('Katana'); - expect(paramsMetadata['0'][1]).to.eq(undefined); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + expect(zeroIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.INJECT_TAG); + expect(zeroIndexedFirstMetadata.value).to.be.eql('Katana'); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // assert metadata for second argument expect(paramsMetadata['1']).to.be.instanceof(Array); - const m2: interfaces.Metadata = paramsMetadata['1'][0]; - expect(m2.key).to.be.eql(METADATA_KEY.INJECT_TAG); - expect(m2.value).to.be.eql('Shuriken'); - expect(paramsMetadata['1'][1]).to.eq(undefined); + + const oneIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '1' + ] as interfaces.Metadata[]; + + const oneIndexedFirstMetadata: interfaces.Metadata = + oneIndexedMetadata[0] as interfaces.Metadata; + + expect(oneIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.INJECT_TAG); + expect(oneIndexedFirstMetadata.value).to.be.eql('Shuriken'); + expect(oneIndexedMetadata[1]).to.eq(undefined); // assert metadata for second argument expect(paramsMetadata['2']).to.be.instanceof(Array); - const m3: interfaces.Metadata = paramsMetadata['2'][0]; - expect(m3.key).to.be.eql(METADATA_KEY.INJECT_TAG); - expect(m3.value).to.be.eql(lazySwordId); - expect(paramsMetadata['2'][1]).to.eq(undefined); + + const twoIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '2' + ] as interfaces.Metadata[]; + + const twoIndexedFirstMetadata: interfaces.Metadata = + twoIndexedMetadata[0] as interfaces.Metadata; + expect(twoIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.INJECT_TAG); + expect(twoIndexedFirstMetadata.value).to.be.eql(lazySwordId); + expect(twoIndexedMetadata[1]).to.eq(undefined); // no more metadata should be available expect(paramsMetadata['3']).to.eq(undefined); - }); it('Should throw when applied multiple times', () => { - - const useDecoratorMoreThanOnce = function () { - __decorate([__param(0, inject('Katana')), __param(0, inject('Shurien'))], InvalidDecoratorUsageWarrior); + const useDecoratorMoreThanOnce: () => void = function () { + __decorate( + [__param(0, inject('Katana')), __param(0, inject('Shurien'))], + InvalidDecoratorUsageWarrior, + ); }; - const msg = `${ERROR_MSGS.DUPLICATED_METADATA} ${METADATA_KEY.INJECT_TAG}`; + const msg: string = `${ERROR_MSGS.DUPLICATED_METADATA} ${METADATA_KEY.INJECT_TAG}`; expect(useDecoratorMoreThanOnce).to.throw(msg); - }); it('Should throw when not applied to a constructor', () => { - - const useDecoratorOnMethodThatIsNotAConstructor = function () { - __decorate([__param(0, inject('Katana'))], + const useDecoratorOnMethodThatIsNotConstructor: () => void = function () { + __decorate( + [__param(0, inject('Katana'))], InvalidDecoratorUsageWarrior.prototype as unknown as NewableFunction, - 'test', Object.getOwnPropertyDescriptor(InvalidDecoratorUsageWarrior.prototype, 'test')); + 'test', + Object.getOwnPropertyDescriptor( + InvalidDecoratorUsageWarrior.prototype, + 'test', + ), + ); }; - const msg = `${ERROR_MSGS.INVALID_DECORATOR_OPERATION}`; - expect(useDecoratorOnMethodThatIsNotAConstructor).to.throw(msg); - + const msg: string = ERROR_MSGS.INVALID_DECORATOR_OPERATION; + expect(useDecoratorOnMethodThatIsNotConstructor).to.throw(msg); }); it('Should throw when applied with undefined', () => { - // this can happen when there is circular dependency between symbols - const useDecoratorWithUndefined = function () { - __decorate([__param(0, inject(undefined as unknown as ServiceIdentifierOrFunc))], InvalidDecoratorUsageWarrior); + const useDecoratorWithUndefined: () => void = function () { + __decorate( + [ + __param( + 0, + inject(undefined as unknown as ServiceIdentifierOrFunc), + ), + ], + InvalidDecoratorUsageWarrior, + ); }; - const msg = `${ERROR_MSGS.UNDEFINED_INJECT_ANNOTATION('InvalidDecoratorUsageWarrior')}`; + const msg: string = ERROR_MSGS.UNDEFINED_INJECT_ANNOTATION( + 'InvalidDecoratorUsageWarrior', + ); expect(useDecoratorWithUndefined).to.throw(msg); - }); it('Should be usable in VanillaJS applications', () => { + type Shuriken = unknown; - interface Shurien { } + const vanillaJsWarrior: (primary: Katana, secondary: unknown) => void = + (function () { + function warrior(_primary: Katana, _secondary: Shuriken) {} + return warrior; + })(); - const VanillaJSWarrior = (function () { - function Warrior(primary: Katana, secondary: Shurien) { - // ... - } - return Warrior; - })(); + decorate(inject('Katana'), vanillaJsWarrior, 0); + decorate(inject('Shurien'), vanillaJsWarrior, 1); - decorate(inject('Katana'), VanillaJSWarrior, 0); - decorate(inject('Shurien'), VanillaJSWarrior, 1); + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + vanillaJsWarrior, + ) as interfaces.MetadataMap; - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, VanillaJSWarrior); expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql(METADATA_KEY.INJECT_TAG); - expect(m1.value).to.be.eql('Katana'); - expect(paramsMetadata['0'][1]).to.eq(undefined); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + + expect(zeroIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.INJECT_TAG); + expect(zeroIndexedFirstMetadata.value).to.be.eql('Katana'); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // assert metadata for second argument expect(paramsMetadata['1']).to.be.instanceof(Array); - const m2: interfaces.Metadata = paramsMetadata['1'][0]; - expect(m2.key).to.be.eql(METADATA_KEY.INJECT_TAG); - expect(m2.value).to.be.eql('Shurien'); - expect(paramsMetadata['1'][1]).to.eq(undefined); + + const oneIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '1' + ] as interfaces.Metadata[]; + + const oneIndexedFirstMetadata: interfaces.Metadata = + oneIndexedMetadata[0] as interfaces.Metadata; + + expect(oneIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.INJECT_TAG); + expect(oneIndexedFirstMetadata.value).to.be.eql('Shurien'); + expect(oneIndexedMetadata[1]).to.eq(undefined); // no more metadata should be available expect(paramsMetadata['2']).to.eq(undefined); - }); it('should throw when applied inject decorator with undefined service identifier to a property', () => { expect(() => { - //@ts-ignore + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars class WithUndefinedInject { @inject(undefined as unknown as ServiceIdentifierOrFunc) - property!: string + public property!: string; } - }).to.throw(`${ERROR_MSGS.UNDEFINED_INJECT_ANNOTATION('WithUndefinedInject')}`) + }).to.throw(ERROR_MSGS.UNDEFINED_INJECT_ANNOTATION('WithUndefinedInject')); }); it('should throw when applied multiInject decorator with undefined service identifier to a constructor parameter', () => { expect(() => { - //@ts-ignore + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-unused-vars class WithUndefinedInject { constructor( - @multiInject(undefined as unknown as ServiceIdentifierOrFunc) - readonly dependency: string[] - ) { } + @multiInject( + undefined as unknown as ServiceIdentifierOrFunc, + ) + public readonly dependency: string[], + ) {} } - }).to.throw(`${ERROR_MSGS.UNDEFINED_INJECT_ANNOTATION('WithUndefinedInject')}`) + }).to.throw(ERROR_MSGS.UNDEFINED_INJECT_ANNOTATION('WithUndefinedInject')); }); it('Should unwrap LazyServiceIdentifier', () => { - const unwrapped = lazySwordId.unwrap(); + const unwrapped: interfaces.ServiceIdentifier = lazySwordId.unwrap(); + expect(unwrapped).to.be.equal('Sword'); }); - }); diff --git a/test/annotation/injectable.test.ts b/test/annotation/injectable.test.ts index 3b11809ce..8f14d9954 100644 --- a/test/annotation/injectable.test.ts +++ b/test/annotation/injectable.test.ts @@ -1,23 +1,21 @@ import { expect } from 'chai'; + import * as ERRORS_MSGS from '../../src/constants/error_msgs'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; import { decorate, injectable } from '../../src/inversify'; describe('@injectable', () => { - it('Should generate metadata if declared injections', () => { + class Katana {} - class Katana { } - - interface Weapon { } + type Weapon = unknown; @injectable() class Warrior { + private readonly _primaryWeapon: Katana; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Katana; - private _secondaryWeapon: Weapon; - - public constructor(primaryWeapon: Katana, secondaryWeapon: Weapon) { + constructor(primaryWeapon: Katana, secondaryWeapon: Weapon) { this._primaryWeapon = primaryWeapon; this._secondaryWeapon = secondaryWeapon; } @@ -25,13 +23,15 @@ describe('@injectable', () => { public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } - } - const metadata = Reflect.getMetadata(METADATA_KEY.PARAM_TYPES, Warrior); + const metadata: NewableFunction[] = Reflect.getMetadata( + METADATA_KEY.PARAM_TYPES, + Warrior, + ) as NewableFunction[]; expect(metadata).to.be.instanceof(Array); expect(metadata[0]).to.be.eql(Katana); @@ -40,33 +40,34 @@ describe('@injectable', () => { }); it('Should throw when applied multiple times', () => { - @injectable() - class Test { } + class Test {} - const useDecoratorMoreThanOnce = function () { + const useDecoratorMoreThanOnce: () => void = function () { decorate(injectable(), Test); decorate(injectable(), Test); }; - expect(useDecoratorMoreThanOnce).to.throw(ERRORS_MSGS.DUPLICATED_INJECTABLE_DECORATOR); + expect(useDecoratorMoreThanOnce).to.throw( + ERRORS_MSGS.DUPLICATED_INJECTABLE_DECORATOR, + ); }); it('Should be usable in VanillaJS applications', () => { + type Katana = unknown; + type Shuriken = unknown; - interface Katana { } - interface Shuriken { } + const vanillaJsWarrior: (primary: unknown, secondary: unknown) => void = + function (_primary: Katana, _secondary: Shuriken) {}; - const VanillaJSWarrior = function (primary: Katana, secondary: Shuriken) { - // ... - }; + decorate(injectable(), vanillaJsWarrior); - decorate(injectable(), VanillaJSWarrior); + const metadata: NewableFunction[] = Reflect.getMetadata( + METADATA_KEY.PARAM_TYPES, + vanillaJsWarrior, + ) as NewableFunction[]; - const metadata = Reflect.getMetadata(METADATA_KEY.PARAM_TYPES, VanillaJSWarrior); expect(metadata).to.be.instanceof(Array); expect(metadata.length).to.eql(0); - }); - -}); \ No newline at end of file +}); diff --git a/test/annotation/multi_inject.test.ts b/test/annotation/multi_inject.test.ts index c6277759d..2fc15389b 100644 --- a/test/annotation/multi_inject.test.ts +++ b/test/annotation/multi_inject.test.ts @@ -1,133 +1,155 @@ +// eslint-disable-next-line @typescript-eslint/naming-convention declare function __decorate( decorators: ClassDecorator[], target: NewableFunction, key?: string | symbol, - descriptor?: PropertyDescriptor | undefined + descriptor?: PropertyDescriptor | undefined, ): void; -declare function __param(paramIndex: number, decorator: ParameterDecorator): ClassDecorator; +// eslint-disable-next-line @typescript-eslint/naming-convention +declare function __param( + paramIndex: number, + decorator: ParameterDecorator, +): ClassDecorator; import { expect } from 'chai'; + import { decorate } from '../../src/annotation/decorator_utils'; import { multiInject } from '../../src/annotation/multi_inject'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; import { interfaces } from '../../src/interfaces/interfaces'; -interface Weapon { } +type Weapon = object; class DecoratedWarrior { + private readonly _primaryWeapon: Weapon; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Weapon; - private _secondaryWeapon: Weapon; - - public constructor( - @multiInject('Weapon') weapons: Weapon[] - ) { - - this._primaryWeapon = weapons[0] as Weapon; - this._secondaryWeapon = weapons[1] as Weapon; + constructor(@multiInject('Weapon') weapons: [Weapon, Weapon]) { + this._primaryWeapon = weapons[0]; + this._secondaryWeapon = weapons[1]; } public mock() { return `${JSON.stringify(this._primaryWeapon)} ${JSON.stringify(this._secondaryWeapon)}`; } - } class InvalidDecoratorUsageWarrior { + private readonly _primaryWeapon: Weapon; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Weapon; - private _secondaryWeapon: Weapon; - - public constructor( - weapons: Weapon[] - ) { - this._primaryWeapon = weapons[0] as Weapon; - this._secondaryWeapon = weapons[1] as Weapon; + constructor(weapons: [Weapon, Weapon]) { + this._primaryWeapon = weapons[0]; + this._secondaryWeapon = weapons[1]; } - public test(a: string) { /*...*/ } + public test(_a: string) {} public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } - } describe('@multiInject', () => { - it('Should generate metadata for named parameters', () => { - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, DecoratedWarrior); + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + DecoratedWarrior, + ) as interfaces.MetadataMap; + expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql(METADATA_KEY.MULTI_INJECT_TAG); - expect(m1.value).to.be.eql('Weapon'); - expect(paramsMetadata['0'][1]).to.eq(undefined); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + + expect(zeroIndexedFirstMetadata.key).to.be.eql( + METADATA_KEY.MULTI_INJECT_TAG, + ); + expect(zeroIndexedFirstMetadata.value).to.be.eql('Weapon'); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // no more metadata should be available expect(paramsMetadata['1']).to.eq(undefined); - }); it('Should throw when applied multiple times', () => { - - const useDecoratorMoreThanOnce = function () { - __decorate([__param(0, multiInject('Weapon')), __param(0, multiInject('Weapon'))], InvalidDecoratorUsageWarrior); + const useDecoratorMoreThanOnce: () => void = function () { + __decorate( + [__param(0, multiInject('Weapon')), __param(0, multiInject('Weapon'))], + InvalidDecoratorUsageWarrior, + ); }; - const msg = `${ERROR_MSGS.DUPLICATED_METADATA} ${METADATA_KEY.MULTI_INJECT_TAG}`; + const msg: string = `${ERROR_MSGS.DUPLICATED_METADATA} ${METADATA_KEY.MULTI_INJECT_TAG}`; expect(useDecoratorMoreThanOnce).to.throw(msg); - }); it('Should throw when not applied to a constructor', () => { - - const useDecoratorOnMethodThatIsNotAConstructor = function () { - __decorate([__param(0, multiInject('Weapon'))], + const useDecoratorOnMethodThatIsNotConstructor: () => void = function () { + __decorate( + [__param(0, multiInject('Weapon'))], InvalidDecoratorUsageWarrior.prototype as unknown as NewableFunction, - 'test', Object.getOwnPropertyDescriptor(InvalidDecoratorUsageWarrior.prototype, 'test')); + 'test', + Object.getOwnPropertyDescriptor( + InvalidDecoratorUsageWarrior.prototype, + 'test', + ), + ); }; - const msg = `${ERROR_MSGS.INVALID_DECORATOR_OPERATION}`; - expect(useDecoratorOnMethodThatIsNotAConstructor).to.throw(msg); - + const msg: string = ERROR_MSGS.INVALID_DECORATOR_OPERATION; + expect(useDecoratorOnMethodThatIsNotConstructor).to.throw(msg); }); it('Should be usable in VanillaJS applications', () => { + type Katana = unknown; + type Shurien = unknown; - interface Katana { } - interface Shurien { } + const vanillaJsWarrior: (primary: unknown, secondary: unknown) => void = + (function () { + function warrior(_primary: Katana, _secondary: Shurien) {} + return warrior; + })(); - const VanillaJSWarrior = (function () { - function Warrior(primary: Katana, secondary: Shurien) { - // ... - } - return Warrior; - })(); + decorate(multiInject('Weapon'), vanillaJsWarrior, 0); - decorate(multiInject('Weapon'), VanillaJSWarrior, 0); + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + vanillaJsWarrior, + ) as interfaces.MetadataMap; - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, VanillaJSWarrior); expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql(METADATA_KEY.MULTI_INJECT_TAG); - expect(m1.value).to.be.eql('Weapon'); - expect(paramsMetadata['0'][1]).to.eq(undefined); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + + expect(zeroIndexedFirstMetadata.key).to.be.eql( + METADATA_KEY.MULTI_INJECT_TAG, + ); + expect(zeroIndexedFirstMetadata.value).to.be.eql('Weapon'); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // no more metadata should be available expect(paramsMetadata['1']).to.eq(undefined); - }); - -}); \ No newline at end of file +}); diff --git a/test/annotation/named.test.ts b/test/annotation/named.test.ts index ec05239e4..ada9eb781 100644 --- a/test/annotation/named.test.ts +++ b/test/annotation/named.test.ts @@ -1,29 +1,34 @@ +// eslint-disable-next-line @typescript-eslint/naming-convention declare function __decorate( decorators: ClassDecorator[], target: NewableFunction, key?: string | symbol, - descriptor?: PropertyDescriptor | undefined + descriptor?: PropertyDescriptor | undefined, ): void; -declare function __param(paramIndex: number, decorator: ParameterDecorator): ClassDecorator; +// eslint-disable-next-line @typescript-eslint/naming-convention +declare function __param( + paramIndex: number, + decorator: ParameterDecorator, +): ClassDecorator; import { expect } from 'chai'; + import { decorate } from '../../src/annotation/decorator_utils'; import { named } from '../../src/annotation/named'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; import { interfaces } from '../../src/interfaces/interfaces'; -interface Weapon { } +type Weapon = unknown; class NamedWarrior { + private readonly _primaryWeapon: Weapon; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Weapon; - private _secondaryWeapon: Weapon; - - public constructor( + constructor( @named('more_powerful') primary: Weapon, - @named('less_powerful') secondary: Weapon) { - + @named('less_powerful') secondary: Weapon, + ) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; } @@ -31,139 +36,176 @@ class NamedWarrior { public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } } class InvalidDecoratorUsageWarrior { + private readonly _primaryWeapon: Weapon; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Weapon; - private _secondaryWeapon: Weapon; - - public constructor( - primary: Weapon, - secondary: Weapon - ) { - + constructor(primary: Weapon, secondary: Weapon) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; } - public test(a: string) { /*...*/ } + public test(_a: string) {} public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } } describe('@named', () => { - it('Should generate metadata for named parameters', () => { + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + NamedWarrior, + ) as interfaces.MetadataMap; - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, NamedWarrior); expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql(METADATA_KEY.NAMED_TAG); - expect(m1.value).to.be.eql('more_powerful'); - expect(paramsMetadata['0'][1]).to.eq(undefined); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + + expect(zeroIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.NAMED_TAG); + expect(zeroIndexedFirstMetadata.value).to.be.eql('more_powerful'); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // assert metadata for second argument expect(paramsMetadata['1']).to.be.instanceof(Array); - const m2: interfaces.Metadata = paramsMetadata['1'][0]; - expect(m2.key).to.be.eql(METADATA_KEY.NAMED_TAG); - expect(m2.value).to.be.eql('less_powerful'); - expect(paramsMetadata['1'][1]).to.eq(undefined); + + const oneIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '1' + ] as interfaces.Metadata[]; + + const oneIndexedFirstMetadata: interfaces.Metadata = + oneIndexedMetadata[0] as interfaces.Metadata; + + expect(oneIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.NAMED_TAG); + expect(oneIndexedFirstMetadata.value).to.be.eql('less_powerful'); + expect(oneIndexedMetadata[1]).to.eq(undefined); // no more metadata should be available expect(paramsMetadata['2']).to.eq(undefined); - }); it('Should generate metadata for named properties', () => { - class Warrior { @named('throwable') public weapon!: Weapon; } - const metadataKey = METADATA_KEY.TAGGED_PROP; - const metadata = Reflect.getMetadata(metadataKey, Warrior); - - const m1 = metadata.weapon[0]; - expect(m1.key).to.be.eql(METADATA_KEY.NAMED_TAG); - expect(m1.value).to.be.eql('throwable'); - expect(metadata.weapon[1]).to.eq(undefined); - + const metadataKey: string = METADATA_KEY.TAGGED_PROP; + const metadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + Warrior, + ) as interfaces.MetadataMap; + + const weaponMetadata: interfaces.Metadata[] = metadata[ + 'weapon' + ] as interfaces.Metadata[]; + + const weaponFirstMetadata: interfaces.Metadata = + weaponMetadata[0] as interfaces.Metadata; + expect(weaponFirstMetadata.key).to.be.eql(METADATA_KEY.NAMED_TAG); + expect(weaponFirstMetadata.value).to.be.eql('throwable'); + expect(weaponMetadata[1]).to.eq(undefined); }); it('Should throw when applied multiple times', () => { - - const useDecoratorMoreThanOnce = function () { - __decorate([__param(0, named('a')), __param(0, named('b'))], InvalidDecoratorUsageWarrior); + const useDecoratorMoreThanOnce: () => void = function () { + __decorate( + [__param(0, named('a')), __param(0, named('b'))], + InvalidDecoratorUsageWarrior, + ); }; - const msg = `${ERROR_MSGS.DUPLICATED_METADATA} ${METADATA_KEY.NAMED_TAG}`; + const msg: string = `${ERROR_MSGS.DUPLICATED_METADATA} ${METADATA_KEY.NAMED_TAG}`; expect(useDecoratorMoreThanOnce).to.throw(msg); - }); it('Should throw when not applied to a constructor', () => { - - const useDecoratorOnMethodThatIsNotAConstructor = function () { - __decorate([__param(0, named('a'))], + const useDecoratorOnMethodThatIsNotConstructor: () => void = function () { + __decorate( + [__param(0, named('a'))], InvalidDecoratorUsageWarrior.prototype as unknown as NewableFunction, - 'test', Object.getOwnPropertyDescriptor(InvalidDecoratorUsageWarrior.prototype, 'test')); + 'test', + Object.getOwnPropertyDescriptor( + InvalidDecoratorUsageWarrior.prototype, + 'test', + ), + ); }; - const msg = `${ERROR_MSGS.INVALID_DECORATOR_OPERATION}`; - expect(useDecoratorOnMethodThatIsNotAConstructor).to.throw(msg); - + const msg: string = ERROR_MSGS.INVALID_DECORATOR_OPERATION; + expect(useDecoratorOnMethodThatIsNotConstructor).to.throw(msg); }); it('Should be usable in VanillaJS applications', () => { + type Katana = unknown; + type Shuriken = unknown; + + const vanillaJsWarrior: (primary: unknown, secondary: unknown) => void = + (function () { + function namedVanillaJsWarrior(_primary: Katana, _secondary: Shuriken) { + // ... + } + return namedVanillaJsWarrior; + })(); + + decorate(named('more_powerful'), vanillaJsWarrior, 0); + decorate(named('less_powerful'), vanillaJsWarrior, 1); + + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + vanillaJsWarrior, + ) as interfaces.MetadataMap; - interface Katana { } - interface Shurien { } - - const VanillaJSWarrior = (function () { - function NamedVanillaJSWarrior(primary: Katana, secondary: Shurien) { - // ... - } - return NamedVanillaJSWarrior; - })(); - - decorate(named('more_powerful'), VanillaJSWarrior, 0); - decorate(named('less_powerful'), VanillaJSWarrior, 1); - - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, VanillaJSWarrior); expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql(METADATA_KEY.NAMED_TAG); - expect(m1.value).to.be.eql('more_powerful'); - expect(paramsMetadata['0'][1]).to.eq(undefined); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + + expect(zeroIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.NAMED_TAG); + expect(zeroIndexedFirstMetadata.value).to.be.eql('more_powerful'); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // assert metadata for second argument expect(paramsMetadata['1']).to.be.instanceof(Array); - const m2: interfaces.Metadata = paramsMetadata['1'][0]; - expect(m2.key).to.be.eql(METADATA_KEY.NAMED_TAG); - expect(m2.value).to.be.eql('less_powerful'); - expect(paramsMetadata['1'][1]).eq(undefined); + + const oneIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '1' + ] as interfaces.Metadata[]; + + const oneIndexedFirstMetadata: interfaces.Metadata = + oneIndexedMetadata[0] as interfaces.Metadata; + + expect(oneIndexedFirstMetadata.key).to.be.eql(METADATA_KEY.NAMED_TAG); + expect(oneIndexedFirstMetadata.value).to.be.eql('less_powerful'); + expect(oneIndexedMetadata[1]).eq(undefined); // no more metadata should be available expect(paramsMetadata['2']).to.eq(undefined); - }); - -}); \ No newline at end of file +}); diff --git a/test/annotation/optional.test.ts b/test/annotation/optional.test.ts index d2e610b65..2510adcd2 100644 --- a/test/annotation/optional.test.ts +++ b/test/annotation/optional.test.ts @@ -1,14 +1,13 @@ import { expect } from 'chai'; + import { Container, inject, injectable, optional } from '../../src/inversify'; describe('@optional', () => { - it('Should allow to flag dependencies as optional', () => { - @injectable() class Katana { public name: string; - public constructor() { + constructor() { this.name = 'Katana'; } } @@ -16,7 +15,7 @@ describe('@optional', () => { @injectable() class Shuriken { public name: string; - public constructor() { + constructor() { this.name = 'Shuriken'; } } @@ -26,9 +25,9 @@ describe('@optional', () => { public name: string; public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') katana: Katana, - @inject('Shuriken') @optional() shuriken: Shuriken + @inject('Shuriken') @optional() shuriken: Shuriken, ) { this.name = 'Ninja'; this.katana = katana; @@ -36,31 +35,29 @@ describe('@optional', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('Katana').to(Katana); container.bind('Ninja').to(Ninja); - let ninja = container.get('Ninja'); + let ninja: Ninja = container.get('Ninja'); expect(ninja.name).to.eql('Ninja'); expect(ninja.katana.name).to.eql('Katana'); expect(ninja.shuriken).to.eql(undefined); container.bind('Shuriken').to(Shuriken); - ninja = container.get('Ninja'); + ninja = container.get('Ninja'); expect(ninja.name).to.eql('Ninja'); expect(ninja.katana.name).to.eql('Katana'); expect(ninja.shuriken.name).to.eql('Shuriken'); - }); it('Should allow to set a default value for dependencies flagged as optional', () => { - @injectable() class Katana { public name: string; - public constructor() { + constructor() { this.name = 'Katana'; } } @@ -68,7 +65,7 @@ describe('@optional', () => { @injectable() class Shuriken { public name: string; - public constructor() { + constructor() { this.name = 'Shuriken'; } } @@ -78,9 +75,11 @@ describe('@optional', () => { public name: string; public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') katana: Katana, - @inject('Shuriken') @optional() shuriken: Shuriken = { name: 'DefaultShuriken' } + @inject('Shuriken') + @optional() + shuriken: Shuriken = { name: 'DefaultShuriken' }, ) { this.name = 'Ninja'; this.katana = katana; @@ -88,12 +87,12 @@ describe('@optional', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('Katana').to(Katana); container.bind('Ninja').to(Ninja); - let ninja = container.get('Ninja'); + let ninja: Ninja = container.get('Ninja'); expect(ninja.name).to.eql('Ninja'); expect(ninja.katana.name).to.eql('Katana'); expect(ninja.shuriken.name).to.eql('DefaultShuriken'); @@ -104,14 +103,13 @@ describe('@optional', () => { expect(ninja.name).to.eql('Ninja'); expect(ninja.katana.name).to.eql('Katana'); expect(ninja.shuriken.name).to.eql('Shuriken'); - }); - it("Should allow to set a default value for class property dependencies flagged as optional", () => { + it('Should allow to set a default value for class property dependencies flagged as optional', () => { @injectable() class Katana { public name: string; - public constructor() { + constructor() { this.name = 'Katana'; } } @@ -119,37 +117,36 @@ describe('@optional', () => { @injectable() class Shuriken { public name: string; - public constructor() { + constructor() { this.name = 'Shuriken'; } } @injectable() class Ninja { - public name: string = "Ninja"; - @inject("Katana") public katana?: Katana; - @inject("Shuriken") @optional() public shuriken: Shuriken = { - name: "DefaultShuriken", + @inject('Katana') public katana?: Katana; + @inject('Shuriken') @optional() public shuriken: Shuriken = { + name: 'DefaultShuriken', }; + public name: string = 'Ninja'; } - const container = new Container(); + const container: Container = new Container(); container.bind('Katana').to(Katana); container.bind('Ninja').to(Ninja); - let ninja = container.get('Ninja'); + let ninja: Ninja = container.get('Ninja'); + expect(ninja.name).to.eql('Ninja'); expect(ninja.katana?.name).to.eql('Katana'); expect(ninja.shuriken.name).to.eql('DefaultShuriken'); container.bind('Shuriken').to(Shuriken); - ninja = container.get('Ninja'); + ninja = container.get('Ninja'); expect(ninja.name).to.eql('Ninja'); expect(ninja.katana?.name).to.eql('Katana'); expect(ninja.shuriken.name).to.eql('Shuriken'); - } - ); - -}); \ No newline at end of file + }); +}); diff --git a/test/annotation/post_construct.test.ts b/test/annotation/post_construct.test.ts index 169b5c561..c03aa2b61 100644 --- a/test/annotation/post_construct.test.ts +++ b/test/annotation/post_construct.test.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; + import { postConstruct } from '../../src/annotation/post_construct'; import * as ERRORS_MSGS from '../../src/constants/error_msgs'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; @@ -6,24 +7,29 @@ import { decorate } from '../../src/inversify'; import { Metadata } from '../../src/planning/metadata'; describe('@postConstruct', () => { - it('Should generate metadata for the decorated method', () => { class Katana { private useMessage!: string; - public use() { - return 'Used Katana!'; - } - @postConstruct() public testMethod() { this.useMessage = 'Used Katana!'; } + + public use() { + return 'Used Katana!'; + } + public debug() { return this.useMessage; } } - const metadata: Metadata = Reflect.getMetadata(METADATA_KEY.POST_CONSTRUCT, Katana); + + const metadata: Metadata = Reflect.getMetadata( + METADATA_KEY.POST_CONSTRUCT, + Katana, + ) as Metadata; + expect(metadata.value).to.be.equal('testMethod'); }); @@ -31,10 +37,14 @@ describe('@postConstruct', () => { function setup() { class Katana { @postConstruct() - public testMethod1() {/* ... */ } + public testMethod1() { + /* ... */ + } @postConstruct() - public testMethod2() {/* ... */ } + public testMethod2() { + /* ... */ + } } Katana.toString(); } @@ -42,18 +52,18 @@ describe('@postConstruct', () => { }); it('Should be usable in VanillaJS applications', () => { + const vanillaJsWarrior: () => void = function () {}; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + vanillaJsWarrior.prototype.testMethod = function () {}; - const VanillaJSWarrior = function () { - // ... - }; - VanillaJSWarrior.prototype.testMethod = function () { - // ... - }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + decorate(postConstruct(), vanillaJsWarrior.prototype, 'testMethod'); - decorate(postConstruct(), VanillaJSWarrior.prototype, 'testMethod'); + const metadata: Metadata = Reflect.getMetadata( + METADATA_KEY.POST_CONSTRUCT, + vanillaJsWarrior, + ) as Metadata; - const metadata: Metadata = Reflect.getMetadata(METADATA_KEY.POST_CONSTRUCT, VanillaJSWarrior); expect(metadata.value).to.be.equal('testMethod'); }); - -}); \ No newline at end of file +}); diff --git a/test/annotation/tagged.test.ts b/test/annotation/tagged.test.ts index bbf43a725..782ef472d 100644 --- a/test/annotation/tagged.test.ts +++ b/test/annotation/tagged.test.ts @@ -1,231 +1,307 @@ +// eslint-disable-next-line @typescript-eslint/naming-convention declare function __decorate( decorators: ClassDecorator[], target: NewableFunction, key?: string | symbol, - descriptor?: PropertyDescriptor | undefined + descriptor?: PropertyDescriptor | undefined, ): void; -declare function __param(paramIndex: number, decorator: ParameterDecorator): ClassDecorator; +// eslint-disable-next-line @typescript-eslint/naming-convention +declare function __param( + paramIndex: number, + decorator: ParameterDecorator, +): ClassDecorator; import { expect } from 'chai'; + import { decorate } from '../../src/annotation/decorator_utils'; import { tagged } from '../../src/annotation/tagged'; import * as ERRORS_MSGS from '../../src/constants/error_msgs'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; import { interfaces } from '../../src/interfaces/interfaces'; -interface Weapon { } +type Weapon = unknown; class TaggedWarrior { + private readonly _primaryWeapon: Weapon; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Weapon; - private _secondaryWeapon: Weapon; - - public constructor( + constructor( @tagged('power', 1) primary: Weapon, - @tagged('power', 2) secondary: Weapon) { + @tagged('power', 2) secondary: Weapon, + ) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; } public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } } class DoubleTaggedWarrior { + private readonly _primaryWeapon: Weapon; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Weapon; - private _secondaryWeapon: Weapon; - - public constructor( + constructor( @tagged('power', 1) @tagged('distance', 1) primary: Weapon, - @tagged('power', 2) @tagged('distance', 5) secondary: Weapon) { + @tagged('power', 2) @tagged('distance', 5) secondary: Weapon, + ) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; } public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } } class InvalidDecoratorUsageWarrior { + private readonly _primaryWeapon: Weapon; + private readonly _secondaryWeapon: Weapon; - private _primaryWeapon: Weapon; - private _secondaryWeapon: Weapon; - - public constructor( - primary: Weapon, - secondary: Weapon) { - + constructor(primary: Weapon, secondary: Weapon) { this._primaryWeapon = primary; this._secondaryWeapon = secondary; } - public test(a: string) { /*...*/ } + public test(_a: string) {} public debug() { return { primaryWeapon: this._primaryWeapon, - secondaryWeapon: this._secondaryWeapon + secondaryWeapon: this._secondaryWeapon, }; } } describe('@Tagged', () => { - it('Should generate metadata for tagged parameters', () => { + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + TaggedWarrior, + ) as interfaces.MetadataMap; - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, TaggedWarrior); expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql('power'); - expect(m1.value).to.be.eql(1); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + + expect(zeroIndexedFirstMetadata.key).to.be.eql('power'); + expect(zeroIndexedFirstMetadata.value).to.be.eql(1); // argument at index 0 should only have one tag - expect(paramsMetadata['0'][1]).to.eq(undefined); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // assert metadata for second argument expect(paramsMetadata['1']).to.be.instanceof(Array); - const m2: interfaces.Metadata = paramsMetadata['1'][0]; - expect(m2.key).to.be.eql('power'); - expect(m2.value).to.be.eql(2); + + const oneIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '1' + ] as interfaces.Metadata[]; + + const oneIndexedFirstMetadata: interfaces.Metadata = + oneIndexedMetadata[0] as interfaces.Metadata; + + expect(oneIndexedFirstMetadata.key).to.be.eql('power'); + + expect(oneIndexedFirstMetadata.value).to.be.eql(2); // argument at index 1 should only have one tag - expect(paramsMetadata['1'][1]).to.eq(undefined); + expect(oneIndexedMetadata[1]).to.eq(undefined); // no more metadata should be available expect(paramsMetadata['2']).to.eq(undefined); }); it('Should generate metadata for tagged properties', () => { - class Warrior { @tagged('throwable', false) public weapon!: Weapon; } - const metadataKey = METADATA_KEY.TAGGED_PROP; - const metadata = Reflect.getMetadata(metadataKey, Warrior); - const m1 = metadata.weapon[0]; - expect(m1.key).to.be.eql('throwable'); - expect(m1.value).to.be.eql(false); - expect(metadata.weapon[1]).to.eq(undefined); + const metadataKey: string = METADATA_KEY.TAGGED_PROP; + const metadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + Warrior, + ) as interfaces.MetadataMap; + const weaponMetadata: interfaces.Metadata[] = metadata[ + 'weapon' + ] as interfaces.Metadata[]; + + const weaponFirstMetadata: interfaces.Metadata = + weaponMetadata[0] as interfaces.Metadata; + + expect(weaponFirstMetadata.key).to.be.eql('throwable'); + expect(weaponFirstMetadata.value).to.be.eql(false); + expect(weaponMetadata[1]).to.eq(undefined); }); it('Should generate metadata for parameters tagged multiple times', () => { - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, DoubleTaggedWarrior); + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + DoubleTaggedWarrior, + ) as interfaces.MetadataMap; + expect(paramsMetadata).to.be.an('object'); // assert metadata for argument at index 0 expect(paramsMetadata['0']).to.be.instanceof(Array); + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + // assert argument at index 0 first tag - const m11: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m11.key).to.be.eql('distance'); - expect(m11.value).to.be.eql(1); + expect(zeroIndexedFirstMetadata.key).to.be.eql('distance'); + expect(zeroIndexedFirstMetadata.value).to.be.eql(1); // assert argument at index 0 second tag - const m12: interfaces.Metadata = paramsMetadata['0'][1]; - expect(m12.key).to.be.eql('power'); - expect(m12.value).to.be.eql(1); + + const zeroIndexedSecondMetadata: interfaces.Metadata = + zeroIndexedMetadata[1] as interfaces.Metadata; + + expect(zeroIndexedSecondMetadata.key).to.be.eql('power'); + expect(zeroIndexedSecondMetadata.value).to.be.eql(1); // assert metadata for argument at index 1 expect(paramsMetadata['1']).to.be.instanceof(Array); + const oneIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '1' + ] as interfaces.Metadata[]; + + const oneIndexedFirstMetadata: interfaces.Metadata = + oneIndexedMetadata[0] as interfaces.Metadata; + // assert argument at index 1 first tag - const m21: interfaces.Metadata = paramsMetadata['1'][0]; - expect(m21.key).to.be.eql('distance'); - expect(m21.value).to.be.eql(5); + expect(oneIndexedFirstMetadata.key).to.be.eql('distance'); + + expect(oneIndexedFirstMetadata.value).to.be.eql(5); // assert argument at index 1 second tag - const m22: interfaces.Metadata = paramsMetadata['1'][1]; - expect(m22.key).to.be.eql('power'); - expect(m22.value).to.be.eql(2); + + const oneIndexedSecondMetadata: interfaces.Metadata = + oneIndexedMetadata[1] as interfaces.Metadata; + + expect(oneIndexedSecondMetadata.key).to.be.eql('power'); + + expect(oneIndexedSecondMetadata.value).to.be.eql(2); // no more metadata (argument at index > 1) expect(paramsMetadata['2']).to.eq(undefined); - }); it('Should throw when applied multiple times', () => { + const metadataKey: string = 'a'; - const metadataKey = 'a'; + const useDecoratorMoreThanOnce: () => void = function () { + __decorate( + [ + __param(0, tagged(metadataKey, 1)), - const useDecoratorMoreThanOnce = function () { - __decorate([__param(0, tagged(metadataKey, 1)), __param(0, tagged(metadataKey, 2))], InvalidDecoratorUsageWarrior); + __param(0, tagged(metadataKey, 2)), + ], + InvalidDecoratorUsageWarrior, + ); }; - const msg = `${ERRORS_MSGS.DUPLICATED_METADATA} ${metadataKey}`; + const msg: string = `${ERRORS_MSGS.DUPLICATED_METADATA} ${metadataKey}`; expect(useDecoratorMoreThanOnce).to.throw(msg); - }); it('Should throw when not applied to a constructor', () => { - - const useDecoratorOnMethodThatIsNotAConstructor = function () { - __decorate([__param(0, tagged('a', 1))], + const useDecoratorOnMethodThatIsNotConstructor: () => void = function () { + __decorate( + [__param(0, tagged('a', 1))], InvalidDecoratorUsageWarrior.prototype as unknown as NewableFunction, - 'test', Object.getOwnPropertyDescriptor(InvalidDecoratorUsageWarrior.prototype, 'test')); + 'test', + Object.getOwnPropertyDescriptor( + InvalidDecoratorUsageWarrior.prototype, + 'test', + ), + ); }; - const msg = ERRORS_MSGS.INVALID_DECORATOR_OPERATION; - expect(useDecoratorOnMethodThatIsNotAConstructor).to.throw(msg); - + const msg: string = ERRORS_MSGS.INVALID_DECORATOR_OPERATION; + expect(useDecoratorOnMethodThatIsNotConstructor).to.throw(msg); }); it('Should be usable in VanillaJS applications', () => { - - interface Katana { } - interface Shuriken { } - - const VanillaJSWarrior = (function () { - function TaggedVanillaJSWarrior(primary: Katana, secondary: Shuriken) { - // ... - } - return TaggedVanillaJSWarrior; - })(); - - decorate(tagged('power', 1), VanillaJSWarrior, 0); - decorate(tagged('power', 2), VanillaJSWarrior, 1); - - const metadataKey = METADATA_KEY.TAGGED; - const paramsMetadata = Reflect.getMetadata(metadataKey, VanillaJSWarrior); + type Katana = unknown; + type Shuriken = unknown; + + const vanillaJsWarrior: (primary: unknown, secondary: unknown) => void = + (function () { + return function taggedVanillaJsWarrior( + _primary: Katana, + _secondary: Shuriken, + ) {}; + })(); + + decorate(tagged('power', 1), vanillaJsWarrior, 0); + + decorate(tagged('power', 2), vanillaJsWarrior, 1); + + const metadataKey: string = METADATA_KEY.TAGGED; + const paramsMetadata: interfaces.MetadataMap = Reflect.getMetadata( + metadataKey, + vanillaJsWarrior, + ) as interfaces.MetadataMap; expect(paramsMetadata).to.be.an('object'); // assert metadata for first argument expect(paramsMetadata['0']).to.be.instanceof(Array); - const m1: interfaces.Metadata = paramsMetadata['0'][0]; - expect(m1.key).to.be.eql('power'); - expect(m1.value).to.be.eql(1); + + const zeroIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '0' + ] as interfaces.Metadata[]; + + const zeroIndexedFirstMetadata: interfaces.Metadata = + zeroIndexedMetadata[0] as interfaces.Metadata; + + expect(zeroIndexedFirstMetadata.key).to.be.eql('power'); + expect(zeroIndexedFirstMetadata.value).to.be.eql(1); // argument at index 0 should only have one tag - expect(paramsMetadata['0'][1]).to.eq(undefined); + expect(zeroIndexedMetadata[1]).to.eq(undefined); // assert metadata for second argument expect(paramsMetadata['1']).to.be.instanceof(Array); - const m2: interfaces.Metadata = paramsMetadata['1'][0]; - expect(m2.key).to.be.eql('power'); - expect(m2.value).to.be.eql(2); + + const oneIndexedMetadata: interfaces.Metadata[] = paramsMetadata[ + '1' + ] as interfaces.Metadata[]; + + const oneIndexedFirstMetadata: interfaces.Metadata = + oneIndexedMetadata[0] as interfaces.Metadata; + + expect(oneIndexedFirstMetadata.key).to.be.eql('power'); + + expect(oneIndexedFirstMetadata.value).to.be.eql(2); // argument at index 1 should only have one tag - expect(paramsMetadata['1'][1]).to.eq(undefined); + expect(oneIndexedMetadata[1]).to.eq(undefined); // no more metadata should be available expect(paramsMetadata['2']).to.eq(undefined); - }); - -}); \ No newline at end of file +}); diff --git a/test/annotation/target_name.test.ts b/test/annotation/target_name.test.ts index f199c47fb..70da3d8ad 100644 --- a/test/annotation/target_name.test.ts +++ b/test/annotation/target_name.test.ts @@ -1,64 +1,92 @@ import { expect } from 'chai'; + import { decorate } from '../../src/annotation/decorator_utils'; import { injectable } from '../../src/annotation/injectable'; import { targetName } from '../../src/annotation/target_name'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; +import { interfaces } from '../../src/inversify'; +import { Metadata } from '../../src/planning/metadata'; import * as Stubs from '../utils/stubs'; describe('@targetName', () => { - it('Should generate metadata if declared parameter names', () => { - @injectable() class Warrior { - public katana: Stubs.Katana; public shuriken: Stubs.Shuriken; - public constructor( + constructor( @targetName('katana') katana: Stubs.Katana, - @targetName('shuriken') shuriken: Stubs.Shuriken + @targetName('shuriken') shuriken: Stubs.Shuriken, ) { - this.katana = katana; this.shuriken = shuriken; } } - const metadata = Reflect.getMetadata(METADATA_KEY.TAGGED, Warrior); + const metadata: interfaces.MetadataMap = Reflect.getMetadata( + METADATA_KEY.TAGGED, + Warrior, + ) as interfaces.MetadataMap; + expect(metadata['0']).to.be.instanceof(Array); expect(metadata['1']).to.be.instanceof(Array); expect(metadata['2']).to.eql(undefined); - expect(metadata['0'][0].key).to.be.eql(METADATA_KEY.NAME_TAG); - expect(metadata['0'][0].value).to.be.eql('katana'); - expect(metadata['1'][0].key).to.be.eql(METADATA_KEY.NAME_TAG); - expect(metadata['1'][0].value).to.be.eql('shuriken'); + const zeroIndexedMetadata: interfaces.Metadata[] = metadata[ + '0' + ] as interfaces.Metadata[]; + const oneIndexedMetadata: interfaces.Metadata[] = metadata[ + '1' + ] as interfaces.Metadata[]; + const expectedFirstMetadata: Metadata[] = [ + new Metadata(METADATA_KEY.NAME_TAG, 'katana'), + ]; + + const expectedSecondMetadata: Metadata[] = [ + new Metadata(METADATA_KEY.NAME_TAG, 'shuriken'), + ]; + + expect(zeroIndexedMetadata).to.deep.equal(expectedFirstMetadata); + expect(oneIndexedMetadata).to.deep.equal(expectedSecondMetadata); }); it('Should be usable in VanillaJS applications', () => { + type Katana = unknown; + type Shuriken = unknown; - interface Katana { } - interface Shuriken { } + const vanillaJsWarrior: (primary: unknown, secondary: unknown) => void = + function (_primary: Katana, _secondary: Shuriken) {}; - const VanillaJSWarrior = function (primary: Katana, secondary: Shuriken) { - // ... - }; + decorate(targetName('primary'), vanillaJsWarrior, 0); + decorate(targetName('secondary'), vanillaJsWarrior, 1); - decorate(targetName('primary'), VanillaJSWarrior, 0); - decorate(targetName('secondary'), VanillaJSWarrior, 1); + const metadata: interfaces.MetadataMap = Reflect.getMetadata( + METADATA_KEY.TAGGED, + vanillaJsWarrior, + ) as interfaces.MetadataMap; - const metadata = Reflect.getMetadata(METADATA_KEY.TAGGED, VanillaJSWarrior); expect(metadata['0']).to.be.instanceof(Array); expect(metadata['1']).to.be.instanceof(Array); expect(metadata['2']).to.eql(undefined); - expect(metadata['0'][0].key).to.be.eql(METADATA_KEY.NAME_TAG); - expect(metadata['0'][0].value).to.be.eql('primary'); - expect(metadata['1'][0].key).to.be.eql(METADATA_KEY.NAME_TAG); - expect(metadata['1'][0].value).to.be.eql('secondary'); + const zeroIndexedMetadata: interfaces.Metadata[] = metadata[ + '0' + ] as interfaces.Metadata[]; + const oneIndexedMetadata: interfaces.Metadata[] = metadata[ + '1' + ] as interfaces.Metadata[]; - }); + const expectedFirstMetadata: Metadata[] = [ + new Metadata(METADATA_KEY.NAME_TAG, 'primary'), + ]; -}); \ No newline at end of file + const expectedSecondMetadata: Metadata[] = [ + new Metadata(METADATA_KEY.NAME_TAG, 'secondary'), + ]; + + expect(zeroIndexedMetadata).to.deep.equal(expectedFirstMetadata); + expect(oneIndexedMetadata).to.deep.equal(expectedSecondMetadata); + }); +}); diff --git a/test/bindings/binding.test.ts b/test/bindings/binding.test.ts index 3fc83e425..a27733499 100644 --- a/test/bindings/binding.test.ts +++ b/test/bindings/binding.test.ts @@ -1,19 +1,22 @@ import { expect } from 'chai'; + import { Binding } from '../../src/bindings/binding'; import { BindingScopeEnum } from '../../src/constants/literal_types'; import * as Stubs from '../utils/stubs'; describe('Binding', () => { - it('Should set its own properties correctly', () => { + const fooIdentifier: string = 'FooInterface'; + const fooBinding: Binding = + new Binding( + fooIdentifier, + BindingScopeEnum.Transient, + ); - const fooIdentifier = 'FooInterface'; - const fooBinding = new Binding(fooIdentifier, BindingScopeEnum.Transient); expect(fooBinding.serviceIdentifier).eql(fooIdentifier); expect(fooBinding.implementationType).eql(null); expect(fooBinding.cache).eql(null); expect(fooBinding.scope).eql(BindingScopeEnum.Transient); expect(fooBinding.id).to.be.a('number'); }); - -}); \ No newline at end of file +}); diff --git a/test/bugs/bugs.test.ts b/test/bugs/bugs.test.ts index d291bbda6..2d6b6e628 100644 --- a/test/bugs/bugs.test.ts +++ b/test/bugs/bugs.test.ts @@ -1,857 +1,922 @@ -import { expect } from 'chai'; -import * as ERROR_MSGS from '../../src/constants/error_msgs'; -import * as METADATA_KEY from '../../src/constants/metadata_keys'; -import { - Container, - decorate, - inject, - injectable, - interfaces, - multiInject, - named, - tagged, - targetName, - unmanaged -} from '../../src/inversify'; -import { MetadataReader } from '../../src/planning/metadata_reader'; -import { getDependencies } from '../../src/planning/reflection_utils'; -import { getFunctionName, getServiceIdentifierAsString } from '../../src/utils/serialization'; - -describe('Bugs', () => { - - it('Should throw when args length of base and derived class not match', () => { - - @injectable() - class Warrior { - public rank: string; - public constructor(rank: string) { // length = 1 - this.rank = rank; - } - } - - @injectable() - class SamuraiMaster extends Warrior { - public constructor() { // length = 0 - super('master'); - } - } - - const container = new Container(); - container.bind(SamuraiMaster).to(SamuraiMaster); - - const shouldThrow = function () { - container.get(SamuraiMaster); - }; - - const error = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH('SamuraiMaster'); - expect(shouldThrow).to.throw(error); - - }); - - it('Should not throw when args length of base and derived class match (property setter)', () => { - - @injectable() - class Warrior { - public rank: string | null; - public constructor() { // length = 0 - this.rank = null; - } - } - - @injectable() - class SamuraiMaster extends Warrior { - public constructor() { // length = 0 - super(); - this.rank = 'master'; - } - } - - const container = new Container(); - container.bind(SamuraiMaster).to(SamuraiMaster); - const master = container.get(SamuraiMaster); - expect(master.rank).eql('master'); - - }); - - it('Should not throw when args length of base and derived class match', () => { - - // Injecting into the derived class - - @injectable() - class Warrior { - protected rank: string; - public constructor(rank: string) { // length = 1 - this.rank = rank; - } - } - - const TYPES = { Rank: 'Rank' }; - - @injectable() - class SamuraiMaster extends Warrior { - public constructor( - @inject(TYPES.Rank) @named('master') public override rank: string // length = 1 - ) { - super(rank); - } - } - - const container = new Container(); - container.bind(SamuraiMaster).to(SamuraiMaster); - container.bind(TYPES.Rank) - .toConstantValue('master') - .whenTargetNamed('master'); - - const master = container.get(SamuraiMaster); - expect(master.rank).eql('master'); - - }); - - it('Should not throw when args length of base and derived class match', () => { - - // Injecting into the derived class with multiple args - - @injectable() - class Warrior { - protected rank: string; - public constructor(rank: string) { // length = 1 - this.rank = rank; - } - } - - interface Weapon { - name: string; - } - - @injectable() - class Katana implements Weapon { - public name: string; - public constructor() { - this.name = 'Katana'; - } - } - - const TYPES = { - Rank: 'Rank', - Weapon: 'Weapon' - }; - - @injectable() - class SamuraiMaster extends Warrior { - public weapon: Weapon; - public constructor( - @inject(TYPES.Rank) @named('master') public override rank: string, - @inject(TYPES.Weapon) weapon: Weapon - ) { // length = 2 - super(rank); - this.weapon = weapon; - } - } - - const container = new Container(); - container.bind(TYPES.Weapon).to(Katana); - container.bind(SamuraiMaster).to(SamuraiMaster); - container.bind(TYPES.Rank) - .toConstantValue('master') - .whenTargetNamed('master'); - - const master = container.get(SamuraiMaster); - expect(master.rank).eql('master'); - expect(master.weapon.name).eql('Katana'); - - }); - - it('Should be able to convert a Symbol value to a string', () => { - - interface Weapon { } - - const TYPES = { - Weapon: Symbol.for('Weapon') - }; - - const container = new Container(); - const throwF = () => { container.get(TYPES.Weapon); }; - expect(throwF).to.throw(`${ERROR_MSGS.NOT_REGISTERED} ${getServiceIdentifierAsString(TYPES.Weapon)}`); - - }); - - it('Should be not require @inject annotation in toConstructor bindings', () => { - - interface ICategorySortingFn { } - interface IContentSortingFn { } - interface Collection { } - - @injectable() - class Category { - public constructor( - public id: string, - public title: string, - public categoryFirstPermalink: string, - public categoryPermalink: string, - public pagination: number, - public categorySortingFn: ICategorySortingFn, - public contentSortingFn: IContentSortingFn, - public belongsToCollection: Collection - ) { - // do nothing - } - } - - const container = new Container(); - container.bind>('Newable').toConstructor(Category); - const expected = container.get>('Newable'); - expect(expected).eql(Category); - - }); - - it('Should be able to combine tagged injection and constant value bindings', () => { - - const container = new Container(); - - interface Intl { } - - container.bind('Intl').toConstantValue({ hello: 'bonjour' }).whenTargetTagged('lang', 'fr'); - container.bind('Intl').toConstantValue({ goodbye: 'au revoir' }).whenTargetTagged('lang', 'fr'); - - const f = function () { container.getTagged('Intl', 'lang', 'fr'); }; - expect(f).to.throw(); - - }); - - it('Should be able to combine dynamic value with singleton scope', () => { - - const container = new Container(); - - container.bind('transient_random').toDynamicValue((context: interfaces.Context) => - Math.random()).inTransientScope(); - - container.bind('singleton_random').toDynamicValue((context: interfaces.Context) => - Math.random()).inSingletonScope(); - - const a = container.get('transient_random'); - const b = container.get('transient_random'); - - expect(a).not.to.eql(b); - - const c = container.get('singleton_random'); - const d = container.get('singleton_random'); - - expect(c).to.eql(d); - - }); - - it('Should be able to use an abstract class as the serviceIdentifier', () => { - - @injectable() - abstract class Animal { - protected name: string; - public constructor(@unmanaged() name: string) { - this.name = name; - } - public abstract makeSound(input: string): string; - public move(meters: number) { - return `${this.name} moved ${meters}m`; - } - } - - @injectable() - class Snake extends Animal { - public constructor() { - super('Snake'); - } - public makeSound(input: string): string { - return 'sssss' + input; - } - public override move() { - return 'Slithering... ' + super.move(5); - } - } - - @injectable() - class Jungle { - public animal: Animal; - public constructor(@inject(Animal) animal: Animal) { - this.animal = animal; - } - } - - const container = new Container(); - container.bind(Animal).to(Snake); - container.bind(Jungle).to(Jungle); - - const jungle = container.get(Jungle); - expect(jungle.animal.makeSound('zzz')).to.eql('ssssszzz'); - expect(jungle.animal.move(5)).to.eql('Slithering... Snake moved 5m'); - - }); - - it('Should be able to identify is a target is tagged', () => { - - const TYPES = { - Dependency1: Symbol.for('Dependency1'), - Dependency2: Symbol.for('Dependency2'), - Dependency3: Symbol.for('Dependency3'), - Dependency4: Symbol.for('Dependency4'), - Dependency5: Symbol.for('Dependency5'), - Test: Symbol.for('Test') - }; - - const TAGS = { - somename: 'somename', - sometag: 'sometag' - }; - - @injectable() - class Dependency1 { - public name = 'Dependency1'; - } - - @injectable() - class Dependency2 { - public name = 'Dependency1'; - } - - @injectable() - class Dependency3 { - public name = 'Dependency1'; - } - - @injectable() - class Dependency4 { - public name = 'Dependency1'; - } - - @injectable() - class Dependency5 { - public name = 'Dependency1'; - } - - @injectable() - class Base { - public baseProp: string; - public constructor( - @unmanaged() baseProp: string - ) { - this.baseProp = baseProp; - } - } - - @injectable() - class Test extends Base { - - private _prop1: Dependency1; - private _prop2: Dependency2[]; - private _prop3: Dependency3; - private _prop4: Dependency4; - private _prop5: Dependency5; - - public constructor( - @inject(TYPES.Dependency1) prop1: Dependency1, // inject - @multiInject(TYPES.Dependency2) prop2: Dependency2[], // multi inject - @inject(TYPES.Dependency3) @named(TAGS.somename) prop3: Dependency3, // named - @inject(TYPES.Dependency4) @tagged(TAGS.sometag, true) prop4: Dependency4, // tagged - @inject(TYPES.Dependency5) @targetName('prop6') prop5: Dependency5 // targetName - ) { - super('unmanaged!'); - this._prop1 = prop1; - this._prop2 = prop2; - this._prop3 = prop3; - this._prop4 = prop4; - this._prop5 = prop5; - } - public debug() { - return { - prop1: this._prop1, - prop2: this._prop2, - prop3: this._prop3, - prop4: this._prop4, - prop5: this._prop5 - }; - } - } - - const container = new Container(); - container.bind(TYPES.Test).to(Test); - container.bind(TYPES.Dependency1).to(Dependency1); - container.bind(TYPES.Dependency2).to(Dependency2); - container.bind(TYPES.Dependency3).to(Dependency3); - container.bind(TYPES.Dependency4).to(Dependency4); - container.bind(TYPES.Dependency5).to(Dependency5); - - function logger(next: interfaces.Next): interfaces.Next { - return (args: interfaces.NextArgs) => { - - const nextContextInterceptor = args.contextInterceptor; - - args.contextInterceptor = (context: interfaces.Context) => { - - context.plan.rootRequest.childRequests.forEach((request, index) => { - - if (request === null || request.target === null) { - throw new Error('Request should not be null!'); - } - - switch (index) { - case 0: - expect(request.target.isNamed()).to.eql(false); - expect(request.target.isTagged()).to.eql(false); - break; - case 1: - expect(request.target.isNamed()).to.eql(false); - expect(request.target.isTagged()).to.eql(false); - break; - case 2: - expect(request.target.isNamed()).to.eql(true); - expect(request.target.isTagged()).to.eql(false); - break; - case 3: - expect(request.target.isNamed()).to.eql(false); - expect(request.target.isTagged()).to.eql(true); - break; - case 4: - expect(request.target.isNamed()).to.eql(false); - expect(request.target.isTagged()).to.eql(false); - } - }); - - if (nextContextInterceptor !== null) { - return nextContextInterceptor(context); - } else { - throw new Error('nextContextInterceptor should not be null!'); - } - - }; - - const result = next(args); - - return result; - }; - } - - container.applyMiddleware(logger); - container.get(TYPES.Test); - - }); - - it('Helper getFunctionName should not throw when using an anonymous function', () => { - - const name = getFunctionName(function (options: unknown) { - return options; - }); - - expect(name).to.eql('Anonymous function: ' + (function (options: unknown) { - return options; - }).toString()); - - }); - - it('Should be able to get all the available bindings for a service identifier', () => { - - const controllerId = 'SomeControllerID'; - const tagA = 'A'; - const tagB = 'B'; - - interface Controller { - name: string; - } - - const container = new Container(); - - @injectable() - class AppController implements Controller { - public name: string; - public constructor() { - this.name = 'AppController'; - } - } - - @injectable() - class AppController2 implements Controller { - public name: string; - public constructor() { - this.name = 'AppController2'; - } - } - - container.bind(controllerId).to(AppController).whenTargetNamed(tagA); - container.bind(controllerId).to(AppController2).whenTargetNamed(tagB); - - function wrongNamedBinding() { container.getAllNamed(controllerId, 'Wrong'); } - expect(wrongNamedBinding).to.throw(); - - const appControllerNamedRight = container.getAllNamed(controllerId, tagA); - expect(appControllerNamedRight.length).to.eql(1, 'getAllNamed'); - expect(appControllerNamedRight[0]?.name).to.eql('AppController'); - - function wrongTaggedBinding() { container.getAllTagged(controllerId, 'Wrong', 'Wrong'); } - expect(wrongTaggedBinding).to.throw(); - - const appControllerTaggedRight = container.getAllTagged(controllerId, METADATA_KEY.NAMED_TAG, tagB); - expect(appControllerTaggedRight.length).to.eql(1, 'getAllTagged'); - expect(appControllerTaggedRight[0]?.name).to.eql('AppController2'); - - const getAppController = () => { - const matches = container.getAll(controllerId); - expect(matches.length).to.eql(2); - expect(matches[0]?.name).to.eql('AppController'); - expect(matches[1]?.name).to.eql('AppController2'); - }; - - expect(getAppController).not.to.throw(); - - }); - - it('Should not be able to get a named dependency if no named bindings are registered', () => { - - const TYPES = { - Weapon: 'Weapon' - }; - - interface Weapon { - name: string; - } - - @injectable() - class Katana implements Weapon { - public name: string; - public constructor() { - this.name = 'Katana'; - } - } - - const container = new Container(); - container.bind(TYPES.Weapon).to(Katana).whenTargetNamed('sword'); - - const throws = () => { container.getNamed(TYPES.Weapon, 'bow'); }; - - const error = 'No matching bindings found for serviceIdentifier: Weapon\n Weapon ' + - '- named: bow \n\nRegistered bindings:\n Katana - named: sword '; - - expect(throws).to.throw(error); - - }); - - it('Should throw a friendly error when binding a non-class using toSelf', () => { - const container = new Container(); - const throws = () => { container.bind('testId').toSelf(); }; - expect(throws).to.throw(ERROR_MSGS.INVALID_TO_SELF_VALUE); - }); - - it('Should generate correct metadata when the spread operator is used', () => { - - const BAR = Symbol.for('BAR'); - const FOO = Symbol.for('FOO'); - - interface Bar { - name: string; - } - - @injectable() - class Foo { - public bar: Bar[]; - public constructor(@multiInject(BAR) ...args: Bar[][]) { - this.bar = args[0] as Bar[]; - } - } - - // is the metadata correct? - const serviceIdentifiers = Reflect.getMetadata(METADATA_KEY.TAGGED, Foo); - expect(serviceIdentifiers['0'][0].value.toString()).to.be.eql('Symbol(BAR)'); - - // is the plan correct? - const dependencies = getDependencies(new MetadataReader(), Foo); - expect(dependencies.length).to.be.eql(1); - expect(dependencies[0]?.serviceIdentifier.toString()).to.be.eql('Symbol(BAR)'); - - // integration test - const container = new Container(); - container.bind(BAR).toConstantValue({ name: 'bar1' }); - container.bind(BAR).toConstantValue({ name: 'bar2' }); - container.bind(FOO).to(Foo); - const foo = container.get(FOO); - expect(foo.bar.length).to.eql(2); - expect(foo.bar[0]?.name).to.eql('bar1'); - expect(foo.bar[1]?.name).to.eql('bar2'); - - }); - - it('Should be able to inject into an abstract class', () => { - - interface Weapon { } - - @injectable() - abstract class BaseSoldier { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { - this.weapon = weapon; - } - } - - @injectable() - class Soldier extends BaseSoldier { } - - @injectable() - class Archer extends BaseSoldier { } - - @injectable() - class Knight extends BaseSoldier { } - - @injectable() - class Sword implements Weapon { } - - @injectable() - class Bow implements Weapon { } - - @injectable() - class DefaultWeapon implements Weapon { } - - const container = new Container(); - - container.bind('Weapon').to(DefaultWeapon).whenInjectedInto(Soldier); - container.bind('Weapon').to(Sword).whenInjectedInto(Knight); - container.bind('Weapon').to(Bow).whenInjectedInto(Archer); - container.bind('BaseSoldier').to(Soldier).whenTargetNamed('default'); - container.bind('BaseSoldier').to(Knight).whenTargetNamed('knight'); - container.bind('BaseSoldier').to(Archer).whenTargetNamed('archer'); - - const soldier = container.getNamed('BaseSoldier', 'default'); - const knight = container.getNamed('BaseSoldier', 'knight'); - const archer = container.getNamed('BaseSoldier', 'archer'); - - expect(soldier.weapon instanceof DefaultWeapon).to.eql(true); - expect(knight.weapon instanceof Sword).to.eql(true); - expect(archer.weapon instanceof Bow).to.eql(true); - - }); - - it('Should be able apply inject to property shortcut', () => { - - interface Weapon { - use(): string; - } - - @injectable() - class Katana implements Weapon { - public use() { - return 'Used Katana!'; - } - } - - @injectable() - class Ninja { - public constructor(@inject('Weapon') @named('sword') private _weapon: Weapon) { - // - } - public fight() { - return this._weapon.use(); - } - } - - const container = new Container(); - container.bind('Weapon').to(Katana).whenTargetNamed('sword'); - container.bind(Ninja).toSelf(); - - const ninja = container.get(Ninja); - expect(ninja.fight()).eql('Used Katana!'); - - }); - - it('Should be able to inject into abstract base class without decorators', () => { - - const TYPES = { - Warrior: 'Warrior', - Weapon: 'Weapon' - }; - - const TAGS = { - Primary: 'Primary', - Priority: 'Priority', - Secondary: 'Secondary' - }; - - interface Weapon { - name: string; - } - - @injectable() - class Katana implements Weapon { - public name: string; - public constructor() { - this.name = 'Katana'; - } - } - - @injectable() - class Shuriken implements Weapon { - public name: string; - public constructor() { - this.name = 'Shuriken'; - } - } - - interface Warrior { - name: string; - primaryWeapon: Weapon; - } - - abstract class BaseWarrior implements Warrior { - - public name: string; - public primaryWeapon!: Weapon; - - public constructor(@unmanaged() name: string) { - this.name = name; - } - - } - - // @injectable() - decorate(injectable(), BaseWarrior); - - // @inject(TYPES.Weapon) - inject(TYPES.Weapon)(BaseWarrior.prototype, 'primaryWeapon'); - - // @tagged(TAGS.Priority, TAGS.Primary) - tagged(TAGS.Priority, TAGS.Primary)(BaseWarrior.prototype, 'primaryWeapon'); - - @injectable() - class Samurai extends BaseWarrior { - - @inject(TYPES.Weapon) - @tagged(TAGS.Priority, TAGS.Secondary) - public secondaryWeapon!: Weapon; - - public constructor() { - super('Samurai'); - } - } - - const container = new Container(); - container.bind(TYPES.Warrior).to(Samurai); - container.bind(TYPES.Weapon).to(Katana).whenTargetTagged(TAGS.Priority, TAGS.Primary); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetTagged(TAGS.Priority, TAGS.Secondary); - - const samurai = container.get(TYPES.Warrior); - expect(samurai.name).to.eql('Samurai'); - expect(samurai.secondaryWeapon).not.to.eql(undefined); - expect(samurai.secondaryWeapon.name).to.eql('Shuriken'); - expect(samurai.primaryWeapon).not.to.eql(undefined); - expect(samurai.primaryWeapon.name).to.eql('Katana'); - }); - - it('Should be able to combine unmanaged and managed injections ', () => { - - interface Model { - instance: T; - } - - interface RepoBaseInterface { - model: Model; - } - - class Type { - public name: string; - public constructor() { - this.name = 'Type'; - } - } - - @injectable() - class RepoBase implements RepoBaseInterface { - - public model: Model; - - public constructor( - // using @unmanaged() here is right - // because entityType is NOT Injected by inversify - @unmanaged() entityType: new () => T - ) { - this.model = { instance: new entityType() }; - } - - } - - @injectable() - class TypedRepo extends RepoBase { - public constructor() { - super(Type); // unmanaged injection (NOT Injected by inversify) - } - } - - @injectable() - class BLBase { - - public repository: RepoBaseInterface; - - public constructor( - // using @unmanaged() here would wrong - // because repository is injected by inversify - repository: RepoBaseInterface - ) { - this.repository = repository; - } - } - - @injectable() - class TypedBL extends BLBase { - public constructor( - repository: TypedRepo // Injected by inversify (no @inject required) - ) { - super(repository); // managed injection (Injected by inversify) - } - } - - const container = new Container(); - container.bind(TypedRepo).toSelf(); - container.bind('TypedBL').to(TypedBL); - - const typedBL = container.get('TypedBL'); - expect(typedBL.repository.model.instance.name).to.eq(new Type().name); - - }); - - it('Should detect missing annotations in base classes', () => { - - @injectable() - class Katana implements Katana { - public hit() { - return 'cut!'; - } - } - - abstract class Warrior { - private _katana: Katana; - - public constructor( - @unmanaged() katana: Katana - ) { - this._katana = katana; - } - - public fight() { return this._katana.hit(); } - } - - @injectable() - class Ninja extends Warrior { - public constructor( - @inject('Katana') katana: Katana - ) { - super(katana); - } - } - - const container = new Container(); - container.bind('Ninja').to(Ninja); - container.bind('Katana').to(Katana); - - const tryGet = () => { - container.get('Ninja'); - }; - - expect(tryGet).to.throw('Missing required @injectable annotation in: Warrior.'); - - }); - -}); \ No newline at end of file +import { expect } from 'chai'; + +import * as ERROR_MSGS from '../../src/constants/error_msgs'; +import * as METADATA_KEY from '../../src/constants/metadata_keys'; +import { + Container, + decorate, + inject, + injectable, + interfaces, + multiInject, + named, + tagged, + targetName, + unmanaged, +} from '../../src/inversify'; +import { Metadata } from '../../src/planning/metadata'; +import { MetadataReader } from '../../src/planning/metadata_reader'; +import { getDependencies } from '../../src/planning/reflection_utils'; +import { + getFunctionName, + getServiceIdentifierAsString, +} from '../../src/utils/serialization'; + +describe('Bugs', () => { + it('Should throw when args length of base and derived class not match', () => { + @injectable() + class Warrior { + public rank: string; + constructor(rank: string) { + // length = 1 + this.rank = rank; + } + } + + @injectable() + class SamuraiMaster extends Warrior { + constructor() { + // length = 0 + super('master'); + } + } + + const container: Container = new Container(); + container.bind(SamuraiMaster).to(SamuraiMaster); + + const shouldThrow: () => void = function () { + container.get(SamuraiMaster); + }; + + const error: string = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH('SamuraiMaster'); + expect(shouldThrow).to.throw(error); + }); + + it('Should not throw when args length of base and derived class match (property setter)', () => { + @injectable() + class Warrior { + public rank: string | null; + constructor() { + // length = 0 + this.rank = null; + } + } + + @injectable() + class SamuraiMaster extends Warrior { + constructor() { + // length = 0 + super(); + this.rank = 'master'; + } + } + + const container: Container = new Container(); + container.bind(SamuraiMaster).to(SamuraiMaster); + const master: SamuraiMaster = container.get(SamuraiMaster); + + expect(master.rank).eql('master'); + }); + + it('Should not throw when args length of base and derived class match', () => { + // Injecting into the derived class + + @injectable() + class Warrior { + protected rank: string; + constructor(rank: string) { + // length = 1 + this.rank = rank; + } + } + + // eslint-disable-next-line @typescript-eslint/typedef + const TYPES = { Rank: 'Rank' }; + + @injectable() + class SamuraiMaster extends Warrior { + constructor( + @inject(TYPES.Rank) @named('master') public override rank: string, // length = 1 + ) { + super(rank); + } + } + + const container: Container = new Container(); + container.bind(SamuraiMaster).to(SamuraiMaster); + container + .bind(TYPES.Rank) + .toConstantValue('master') + .whenTargetNamed('master'); + + const master: SamuraiMaster = container.get(SamuraiMaster); + expect(master.rank).eql('master'); + }); + + it('Should not throw when args length of base and derived class match', () => { + // Injecting into the derived class with multiple args + + @injectable() + class Warrior { + protected rank: string; + constructor(rank: string) { + // length = 1 + this.rank = rank; + } + } + + interface Weapon { + name: string; + } + + @injectable() + class Katana implements Weapon { + public name: string; + constructor() { + this.name = 'Katana'; + } + } + + // eslint-disable-next-line @typescript-eslint/typedef + const TYPES = { + Rank: 'Rank', + Weapon: 'Weapon', + }; + + @injectable() + class SamuraiMaster extends Warrior { + public weapon: Weapon; + constructor( + @inject(TYPES.Rank) @named('master') public override rank: string, + @inject(TYPES.Weapon) weapon: Weapon, + ) { + // length = 2 + super(rank); + this.weapon = weapon; + } + } + + const container: Container = new Container(); + container.bind(TYPES.Weapon).to(Katana); + container.bind(SamuraiMaster).to(SamuraiMaster); + container + .bind(TYPES.Rank) + .toConstantValue('master') + .whenTargetNamed('master'); + + const master: SamuraiMaster = container.get(SamuraiMaster); + expect(master.rank).eql('master'); + expect(master.weapon.name).eql('Katana'); + }); + + it('Should be able to convert a Symbol value to a string', () => { + type Weapon = unknown; + + // eslint-disable-next-line @typescript-eslint/typedef + const TYPES = { + Weapon: Symbol.for('Weapon'), + }; + + const container: Container = new Container(); + const throwF: () => void = () => { + container.get(TYPES.Weapon); + }; + expect(throwF).to.throw( + `${ERROR_MSGS.NOT_REGISTERED} ${getServiceIdentifierAsString(TYPES.Weapon)}`, + ); + }); + + it('Should be not require @inject annotation in toConstructor bindings', () => { + type CategorySortingFn = unknown; + type ContentSortingFn = unknown; + type Collection = unknown; + + @injectable() + class Category { + constructor( + public id: string, + public title: string, + public categoryFirstPermalink: string, + public categoryPermalink: string, + public pagination: number, + public categorySortingFn: CategorySortingFn, + public contentSortingFn: ContentSortingFn, + public belongsToCollection: Collection, + ) { + // do nothing + } + } + + const container: Container = new Container(); + container + .bind>('Newable') + .toConstructor(Category); + const expected: interfaces.Newable = + container.get>('Newable'); + expect(expected).eql(Category); + }); + + it('Should be able to combine tagged injection and constant value bindings', () => { + const container: Container = new Container(); + + type Intl = unknown; + + container + .bind('Intl') + .toConstantValue({ hello: 'bonjour' }) + .whenTargetTagged('lang', 'fr'); + container + .bind('Intl') + .toConstantValue({ goodbye: 'au revoir' }) + .whenTargetTagged('lang', 'fr'); + + const f: () => void = function () { + container.getTagged('Intl', 'lang', 'fr'); + }; + expect(f).to.throw(); + }); + + it('Should be able to combine dynamic value with singleton scope', () => { + const container: Container = new Container(); + + container + .bind('transient_random') + .toDynamicValue((_context: interfaces.Context) => Math.random()) + .inTransientScope(); + + container + .bind('singleton_random') + .toDynamicValue((_context: interfaces.Context) => Math.random()) + .inSingletonScope(); + + const a: number = container.get('transient_random'); + const b: number = container.get('transient_random'); + + expect(a).not.to.eql(b); + + const c: number = container.get('singleton_random'); + const d: number = container.get('singleton_random'); + + expect(c).to.eql(d); + }); + + it('Should be able to use an abstract class as the serviceIdentifier', () => { + @injectable() + abstract class Animal { + protected name: string; + constructor(@unmanaged() name: string) { + this.name = name; + } + public move(meters: number) { + return `${this.name} moved ${meters.toString()}m`; + } + public abstract makeSound(input: string): string; + } + + @injectable() + class Snake extends Animal { + constructor() { + super('Snake'); + } + public makeSound(input: string): string { + return 'sssss' + input; + } + public override move() { + return 'Slithering... ' + super.move(5); + } + } + + @injectable() + class Jungle { + public animal: Animal; + constructor(@inject(Animal) animal: Animal) { + this.animal = animal; + } + } + + const container: Container = new Container(); + container.bind(Animal).to(Snake); + container.bind(Jungle).to(Jungle); + + const jungle: Jungle = container.get(Jungle); + expect(jungle.animal.makeSound('zzz')).to.eql('ssssszzz'); + + expect(jungle.animal.move(5)).to.eql('Slithering... Snake moved 5m'); + }); + + it('Should be able to identify is a target is tagged', () => { + // eslint-disable-next-line @typescript-eslint/typedef + const TYPES = { + Dependency1: Symbol.for('Dependency1'), + Dependency2: Symbol.for('Dependency2'), + Dependency3: Symbol.for('Dependency3'), + Dependency4: Symbol.for('Dependency4'), + Dependency5: Symbol.for('Dependency5'), + Test: Symbol.for('Test'), + }; + + // eslint-disable-next-line @typescript-eslint/typedef + const TAGS = { + somename: 'somename', + sometag: 'sometag', + }; + + @injectable() + class Dependency1 { + public name: string = 'Dependency1'; + } + + @injectable() + class Dependency2 { + public name: string = 'Dependency1'; + } + + @injectable() + class Dependency3 { + public name: string = 'Dependency1'; + } + + @injectable() + class Dependency4 { + public name: string = 'Dependency1'; + } + + @injectable() + class Dependency5 { + public name: string = 'Dependency1'; + } + + @injectable() + class Base { + public baseProp: string; + constructor(@unmanaged() baseProp: string) { + this.baseProp = baseProp; + } + } + + @injectable() + class Test extends Base { + private readonly _prop1: Dependency1; + private readonly _prop2: Dependency2[]; + private readonly _prop3: Dependency3; + private readonly _prop4: Dependency4; + private readonly _prop5: Dependency5; + + constructor( + @inject(TYPES.Dependency1) prop1: Dependency1, // inject + @multiInject(TYPES.Dependency2) prop2: Dependency2[], // multi inject + @inject(TYPES.Dependency3) @named(TAGS.somename) prop3: Dependency3, // named + @inject(TYPES.Dependency4) + @tagged(TAGS.sometag, true) + prop4: Dependency4, // tagged + @inject(TYPES.Dependency5) @targetName('prop6') prop5: Dependency5, // targetName + ) { + super('unmanaged!'); + this._prop1 = prop1; + this._prop2 = prop2; + this._prop3 = prop3; + this._prop4 = prop4; + this._prop5 = prop5; + } + public debug() { + return { + prop1: this._prop1, + prop2: this._prop2, + prop3: this._prop3, + prop4: this._prop4, + prop5: this._prop5, + }; + } + } + + const container: Container = new Container(); + container.bind(TYPES.Test).to(Test); + container.bind(TYPES.Dependency1).to(Dependency1); + container.bind(TYPES.Dependency2).to(Dependency2); + container.bind(TYPES.Dependency3).to(Dependency3); + container.bind(TYPES.Dependency4).to(Dependency4); + container.bind(TYPES.Dependency5).to(Dependency5); + + function logger(next: interfaces.Next): interfaces.Next { + return (args: interfaces.NextArgs) => { + const nextContextInterceptor: ( + contexts: interfaces.Context, + ) => interfaces.Context = args.contextInterceptor; + + args.contextInterceptor = (context: interfaces.Context) => { + context.plan.rootRequest.childRequests.forEach( + (request: interfaces.Request | null, index: number) => { + if ( + request === null || + (request.target as interfaces.Target | null) === null + ) { + throw new Error('Request should not be null!'); + } + + switch (index) { + case 0: + expect(request.target.isNamed()).to.eql(false); + expect(request.target.isTagged()).to.eql(false); + break; + case 1: + expect(request.target.isNamed()).to.eql(false); + expect(request.target.isTagged()).to.eql(false); + break; + + case 2: + expect(request.target.isNamed()).to.eql(true); + expect(request.target.isTagged()).to.eql(false); + break; + + case 3: + expect(request.target.isNamed()).to.eql(false); + expect(request.target.isTagged()).to.eql(true); + break; + + case 4: + expect(request.target.isNamed()).to.eql(false); + expect(request.target.isTagged()).to.eql(false); + } + }, + ); + + return nextContextInterceptor(context); + }; + + const result: unknown = next(args); + + return result; + }; + } + + container.applyMiddleware(logger); + container.get(TYPES.Test); + }); + + it('Helper getFunctionName should not throw when using an anonymous function', () => { + const anonymousFunctionBuilder: () => (options: unknown) => unknown = + () => + (options: unknown): unknown => { + return options; + }; + + const name: string = getFunctionName(anonymousFunctionBuilder()); + + expect(name).to.eql( + 'Anonymous function: ' + anonymousFunctionBuilder().toString(), + ); + }); + + it('Should be able to get all the available bindings for a service identifier', () => { + const controllerId: string = 'SomeControllerID'; + const tagA: string = 'A'; + const tagB: string = 'B'; + + interface Controller { + name: string; + } + + const container: Container = new Container(); + + @injectable() + class AppController implements Controller { + public name: string; + constructor() { + this.name = 'AppController'; + } + } + + @injectable() + class AppController2 implements Controller { + public name: string; + constructor() { + this.name = 'AppController2'; + } + } + + container.bind(controllerId).to(AppController).whenTargetNamed(tagA); + container.bind(controllerId).to(AppController2).whenTargetNamed(tagB); + + function wrongNamedBinding() { + container.getAllNamed(controllerId, 'Wrong'); + } + expect(wrongNamedBinding).to.throw(); + + const appControllerNamedRight: Controller[] = + container.getAllNamed(controllerId, tagA); + expect(appControllerNamedRight.length).to.eql(1, 'getAllNamed'); + expect(appControllerNamedRight[0]?.name).to.eql('AppController'); + + function wrongTaggedBinding() { + container.getAllTagged(controllerId, 'Wrong', 'Wrong'); + } + expect(wrongTaggedBinding).to.throw(); + + const appControllerTaggedRight: Controller[] = + container.getAllTagged( + controllerId, + METADATA_KEY.NAMED_TAG, + tagB, + ); + expect(appControllerTaggedRight.length).to.eql(1, 'getAllTagged'); + expect(appControllerTaggedRight[0]?.name).to.eql('AppController2'); + + const getAppController: () => void = () => { + const matches: Controller[] = container.getAll(controllerId); + + expect(matches.length).to.eql(2); + expect(matches[0]?.name).to.eql('AppController'); + expect(matches[1]?.name).to.eql('AppController2'); + }; + + expect(getAppController).not.to.throw(); + }); + + it('Should not be able to get a named dependency if no named bindings are registered', () => { + // eslint-disable-next-line @typescript-eslint/typedef + const TYPES = { + Weapon: 'Weapon', + }; + + interface Weapon { + name: string; + } + + @injectable() + class Katana implements Weapon { + public name: string; + constructor() { + this.name = 'Katana'; + } + } + + const container: Container = new Container(); + container.bind(TYPES.Weapon).to(Katana).whenTargetNamed('sword'); + + const throws: () => void = () => { + container.getNamed(TYPES.Weapon, 'bow'); + }; + + const error: string = + 'No matching bindings found for serviceIdentifier: Weapon\n Weapon ' + + '- named: bow \n\nRegistered bindings:\n Katana - named: sword '; + + expect(throws).to.throw(error); + }); + + it('Should throw a friendly error when binding a non-class using toSelf', () => { + const container: Container = new Container(); + const throws: () => void = () => { + container.bind('testId').toSelf(); + }; + expect(throws).to.throw(ERROR_MSGS.INVALID_TO_SELF_VALUE); + }); + + it('Should generate correct metadata when the spread operator is used', () => { + const BAR: symbol = Symbol.for('BAR'); + const FOO: symbol = Symbol.for('FOO'); + + interface Bar { + name: string; + } + + @injectable() + class Foo { + public bar: Bar[]; + constructor(@multiInject(BAR) ...args: Bar[][]) { + this.bar = args[0] as Bar[]; + } + } + + // is the metadata correct? + const serviceIdentifiers: interfaces.MetadataMap = Reflect.getMetadata( + METADATA_KEY.TAGGED, + Foo, + ) as interfaces.MetadataMap; + + const zeroIndexedMetadata: interfaces.Metadata[] = serviceIdentifiers[ + '0' + ] as interfaces.Metadata[]; + + const expectedMetadata: interfaces.Metadata = new Metadata( + METADATA_KEY.MULTI_INJECT_TAG, + BAR, + ); + + expect(zeroIndexedMetadata).to.deep.equal([expectedMetadata]); + + // is the plan correct? + const dependencies: interfaces.Target[] = getDependencies( + new MetadataReader(), + Foo, + ); + expect(dependencies.length).to.be.eql(1); + expect(dependencies[0]?.serviceIdentifier.toString()).to.be.eql( + 'Symbol(BAR)', + ); + + // integration test + const container: Container = new Container(); + container.bind(BAR).toConstantValue({ name: 'bar1' }); + container.bind(BAR).toConstantValue({ name: 'bar2' }); + container.bind(FOO).to(Foo); + const foo: Foo = container.get(FOO); + + expect(foo.bar.length).to.eql(2); + expect(foo.bar[0]?.name).to.eql('bar1'); + expect(foo.bar[1]?.name).to.eql('bar2'); + }); + + it('Should be able to inject into an abstract class', () => { + type Weapon = unknown; + + @injectable() + abstract class BaseSoldier { + public weapon: Weapon; + constructor(@inject('Weapon') weapon: Weapon) { + this.weapon = weapon; + } + } + + @injectable() + class Soldier extends BaseSoldier {} + + @injectable() + class Archer extends BaseSoldier {} + + @injectable() + class Knight extends BaseSoldier {} + + @injectable() + class Sword {} + + @injectable() + class Bow {} + + @injectable() + class DefaultWeapon {} + + const container: Container = new Container(); + + container + .bind('Weapon') + .to(DefaultWeapon) + .whenInjectedInto(Soldier); + container.bind('Weapon').to(Sword).whenInjectedInto(Knight); + container.bind('Weapon').to(Bow).whenInjectedInto(Archer); + container + .bind('BaseSoldier') + .to(Soldier) + .whenTargetNamed('default'); + container + .bind('BaseSoldier') + .to(Knight) + .whenTargetNamed('knight'); + container + .bind('BaseSoldier') + .to(Archer) + .whenTargetNamed('archer'); + + const soldier: BaseSoldier = container.getNamed( + 'BaseSoldier', + 'default', + ); + const knight: BaseSoldier = container.getNamed( + 'BaseSoldier', + 'knight', + ); + const archer: BaseSoldier = container.getNamed( + 'BaseSoldier', + 'archer', + ); + + expect(soldier.weapon instanceof DefaultWeapon).to.eql(true); + expect(knight.weapon instanceof Sword).to.eql(true); + expect(archer.weapon instanceof Bow).to.eql(true); + }); + + it('Should be able apply inject to property shortcut', () => { + interface Weapon { + use(): string; + } + + @injectable() + class Katana implements Weapon { + public use() { + return 'Used Katana!'; + } + } + + @injectable() + class Ninja { + constructor( + @inject('Weapon') @named('sword') private readonly _weapon: Weapon, + ) { + // + } + public fight() { + return this._weapon.use(); + } + } + + const container: Container = new Container(); + container.bind('Weapon').to(Katana).whenTargetNamed('sword'); + container.bind(Ninja).toSelf(); + + const ninja: Ninja = container.get(Ninja); + expect(ninja.fight()).eql('Used Katana!'); + }); + + it('Should be able to inject into abstract base class without decorators', () => { + // eslint-disable-next-line @typescript-eslint/typedef + const TYPES = { + Warrior: 'Warrior', + Weapon: 'Weapon', + }; + + // eslint-disable-next-line @typescript-eslint/typedef + const TAGS = { + Primary: 'Primary', + Priority: 'Priority', + Secondary: 'Secondary', + }; + + interface Weapon { + name: string; + } + + @injectable() + class Katana implements Weapon { + public name: string; + constructor() { + this.name = 'Katana'; + } + } + + @injectable() + class Shuriken implements Weapon { + public name: string; + constructor() { + this.name = 'Shuriken'; + } + } + + interface Warrior { + name: string; + primaryWeapon: Weapon; + } + + abstract class BaseWarrior implements Warrior { + public name: string; + public primaryWeapon!: Weapon; + + constructor(@unmanaged() name: string) { + this.name = name; + } + } + + // @injectable() + decorate(injectable(), BaseWarrior); + + // @inject(TYPES.Weapon) + inject(TYPES.Weapon)(BaseWarrior.prototype, 'primaryWeapon'); + + // @tagged(TAGS.Priority, TAGS.Primary) + tagged(TAGS.Priority, TAGS.Primary)(BaseWarrior.prototype, 'primaryWeapon'); + + @injectable() + class Samurai extends BaseWarrior { + @inject(TYPES.Weapon) + @tagged(TAGS.Priority, TAGS.Secondary) + public secondaryWeapon!: Weapon; + + constructor() { + super('Samurai'); + } + } + + const container: Container = new Container(); + container.bind(TYPES.Warrior).to(Samurai); + container + .bind(TYPES.Weapon) + .to(Katana) + .whenTargetTagged(TAGS.Priority, TAGS.Primary); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetTagged(TAGS.Priority, TAGS.Secondary); + + const samurai: Samurai = container.get(TYPES.Warrior); + + expect(samurai.name).to.eql('Samurai'); + expect(samurai.secondaryWeapon).not.to.eql(undefined); + expect(samurai.secondaryWeapon.name).to.eql('Shuriken'); + expect(samurai.primaryWeapon).not.to.eql(undefined); + expect(samurai.primaryWeapon.name).to.eql('Katana'); + }); + + it('Should be able to combine unmanaged and managed injections ', () => { + interface Model { + instance: T; + } + + interface RepoBaseInterface { + model: Model; + } + + class Type { + public name: string; + constructor() { + this.name = 'Type'; + } + } + + @injectable() + class RepoBase implements RepoBaseInterface { + public model: Model; + + constructor( + // using @unmanaged() here is right + // because entityType is NOT Injected by inversify + @unmanaged() entityType: new () => T, + ) { + this.model = { instance: new entityType() }; + } + } + + @injectable() + class TypedRepo extends RepoBase { + constructor() { + super(Type); // unmanaged injection (NOT Injected by inversify) + } + } + + @injectable() + class BlBase { + public repository: RepoBaseInterface; + + constructor( + // using @unmanaged() here would wrong + // because repository is injected by inversify + repository: RepoBaseInterface, + ) { + this.repository = repository; + } + } + + @injectable() + class TypedBl extends BlBase { + // eslint-disable-next-line @typescript-eslint/no-useless-constructor + constructor(repository: TypedRepo) { + super(repository); + } + } + + const container: Container = new Container(); + container.bind(TypedRepo).toSelf(); + container.bind('TypedBL').to(TypedBl); + + const typedBl: TypedBl = container.get('TypedBL'); + expect(typedBl.repository.model.instance.name).to.eq(new Type().name); + }); + + it('Should detect missing annotations in base classes', () => { + @injectable() + class Katana implements Katana { + public hit() { + return 'cut!'; + } + } + + abstract class Warrior { + private readonly _katana: Katana; + + constructor(@unmanaged() katana: Katana) { + this._katana = katana; + } + + public fight() { + return this._katana.hit(); + } + } + + @injectable() + class Ninja extends Warrior { + constructor(@inject('Katana') katana: Katana) { + super(katana); + } + } + + const container: Container = new Container(); + container.bind('Ninja').to(Ninja); + container.bind('Katana').to(Katana); + + const tryGet: () => void = () => { + container.get('Ninja'); + }; + + expect(tryGet).to.throw( + 'Missing required @injectable annotation in: Warrior.', + ); + }); +}); diff --git a/test/bugs/issue_1190.test.ts b/test/bugs/issue_1190.test.ts index 8d1cdb226..54b5c2446 100644 --- a/test/bugs/issue_1190.test.ts +++ b/test/bugs/issue_1190.test.ts @@ -1,15 +1,23 @@ import { expect } from 'chai'; -import { injectable, inject, optional, Container, named } from '../../src/inversify'; -describe('Issue 1190', () => { +import { + Container, + inject, + injectable, + named, + optional, +} from '../../src/inversify'; +describe('Issue 1190', () => { it('should inject a katana as default weapon to ninja', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Weapon: 'Weapon' + Weapon: 'Weapon', }; + // eslint-disable-next-line @typescript-eslint/typedef const TAG = { - throwable: 'throwable' + throwable: 'throwable', }; interface Weapon { @@ -19,7 +27,7 @@ describe('Issue 1190', () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'Katana'; } } @@ -27,7 +35,7 @@ describe('Issue 1190', () => { @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'Shuriken'; } } @@ -37,9 +45,9 @@ describe('Issue 1190', () => { public name: string; public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject(TYPES.Weapon) @optional() katana: Weapon, - @inject(TYPES.Weapon) @named(TAG.throwable) shuriken: Weapon + @inject(TYPES.Weapon) @named(TAG.throwable) shuriken: Weapon, ) { this.name = 'Ninja'; this.katana = katana; @@ -47,15 +55,18 @@ describe('Issue 1190', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Weapon).to(Katana).whenTargetIsDefault(); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetNamed(TAG.throwable); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetNamed(TAG.throwable); container.bind('Ninja').to(Ninja); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja.katana).to.deep.eq(new Katana()); }); -}); \ No newline at end of file +}); diff --git a/test/bugs/issue_1297.test.ts b/test/bugs/issue_1297.test.ts index 6ff1fb69b..317ddd41b 100644 --- a/test/bugs/issue_1297.test.ts +++ b/test/bugs/issue_1297.test.ts @@ -1,16 +1,21 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; + import { Container, injectable, interfaces } from '../../src/inversify'; describe('Issue 1297', () => { it('should call onActivation once if the service is a constant value binding', () => { - const container = new Container(); + const container: Container = new Container(); - const onActivationHandlerSpy = sinon.spy< - (ctx: interfaces.Context, message: string) => string - >((_ctx: interfaces.Context, message: string) => message); + const onActivationHandlerSpy: sinon.SinonSpy< + [interfaces.Context, string], + string + > = sinon.spy<(ctx: interfaces.Context, message: string) => string>( + (_ctx: interfaces.Context, message: string) => message, + ); - container.bind('message') + container + .bind('message') .toConstantValue('Hello world') .onActivation(onActivationHandlerSpy); @@ -21,7 +26,6 @@ describe('Issue 1297', () => { }); it('should call onActivation once if the service is a factory binding', () => { - @injectable() class Katana { public hit() { @@ -29,17 +33,30 @@ describe('Issue 1297', () => { } } - const container = new Container(); - - const onActivationHandlerSpy = sinon.spy< - (ctx: interfaces.Context, instance: interfaces.Factory) => interfaces.Factory - >((_ctx: interfaces.Context, instance: interfaces.Factory) => instance); + const container: Container = new Container(); + + const onActivationHandlerSpy: sinon.SinonSpy< + [interfaces.Context, interfaces.Factory], + interfaces.Factory + > = sinon.spy< + ( + ctx: interfaces.Context, + instance: interfaces.Factory, + ) => interfaces.Factory + >( + (_ctx: interfaces.Context, instance: interfaces.Factory) => + instance, + ); container.bind('Katana').to(Katana); - container.bind>('Factory').toFactory((context) => - () => - context.container.get('Katana')).onActivation(onActivationHandlerSpy); + container + .bind>('Factory') + .toFactory( + (context: interfaces.Context) => () => + context.container.get('Katana'), + ) + .onActivation(onActivationHandlerSpy); container.get('Factory'); container.get('Factory'); @@ -48,7 +65,6 @@ describe('Issue 1297', () => { }); it('should call onActivation once if the service is an auto factory binding', () => { - @injectable() class Katana { public hit() { @@ -56,16 +72,27 @@ describe('Issue 1297', () => { } } - const container = new Container(); - - const onActivationHandlerSpy = sinon.spy< - (ctx: interfaces.Context, instance: interfaces.Factory) => interfaces.Factory - >((_ctx: interfaces.Context, instance: interfaces.Factory) => instance); + const container: Container = new Container(); + + const onActivationHandlerSpy: sinon.SinonSpy< + [interfaces.Context, interfaces.Factory], + interfaces.Factory + > = sinon.spy< + ( + ctx: interfaces.Context, + instance: interfaces.Factory, + ) => interfaces.Factory + >( + (_ctx: interfaces.Context, instance: interfaces.Factory) => + instance, + ); container.bind('Katana').to(Katana); - container.bind>('Factory') - .toAutoFactory('Katana').onActivation(onActivationHandlerSpy); + container + .bind>('Factory') + .toAutoFactory('Katana') + .onActivation(onActivationHandlerSpy); container.get('Factory'); container.get('Factory'); @@ -74,14 +101,20 @@ describe('Issue 1297', () => { }); it('should call onActivation once if the service is a function binding', () => { + const container: Container = new Container(); - const container = new Container(); - - const onActivationHandlerSpy = sinon.spy< + const onActivationHandlerSpy: sinon.SinonSpy< + [interfaces.Context, () => string], + () => string + > = sinon.spy< (ctx: interfaces.Context, messageGenerator: () => string) => () => string - >((_ctx: interfaces.Context, messageGenerator: () => string) => messageGenerator); + >( + (_ctx: interfaces.Context, messageGenerator: () => string) => + messageGenerator, + ); - container.bind<() => string>('message') + container + .bind<() => string>('message') .toFunction(() => 'Hello world') .onActivation(onActivationHandlerSpy); @@ -92,7 +125,6 @@ describe('Issue 1297', () => { }); it('should call onActivation once if the service is a constructor binding', () => { - @injectable() class Katana { public hit() { @@ -100,13 +132,17 @@ describe('Issue 1297', () => { } } - const container = new Container(); + const container: Container = new Container(); - const onActivationHandlerSpy = sinon.spy< - (ctx: interfaces.Context, injectableObj: unknown) => unknown - >((_ctx: interfaces.Context, injectableObj: unknown) => injectableObj); + const onActivationHandlerSpy: sinon.SinonSpy< + [interfaces.Context, unknown], + unknown + > = sinon.spy<(ctx: interfaces.Context, injectableObj: unknown) => unknown>( + (_ctx: interfaces.Context, injectableObj: unknown) => injectableObj, + ); - container.bind('Katana') + container + .bind('Katana') .toConstructor(Katana) .onActivation(onActivationHandlerSpy); @@ -117,7 +153,6 @@ describe('Issue 1297', () => { }); it('should call onActivation once if the service is a provider binding', () => { - @injectable() class Katana { public hit() { @@ -125,20 +160,26 @@ describe('Issue 1297', () => { } } - const container = new Container(); - - const onActivationHandlerSpy = sinon.spy< - (ctx: interfaces.Context, injectableObj: unknown) => unknown - >((_ctx: interfaces.Context, injectableObj: unknown) => injectableObj); - - container.bind('Provider') - .toProvider((context: interfaces.Context) => - () => - Promise.resolve(new Katana())).onActivation(onActivationHandlerSpy); + const container: Container = new Container(); + + const onActivationHandlerSpy: sinon.SinonSpy< + [interfaces.Context, unknown], + unknown + > = sinon.spy<(ctx: interfaces.Context, injectableObj: unknown) => unknown>( + (_ctx: interfaces.Context, injectableObj: unknown) => injectableObj, + ); + + container + .bind('Provider') + .toProvider( + (_context: interfaces.Context) => async () => + Promise.resolve(new Katana()), + ) + .onActivation(onActivationHandlerSpy); container.get('Provider'); container.get('Provider'); expect(onActivationHandlerSpy.callCount).to.eq(1); }); -}); \ No newline at end of file +}); diff --git a/test/bugs/issue_543.test.ts b/test/bugs/issue_543.test.ts index 9c090db8a..4227fc6a9 100644 --- a/test/bugs/issue_543.test.ts +++ b/test/bugs/issue_543.test.ts @@ -1,44 +1,37 @@ import { expect } from 'chai'; + import * as ERROR_MSGS from '../../src/constants/error_msgs'; import { Container, inject, injectable } from '../../src/inversify'; describe('Issue 543', () => { - it('Should throw correct circular dependency path', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPE = { Child: Symbol.for('Child'), Child2: Symbol.for('Child2'), Circular: Symbol.for('Circular'), Irrelevant: Symbol.for('Irrelevant1'), - Root: Symbol.for('Root') + Root: Symbol.for('Root'), }; - interface IIrrelevant { } - interface ICircular { } - interface IChild { } - interface IChild2 { } - @injectable() - class Irrelevant implements IIrrelevant { } + class Irrelevant {} @injectable() - class Child2 implements IChild2 { - public circ: ICircular; - public constructor( - @inject(TYPE.Circular) circ: ICircular - ) { + class Child2 { + public circ: unknown; + constructor(@inject(TYPE.Circular) circ: unknown) { this.circ = circ; } } @injectable() - class Child implements IChild { - public irrelevant: IIrrelevant; - public child2: IChild2; - public constructor( - @inject(TYPE.Irrelevant) irrelevant: IIrrelevant, - @inject(TYPE.Child2) child2: IChild2 + class Child { + public irrelevant: Irrelevant; + public child2: Child2; + constructor( + @inject(TYPE.Irrelevant) irrelevant: Irrelevant, + @inject(TYPE.Child2) child2: Child2, ) { this.irrelevant = irrelevant; this.child2 = child2; @@ -46,12 +39,12 @@ describe('Issue 543', () => { } @injectable() - class Circular implements Circular { - public irrelevant: IIrrelevant; - public child: IChild; - public constructor( - @inject(TYPE.Irrelevant) irrelevant: IIrrelevant, - @inject(TYPE.Child) child: IChild + class Circular { + public irrelevant: Irrelevant; + public child: Child; + constructor( + @inject(TYPE.Irrelevant) irrelevant: Irrelevant, + @inject(TYPE.Child) child: Child, ) { this.irrelevant = irrelevant; this.child = child; @@ -60,18 +53,18 @@ describe('Issue 543', () => { @injectable() class Root { - public irrelevant: IIrrelevant; - public circ: ICircular; - public constructor( - @inject(TYPE.Irrelevant) irrelevant1: IIrrelevant, - @inject(TYPE.Circular) circ: ICircular + public irrelevant: Irrelevant; + public circ: Circular; + constructor( + @inject(TYPE.Irrelevant) irrelevant1: Irrelevant, + @inject(TYPE.Circular) circ: Circular, ) { this.irrelevant = irrelevant1; this.circ = circ; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPE.Root).to(Root); container.bind(TYPE.Irrelevant).to(Irrelevant); container.bind(TYPE.Circular).to(Circular); @@ -83,9 +76,7 @@ describe('Issue 543', () => { } expect(throws).to.throw( - `${ERROR_MSGS.CIRCULAR_DEPENDENCY} Symbol(Root) --> Symbol(Circular) --> Symbol(Child) --> Symbol(Child2) --> Symbol(Circular)` + `${ERROR_MSGS.CIRCULAR_DEPENDENCY} Symbol(Root) --> Symbol(Circular) --> Symbol(Child) --> Symbol(Child2) --> Symbol(Circular)`, ); - }); - -}); \ No newline at end of file +}); diff --git a/test/bugs/issue_549.test.ts b/test/bugs/issue_549.test.ts index a273d9805..0263d3eba 100644 --- a/test/bugs/issue_549.test.ts +++ b/test/bugs/issue_549.test.ts @@ -1,79 +1,79 @@ import * as ERROR_MSGS from '../../src/constants/error_msgs'; -import { Container, inject, injectable } from '../../src/inversify'; +import { Container, inject, injectable, interfaces } from '../../src/inversify'; describe('Issue 549', () => { - it('Should throw if circular dependencies found with dynamics', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPE = { ADynamicValue: Symbol.for('ADynamicValue'), - BDynamicValue: Symbol.for('BDynamicValue') + BDynamicValue: Symbol.for('BDynamicValue'), }; - interface IA { } - interface IB { } + type InterfaceA = unknown; + type InterfaceB = unknown; @injectable() class A { - public b: IB; - public constructor( - @inject(TYPE.BDynamicValue) b: IB - ) { + public b: InterfaceB; + constructor(@inject(TYPE.BDynamicValue) b: InterfaceB) { this.b = b; } } @injectable() class B { - public a: IA; - public constructor( - @inject(TYPE.ADynamicValue) a: IA - ) { + public a: InterfaceA; + constructor(@inject(TYPE.ADynamicValue) a: InterfaceA) { this.a = a; } } - const container = new Container({ defaultScope: 'Singleton' }); + const container: Container = new Container({ defaultScope: 'Singleton' }); container.bind(A).toSelf(); container.bind(B).toSelf(); - container.bind(TYPE.ADynamicValue).toDynamicValue((ctx) => - ctx.container.get(A) - ); + container + .bind(TYPE.ADynamicValue) + .toDynamicValue((ctx: interfaces.Context) => ctx.container.get(A)); - container.bind(TYPE.BDynamicValue).toDynamicValue((ctx) => - ctx.container.get(B) - ); + container + .bind(TYPE.BDynamicValue) + .toDynamicValue((ctx: interfaces.Context) => ctx.container.get(B)); function willThrow() { return container.get(A); } try { - const result = willThrow(); + const result: A = willThrow(); throw new Error( - `This line should never be executed. Expected \`willThrow\` to throw! ${JSON.stringify(result)}` + `This line should never be executed. Expected \`willThrow\` to throw! ${JSON.stringify(result)}`, ); } catch (e) { - const localError = e as Error; - const expectedErrorA = ERROR_MSGS.CIRCULAR_DEPENDENCY_IN_FACTORY('toDynamicValue', TYPE.ADynamicValue.toString()); - const expectedErrorB = ERROR_MSGS.CIRCULAR_DEPENDENCY_IN_FACTORY('toDynamicValue', TYPE.BDynamicValue.toString()); - const matchesErrorA = localError.message.indexOf(expectedErrorA) !== -1; - const matchesErrorB = localError.message.indexOf(expectedErrorB) !== -1; + const localError: Error = e as Error; + const expectedErrorA: string = ERROR_MSGS.CIRCULAR_DEPENDENCY_IN_FACTORY( + 'toDynamicValue', + TYPE.ADynamicValue.toString(), + ); + const expectedErrorB: string = ERROR_MSGS.CIRCULAR_DEPENDENCY_IN_FACTORY( + 'toDynamicValue', + TYPE.BDynamicValue.toString(), + ); + const matchesErrorA: boolean = + localError.message.indexOf(expectedErrorA) !== -1; + const matchesErrorB: boolean = + localError.message.indexOf(expectedErrorB) !== -1; if (!matchesErrorA && !matchesErrorB) { throw new Error( - 'Expected \`willThrow\` to throw:\n' + - `- ${expectedErrorA}\n` + - 'or\n' + - `- ${expectedErrorB}\n` + - 'but got\n' + - `- ${localError.message}` + 'Expected `willThrow` to throw:\n' + + `- ${expectedErrorA}\n` + + 'or\n' + + `- ${expectedErrorB}\n` + + 'but got\n' + + `- ${localError.message}`, ); } - } - }); - -}); \ No newline at end of file +}); diff --git a/test/bugs/issue_633.test.ts b/test/bugs/issue_633.test.ts index ace283ac3..5202b4b7d 100644 --- a/test/bugs/issue_633.test.ts +++ b/test/bugs/issue_633.test.ts @@ -1,36 +1,37 @@ import { expect } from 'chai'; -import { Container, injectable } from '../../src/inversify'; -describe('Issue 633', () => { +import { Container, injectable, interfaces } from '../../src/inversify'; +describe('Issue 633', () => { it('Should expose metadata through context', () => { - @injectable() class Logger { public named: string; - public constructor(named: string) { + constructor(named: string) { this.named = named; } } - const container = new Container(); + const container: Container = new Container(); + // eslint-disable-next-line @typescript-eslint/typedef const TYPE = { - Logger: Symbol.for('Logger') + Logger: Symbol.for('Logger'), }; - container.bind(TYPE.Logger).toDynamicValue((context) => { - const namedMetadata = context.currentRequest.target.getNamedTag(); - const named = namedMetadata ? namedMetadata.value : 'default'; - return new Logger(named as string); - }); + container + .bind(TYPE.Logger) + .toDynamicValue((context: interfaces.Context) => { + const namedMetadata: interfaces.Metadata | null = + context.currentRequest.target.getNamedTag(); + const named: string = namedMetadata ? namedMetadata.value : 'default'; + return new Logger(named); + }); - const logger1 = container.getNamed(TYPE.Logger, 'Name1'); - const logger2 = container.getNamed(TYPE.Logger, 'Name2'); + const logger1: Logger = container.getNamed(TYPE.Logger, 'Name1'); + const logger2: Logger = container.getNamed(TYPE.Logger, 'Name2'); expect(logger1.named).to.eq('Name1'); expect(logger2.named).to.eq('Name2'); - }); - -}); \ No newline at end of file +}); diff --git a/test/bugs/issue_706.test.ts b/test/bugs/issue_706.test.ts index 07a70c0b4..bc5600a65 100644 --- a/test/bugs/issue_706.test.ts +++ b/test/bugs/issue_706.test.ts @@ -1,33 +1,31 @@ import { expect } from 'chai'; + import { BindingScopeEnum, Container, injectable } from '../../src/inversify'; describe('Issue 706', () => { - it('Should expose BindingScopeEnum as part of the public API', () => { - @injectable() class SomeClass { public time: number; - public constructor() { + constructor() { this.time = new Date().getTime(); } } - const container = new Container({ + const container: Container = new Container({ defaultScope: BindingScopeEnum.Singleton, }); + // eslint-disable-next-line @typescript-eslint/typedef const TYPE = { - SomeClass: Symbol.for('SomeClass') + SomeClass: Symbol.for('SomeClass'), }; container.bind(TYPE.SomeClass).to(SomeClass); - const instanceOne = container.get(TYPE.SomeClass); - const instanceTwo = container.get(TYPE.SomeClass); + const instanceOne: SomeClass = container.get(TYPE.SomeClass); + const instanceTwo: SomeClass = container.get(TYPE.SomeClass); expect(instanceOne.time).to.eq(instanceTwo.time); - }); - -}); \ No newline at end of file +}); diff --git a/test/bugs/issue_928.test.ts b/test/bugs/issue_928.test.ts index 2c08c5da6..19aba5692 100644 --- a/test/bugs/issue_928.test.ts +++ b/test/bugs/issue_928.test.ts @@ -1,37 +1,55 @@ import { expect } from 'chai'; -import { Container, inject, injectable, optional } from '../../src/inversify'; +import { Container, inject, injectable, optional } from '../../src/inversify'; describe('Issue 928', () => { - it('should inject the right instances', () => { - let injectedA: unknown; let injectedB: unknown; let injectedC: unknown; // some dependencies - @injectable() class DepA { a = 1 } - @injectable() class DepB { b = 1 } - @injectable() class DepC { c = 1 } + @injectable() + class DepA { + public a: number = 1; + } + @injectable() + class DepB { + public b: number = 1; + } + @injectable() + class DepC { + public c: number = 1; + } - @injectable() abstract class AbstractCls { - constructor(@inject(DepA) a: DepA, @inject(DepB) @optional() b: DepB = { b: 0 }) { + @injectable() + abstract class AbstractCls { + constructor( + @inject(DepA) a: DepA, + @inject(DepB) @optional() b: DepB = { b: 0 }, + ) { injectedA = a; injectedB = b; } } - @injectable() class Cls extends AbstractCls { - constructor(@inject(DepC) c: DepC, @inject(DepB) @optional() b: DepB = { b: 0 }, @inject(DepA) a: DepA) { + @injectable() + class Cls extends AbstractCls { + constructor( + @inject(DepC) c: DepC, + @inject(DepB) @optional() b: DepB = { b: 0 }, + @inject(DepA) a: DepA, + ) { super(a, b); injectedC = c; } } - const container = new Container(); - [DepA, DepB, DepC, Cls].forEach(i => container.bind(i).toSelf().inSingletonScope()); + const container: Container = new Container(); + [DepA, DepB, DepC, Cls].forEach((i: NewableFunction) => + container.bind(i).toSelf().inSingletonScope(), + ); container.get(Cls); @@ -39,4 +57,4 @@ describe('Issue 928', () => { expect(injectedB).to.deep.eq(new DepB()); expect(injectedC).to.deep.eq(new DepC()); }); -}); \ No newline at end of file +}); diff --git a/test/constants/error_message.test.ts b/test/constants/error_message.test.ts index a9cebbeb5..d0791202f 100644 --- a/test/constants/error_message.test.ts +++ b/test/constants/error_message.test.ts @@ -1,25 +1,31 @@ import { expect } from 'chai'; + import * as ERROR_MSGS from '../../src/constants/error_msgs'; describe('ERROR_MSGS', () => { - it('Should be able to customize POST_CONSTRUCT_ERROR', () => { - const error = ERROR_MSGS.POST_CONSTRUCT_ERROR('a', 'b'); + const error: string = ERROR_MSGS.POST_CONSTRUCT_ERROR('a', 'b'); expect(error).eql('@postConstruct error in class a: b'); }); it('Should properly stringify symbol in LAZY_IN_SYNC', () => { - const error = ERROR_MSGS.LAZY_IN_SYNC(Symbol('a')); - expect(error).eql(`You are attempting to construct Symbol(a) in a synchronous way but it has asynchronous dependencies.`); + const error: string = ERROR_MSGS.LAZY_IN_SYNC(Symbol('a')); + expect(error).eql( + `You are attempting to construct Symbol(a) in a synchronous way but it has asynchronous dependencies.`, + ); }); it('Should properly stringify class in LAZY_IN_SYNC', () => { - const error = ERROR_MSGS.LAZY_IN_SYNC(class B {}); - expect(error).eql(`You are attempting to construct [function/class B] in a synchronous way but it has asynchronous dependencies.`); + const error: string = ERROR_MSGS.LAZY_IN_SYNC(class B {}); + expect(error).eql( + `You are attempting to construct [function/class B] in a synchronous way but it has asynchronous dependencies.`, + ); }); it('Should properly stringify string in LAZY_IN_SYNC', () => { - const error = ERROR_MSGS.LAZY_IN_SYNC('c'); - expect(error).eql(`You are attempting to construct 'c' in a synchronous way but it has asynchronous dependencies.`); + const error: string = ERROR_MSGS.LAZY_IN_SYNC('c'); + expect(error).eql( + `You are attempting to construct 'c' in a synchronous way but it has asynchronous dependencies.`, + ); }); }); diff --git a/test/container/container.test.ts b/test/container/container.test.ts index 0a98374ff..7867d885d 100644 --- a/test/container/container.test.ts +++ b/test/container/container.test.ts @@ -1,5 +1,6 @@ import { assert, expect } from 'chai'; import * as sinon from 'sinon'; + import { inject } from '../../src/annotation/inject'; import { injectable } from '../../src/annotation/injectable'; import { postConstruct } from '../../src/annotation/post_construct'; @@ -12,10 +13,12 @@ import { interfaces } from '../../src/interfaces/interfaces'; import { getBindingDictionary } from '../../src/planning/planner'; import { getServiceIdentifierAsString } from '../../src/utils/serialization'; -type Dictionary = Map[]>; +type Dictionary = Map< + interfaces.ServiceIdentifier, + interfaces.Binding[] +>; describe('Container', () => { - let sandbox: sinon.SinonSandbox; beforeEach(() => { @@ -27,44 +30,51 @@ describe('Container', () => { }); it('Should be able to use modules as configuration', () => { - - interface Ninja { } - interface Katana { } - interface Shuriken { } - @injectable() - class Katana implements Katana { } + class Katana {} @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} @injectable() - class Ninja implements Ninja { } - - const warriors = new ContainerModule((bind: interfaces.Bind) => { - bind('Ninja').to(Ninja); - }); - - const weapons = new ContainerModule((bind: interfaces.Bind) => { - bind('Katana').to(Katana); - bind('Shuriken').to(Shuriken); - }); - - const container = new Container(); + class Ninja {} + + const warriors: ContainerModule = new ContainerModule( + (bind: interfaces.Bind) => { + bind('Ninja').to(Ninja); + }, + ); + + const weapons: ContainerModule = new ContainerModule( + (bind: interfaces.Bind) => { + bind('Katana').to(Katana); + bind('Shuriken').to(Shuriken); + }, + ); + + const container: Container = new Container(); container.load(warriors, weapons); let map: Dictionary = getBindingDictionary(container).getMap(); expect(map.has('Ninja')).equal(true); expect(map.has('Katana')).equal(true); expect(map.has('Shuriken')).equal(true); + expect(map.size).equal(3); - const tryGetNinja = () => { container.get('Ninja'); }; - const tryGetKatana = () => { container.get('Katana'); }; - const tryGetShuruken = () => { container.get('Shuriken'); }; + const tryGetNinja: () => void = () => { + container.get('Ninja'); + }; + const tryGetKatana: () => void = () => { + container.get('Katana'); + }; + const tryGetShuruken: () => void = () => { + container.get('Shuriken'); + }; container.unload(warriors); map = getBindingDictionary(container).getMap(); + expect(map.size).equal(2); expect(tryGetNinja).to.throw(ERROR_MSGS.NOT_REGISTERED); expect(tryGetKatana).not.to.throw(); @@ -76,45 +86,35 @@ describe('Container', () => { expect(tryGetNinja).to.throw(ERROR_MSGS.NOT_REGISTERED); expect(tryGetKatana).to.throw(ERROR_MSGS.NOT_REGISTERED); expect(tryGetShuruken).to.throw(ERROR_MSGS.NOT_REGISTERED); - }); it('Should be able to store bindings', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } - const ninjaId = 'Ninja'; + class Ninja {} + const ninjaId: string = 'Ninja'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); const map: Dictionary = getBindingDictionary(container).getMap(); expect(map.size).equal(1); expect(map.has(ninjaId)).equal(true); - }); it('Should have an unique identifier', () => { - - const container1 = new Container(); - const container2 = new Container(); + const container1: Container = new Container(); + const container2: Container = new Container(); expect(container1.id).to.be.a('number'); expect(container2.id).to.be.a('number'); expect(container1.id).not.equal(container2.id); - }); it('Should unbind a binding when requested', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } - const ninjaId = 'Ninja'; + class Ninja {} + const ninjaId: string = 'Ninja'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); const map: Dictionary = getBindingDictionary(container).getMap(); @@ -123,43 +123,44 @@ describe('Container', () => { container.unbind(ninjaId); expect(map.has(ninjaId)).equal(false); expect(map.size).equal(0); - }); it('Should throw when cannot unbind', () => { - const serviceIdentifier = 'Ninja'; - const container = new Container(); - const throwFunction = () => { container.unbind(serviceIdentifier); }; - expect(throwFunction).to.throw(`${ERROR_MSGS.CANNOT_UNBIND} ${getServiceIdentifierAsString(serviceIdentifier)}`); + const serviceIdentifier: string = 'Ninja'; + const container: Container = new Container(); + const throwFunction: () => void = () => { + container.unbind(serviceIdentifier); + }; + expect(throwFunction).to.throw( + `${ERROR_MSGS.CANNOT_UNBIND} ${getServiceIdentifierAsString(serviceIdentifier)}`, + ); }); it('Should throw when cannot unbind (async)', async () => { - const serviceIdentifier = 'Ninja'; - const container = new Container(); + const serviceIdentifier: string = 'Ninja'; + const container: Container = new Container(); try { await container.unbindAsync(serviceIdentifier); assert.fail(); } catch (err: unknown) { - expect((err as Error).message).to.eql(`${ERROR_MSGS.CANNOT_UNBIND} ${getServiceIdentifierAsString(serviceIdentifier)}`); + expect((err as Error).message).to.eql( + `${ERROR_MSGS.CANNOT_UNBIND} ${getServiceIdentifierAsString(serviceIdentifier)}`, + ); } }); it('Should unbind a binding when requested', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } - interface Samurai { } + class Ninja {} @injectable() - class Samurai implements Samurai { } + class Samurai {} - const ninjaId = 'Ninja'; - const samuraiId = 'Samurai'; + const ninjaId: string = 'Ninja'; + const samuraiId: string = 'Samurai'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(samuraiId).to(Samurai); @@ -172,25 +173,19 @@ describe('Container', () => { container.unbind(ninjaId); map = getBindingDictionary(container).getMap(); expect(map.size).equal(1); - }); it('Should be able unbound all dependencies', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } - - interface Samurai { } + class Ninja {} @injectable() - class Samurai implements Samurai { } + class Samurai {} - const ninjaId = 'Ninja'; - const samuraiId = 'Samurai'; + const ninjaId: string = 'Ninja'; + const samuraiId: string = 'Samurai'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(samuraiId).to(Samurai); @@ -203,99 +198,100 @@ describe('Container', () => { container.unbindAll(); map = getBindingDictionary(container).getMap(); expect(map.size).equal(0); - }); it('Should NOT be able to get unregistered services', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } - const ninjaId = 'Ninja'; + class Ninja {} + const ninjaId: string = 'Ninja'; - const container = new Container(); - const throwFunction = () => { container.get(ninjaId); }; + const container: Container = new Container(); + const throwFunction: () => void = () => { + container.get(ninjaId); + }; expect(throwFunction).to.throw(`${ERROR_MSGS.NOT_REGISTERED} ${ninjaId}`); }); it('Should NOT be able to get ambiguous match', () => { - - interface Warrior { } + type Warrior = unknown; @injectable() - class Ninja implements Warrior { } + class Ninja {} @injectable() - class Samurai implements Warrior { } + class Samurai {} - const warriorId = 'Warrior'; + const warriorId: string = 'Warrior'; - const container = new Container(); + const container: Container = new Container(); container.bind(warriorId).to(Ninja); container.bind(warriorId).to(Samurai); const dictionary: Dictionary = getBindingDictionary(container).getMap(); expect(dictionary.size).equal(1); - dictionary.forEach((value, key) => { - expect(key).equal(warriorId); - expect(value.length).equal(2); - }); - - const throwFunction = () => { container.get(warriorId); }; - expect(throwFunction).to.throw(`${ERROR_MSGS.AMBIGUOUS_MATCH} ${warriorId}`); - + dictionary.forEach( + ( + value: interfaces.Binding[], + key: interfaces.ServiceIdentifier, + ) => { + expect(key).equal(warriorId); + + expect(value.length).equal(2); + }, + ); + + const throwFunction: () => void = () => { + container.get(warriorId); + }; + expect(throwFunction).to.throw( + `${ERROR_MSGS.AMBIGUOUS_MATCH} ${warriorId}`, + ); }); it('Should NOT be able to getAll of an unregistered services', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } - const ninjaId = 'Ninja'; + class Ninja {} + const ninjaId: string = 'Ninja'; - const container = new Container(); - const throwFunction = () => { container.getAll(ninjaId); }; + const container: Container = new Container(); + const throwFunction: () => void = () => { + container.getAll(ninjaId); + }; expect(throwFunction).to.throw(`${ERROR_MSGS.NOT_REGISTERED} ${ninjaId}`); - }); it('Should be able to get a string literal identifier as a string', () => { - const Katana = 'Katana'; - const KatanaStr = getServiceIdentifierAsString(Katana); - expect(KatanaStr).to.equal('Katana'); + const katana: string = 'Katana'; + const katanaStr: string = getServiceIdentifierAsString(katana); + expect(katanaStr).to.equal('Katana'); }); it('Should be able to get a symbol identifier as a string', () => { - const KatanaSymbol = Symbol.for('Katana'); - const KatanaStr = getServiceIdentifierAsString(KatanaSymbol); - expect(KatanaStr).to.equal('Symbol(Katana)'); + const katanaSymbol: symbol = Symbol.for('Katana'); + const katanaStr: string = getServiceIdentifierAsString(katanaSymbol); + expect(katanaStr).to.equal('Symbol(Katana)'); }); it('Should be able to get a class identifier as a string', () => { - class Katana { } - const KatanaStr = getServiceIdentifierAsString(Katana); - expect(KatanaStr).to.equal('Katana'); + class Katana {} + + const katanaStr: string = getServiceIdentifierAsString(Katana); + expect(katanaStr).to.equal('Katana'); }); it('Should be able to snapshot and restore container', () => { - - interface Warrior { - } - @injectable() - class Ninja implements Warrior { } + class Ninja {} @injectable() - class Samurai implements Warrior { } + class Samurai {} - const container = new Container(); - container.bind(Ninja).to(Ninja); - container.bind(Samurai).to(Samurai); + const container: Container = new Container(); + container.bind(Ninja).to(Ninja); + container.bind(Samurai).to(Samurai); expect(container.get(Samurai)).to.be.instanceOf(Samurai); expect(container.get(Ninja)).to.be.instanceOf(Ninja); @@ -309,7 +305,7 @@ describe('Container', () => { container.snapshot(); // snapshot container = v2 expect(() => container.get(Ninja)).to.throw(); - container.bind(Ninja).to(Ninja); + container.bind(Ninja).to(Ninja); expect(container.get(Samurai)).to.be.instanceOf(Samurai); expect(container.get(Ninja)).to.be.instanceOf(Ninja); @@ -321,12 +317,13 @@ describe('Container', () => { expect(container.get(Samurai)).to.be.instanceOf(Samurai); expect(container.get(Ninja)).to.be.instanceOf(Ninja); - expect(() => container.restore()).to.throw(ERROR_MSGS.NO_MORE_SNAPSHOTS_AVAILABLE); + expect(() => { + container.restore(); + }).to.throw(ERROR_MSGS.NO_MORE_SNAPSHOTS_AVAILABLE); }); it('Should maintain the activation state of a singleton when doing a snapshot of a container', () => { - - let timesCalled = 0; + let timesCalled: number = 0; @injectable() class Ninja { @@ -336,7 +333,7 @@ describe('Container', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind(Ninja).to(Ninja).inSingletonScope(); @@ -349,20 +346,20 @@ describe('Container', () => { }); it('Should save and restore the container activations and deactivations when snapshot and restore', () => { - const sid = 'sid'; - const container = new Container(); + const sid: string = 'sid'; + const container: Container = new Container(); container.bind(sid).toConstantValue('Value'); - let activated = false; - let deactivated = false + let activated: boolean = false; + let deactivated: boolean = false; container.snapshot(); - container.onActivation(sid, (c, i) => { + container.onActivation(sid, (_c: interfaces.Context, i: string) => { activated = true; return i; }); - container.onDeactivation(sid, i => { + container.onDeactivation(sid, (_i: unknown) => { deactivated = true; }); @@ -373,69 +370,75 @@ describe('Container', () => { expect(activated).to.equal(false); expect(deactivated).to.equal(false); - }) + }); it('Should save and restore the module activation store when snapshot and restore', () => { - const container = new Container(); - const clonedActivationStore = new ModuleActivationStore(); - const originalActivationStore = { + const container: Container = new Container(); + const clonedActivationStore: ModuleActivationStore = + new ModuleActivationStore(); + + const originalActivationStore: { + clone(): ModuleActivationStore; + } = { clone() { return clonedActivationStore; - } - } + }, + }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any const anyContainer: any = container; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access anyContainer._moduleActivationStore = originalActivationStore; container.snapshot(); - const snapshot = anyContainer._snapshots[0] as interfaces.ContainerSnapshot; - expect(snapshot.moduleActivationStore === clonedActivationStore).to.equal(true); + const snapshot: interfaces.ContainerSnapshot = + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + anyContainer._snapshots[0] as interfaces.ContainerSnapshot; + expect(snapshot.moduleActivationStore === clonedActivationStore).to.equal( + true, + ); container.restore(); - expect(anyContainer._moduleActivationStore === clonedActivationStore).to.equal(true); - }) + expect( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + anyContainer._moduleActivationStore === clonedActivationStore, + ).to.equal(true); + }); it('Should be able to check is there are bindings available for a given identifier', () => { - - interface Warrior { } - const warriorId = 'Warrior'; - const warriorSymbol = Symbol.for('Warrior'); + const warriorId: string = 'Warrior'; + const warriorSymbol: symbol = Symbol.for('Warrior'); @injectable() - class Ninja implements Warrior { } + class Ninja {} - const container = new Container(); - container.bind(Ninja).to(Ninja); - container.bind(warriorId).to(Ninja); - container.bind(warriorSymbol).to(Ninja); + const container: Container = new Container(); + container.bind(Ninja).to(Ninja); + container.bind(warriorId).to(Ninja); + container.bind(warriorSymbol).to(Ninja); expect(container.isBound(Ninja)).equal(true); expect(container.isBound(warriorId)).equal(true); expect(container.isBound(warriorSymbol)).equal(true); - interface Katana { } - const katanaId = 'Katana'; - const katanaSymbol = Symbol.for('Katana'); + const katanaId: string = 'Katana'; + const katanaSymbol: symbol = Symbol.for('Katana'); @injectable() - class Katana implements Katana { } + class Katana {} expect(container.isBound(Katana)).equal(false); expect(container.isBound(katanaId)).equal(false); expect(container.isBound(katanaSymbol)).equal(false); - }); it('Should be able to check is there are bindings available for a given identifier only in current container', () => { - - interface Warrior { } - @injectable() - class Ninja implements Warrior { } + class Ninja {} - const containerParent = new Container(); - const containerChild = new Container(); + const containerParent: Container = new Container(); + const containerChild: Container = new Container(); containerChild.parent = containerParent; - containerParent.bind(Ninja).to(Ninja); + containerParent.bind(Ninja).to(Ninja); expect(containerParent.isBound(Ninja)).to.eql(true); expect(containerParent.isCurrentBound(Ninja)).to.eql(true); @@ -444,115 +447,140 @@ describe('Container', () => { }); it('Should be able to get services from parent container', () => { - const weaponIdentifier = 'Weapon'; + const weaponIdentifier: string = 'Weapon'; @injectable() - class Katana { } + class Katana {} - const container = new Container(); + const container: Container = new Container(); container.bind(weaponIdentifier).to(Katana); - const childContainer = new Container(); + const childContainer: Container = new Container(); childContainer.parent = container; - const secondChildContainer = new Container(); + const secondChildContainer: Container = new Container(); secondChildContainer.parent = childContainer; expect(secondChildContainer.get(weaponIdentifier)).to.be.instanceOf(Katana); }); it('Should be able to check if services are bound from parent container', () => { - const weaponIdentifier = 'Weapon'; + const weaponIdentifier: string = 'Weapon'; @injectable() - class Katana { } + class Katana {} - const container = new Container(); + const container: Container = new Container(); container.bind(weaponIdentifier).to(Katana); - const childContainer = new Container(); + const childContainer: Container = new Container(); childContainer.parent = container; - const secondChildContainer = new Container(); + const secondChildContainer: Container = new Container(); secondChildContainer.parent = childContainer; expect(secondChildContainer.isBound(weaponIdentifier)).to.be.equal(true); }); it('Should prioritize requested container to resolve a service identifier', () => { - const weaponIdentifier = 'Weapon'; + const weaponIdentifier: string = 'Weapon'; @injectable() - class Katana { } + class Katana {} @injectable() - class DivineRapier { } + class DivineRapier {} - const container = new Container(); + const container: Container = new Container(); container.bind(weaponIdentifier).to(Katana); - const childContainer = new Container(); + const childContainer: Container = new Container(); childContainer.parent = container; - const secondChildContainer = new Container(); + const secondChildContainer: Container = new Container(); secondChildContainer.parent = childContainer; secondChildContainer.bind(weaponIdentifier).to(DivineRapier); - expect(secondChildContainer.get(weaponIdentifier)).to.be.instanceOf(DivineRapier); + expect(secondChildContainer.get(weaponIdentifier)).to.be.instanceOf( + DivineRapier, + ); }); it('Should be able to resolve named multi-injection', () => { - interface Intl { hello?: string; goodbye?: string; } - const container = new Container(); - container.bind('Intl').toConstantValue({ hello: 'bonjour' }).whenTargetNamed('fr'); - container.bind('Intl').toConstantValue({ goodbye: 'au revoir' }).whenTargetNamed('fr'); - container.bind('Intl').toConstantValue({ hello: 'hola' }).whenTargetNamed('es'); - container.bind('Intl').toConstantValue({ goodbye: 'adios' }).whenTargetNamed('es'); + const container: Container = new Container(); + container + .bind('Intl') + .toConstantValue({ hello: 'bonjour' }) + .whenTargetNamed('fr'); + container + .bind('Intl') + .toConstantValue({ goodbye: 'au revoir' }) + .whenTargetNamed('fr'); + container + .bind('Intl') + .toConstantValue({ hello: 'hola' }) + .whenTargetNamed('es'); + container + .bind('Intl') + .toConstantValue({ goodbye: 'adios' }) + .whenTargetNamed('es'); + + const fr: Intl[] = container.getAllNamed('Intl', 'fr'); - const fr = container.getAllNamed('Intl', 'fr'); expect(fr.length).to.equal(2); expect(fr[0]?.hello).to.equal('bonjour'); expect(fr[1]?.goodbye).to.equal('au revoir'); - const es = container.getAllNamed('Intl', 'es'); + const es: Intl[] = container.getAllNamed('Intl', 'es'); + expect(es.length).to.equal(2); expect(es[0]?.hello).to.equal('hola'); expect(es[1]?.goodbye).to.equal('adios'); - }); it('Should be able to resolve tagged multi-injection', () => { - interface Intl { hello?: string; goodbye?: string; } - const container = new Container(); - container.bind('Intl').toConstantValue({ hello: 'bonjour' }).whenTargetTagged('lang', 'fr'); - container.bind('Intl').toConstantValue({ goodbye: 'au revoir' }).whenTargetTagged('lang', 'fr'); - container.bind('Intl').toConstantValue({ hello: 'hola' }).whenTargetTagged('lang', 'es'); - container.bind('Intl').toConstantValue({ goodbye: 'adios' }).whenTargetTagged('lang', 'es'); + const container: Container = new Container(); + container + .bind('Intl') + .toConstantValue({ hello: 'bonjour' }) + .whenTargetTagged('lang', 'fr'); + container + .bind('Intl') + .toConstantValue({ goodbye: 'au revoir' }) + .whenTargetTagged('lang', 'fr'); + container + .bind('Intl') + .toConstantValue({ hello: 'hola' }) + .whenTargetTagged('lang', 'es'); + container + .bind('Intl') + .toConstantValue({ goodbye: 'adios' }) + .whenTargetTagged('lang', 'es'); + + const fr: Intl[] = container.getAllTagged('Intl', 'lang', 'fr'); - const fr = container.getAllTagged('Intl', 'lang', 'fr'); expect(fr.length).to.equal(2); expect(fr[0]?.hello).to.equal('bonjour'); expect(fr[1]?.goodbye).to.equal('au revoir'); - const es = container.getAllTagged('Intl', 'lang', 'es'); + const es: Intl[] = container.getAllTagged('Intl', 'lang', 'es'); + expect(es.length).to.equal(2); expect(es[0]?.hello).to.equal('hola'); expect(es[1]?.goodbye).to.equal('adios'); - }); it('Should be able configure the default scope at a global level', () => { - interface Warrior { health: number; takeHit(damage: number): void; @@ -561,7 +589,7 @@ describe('Container', () => { @injectable() class Ninja implements Warrior { public health: number; - public constructor() { + constructor() { this.health = 100; } public takeHit(damage: number) { @@ -569,62 +597,81 @@ describe('Container', () => { } } + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: 'Warrior' + Warrior: 'Warrior', }; - const container1 = new Container(); + const container1: Container = new Container(); container1.bind(TYPES.Warrior).to(Ninja); - const transientNinja1 = container1.get(TYPES.Warrior); + const transientNinja1: Warrior = container1.get(TYPES.Warrior); + expect(transientNinja1.health).to.equal(100); + transientNinja1.takeHit(10); + expect(transientNinja1.health).to.equal(90); - const transientNinja2 = container1.get(TYPES.Warrior); + const transientNinja2: Warrior = container1.get(TYPES.Warrior); + expect(transientNinja2.health).to.equal(100); + transientNinja2.takeHit(10); + expect(transientNinja2.health).to.equal(90); - const container2 = new Container({ defaultScope: BindingScopeEnum.Singleton }); + const container2: Container = new Container({ + defaultScope: BindingScopeEnum.Singleton, + }); container2.bind(TYPES.Warrior).to(Ninja); - const singletonNinja1 = container2.get(TYPES.Warrior); + const singletonNinja1: Warrior = container2.get(TYPES.Warrior); + expect(singletonNinja1.health).to.equal(100); + singletonNinja1.takeHit(10); + expect(singletonNinja1.health).to.equal(90); - const singletonNinja2 = container2.get(TYPES.Warrior); + const singletonNinja2: Warrior = container2.get(TYPES.Warrior); + expect(singletonNinja2.health).to.equal(90); + singletonNinja2.takeHit(10); - expect(singletonNinja2.health).to.equal(80); + expect(singletonNinja2.health).to.equal(80); }); it('Should default binding scope to Transient if no default scope on options', () => { - const container = new Container(); + const container: Container = new Container(); container.options.defaultScope = undefined; const expectedScope: interfaces.BindingScope = 'Transient'; - expect((container.bind('SID') as unknown as { _binding: { scope: interfaces.BindingScope } })._binding.scope).to.equal(expectedScope); + expect( + ( + container.bind('SID') as unknown as { + _binding: { scope: interfaces.BindingScope }; + } + )._binding.scope, + ).to.equal(expectedScope); }); it('Should be able to configure automatic binding for @injectable() decorated classes', () => { - @injectable() - class Katana { } + class Katana {} @injectable() - class Shuriken { } + class Shuriken {} @injectable() class Ninja { - public constructor(public weapon: Katana) { } + constructor(public weapon: Katana) {} } - class Samurai { } + class Samurai {} - const container1 = new Container({ autoBindInjectable: true }); - const katana1 = container1.get(Katana); - const ninja1 = container1.get(Ninja); + const container1: Container = new Container({ autoBindInjectable: true }); + const katana1: Katana = container1.get(Katana); + const ninja1: Ninja = container1.get(Ninja); expect(katana1).to.be.an.instanceof(Katana); expect(katana1).to.not.equal(container1.get(Katana)); expect(ninja1).to.be.an.instanceof(Ninja); @@ -633,9 +680,13 @@ describe('Container', () => { expect(ninja1.weapon).to.not.equal(container1.get(Ninja).weapon); expect(ninja1.weapon).to.not.equal(katana1); - const container2 = new Container({ defaultScope: BindingScopeEnum.Singleton, autoBindInjectable: true }); - const katana2 = container2.get(Katana); - const ninja2 = container2.get(Ninja); + const container2: Container = new Container({ + autoBindInjectable: true, + defaultScope: BindingScopeEnum.Singleton, + }); + const katana2: Katana = container2.get(Katana); + const ninja2: Ninja = container2.get(Ninja); + expect(katana2).to.be.an.instanceof(Katana); expect(katana2).to.equal(container2.get(Katana)); expect(ninja2).to.be.an.instanceof(Ninja); @@ -644,10 +695,11 @@ describe('Container', () => { expect(ninja2.weapon).to.equal(container2.get(Ninja).weapon); expect(ninja2.weapon).to.equal(katana2); - const container3 = new Container({ autoBindInjectable: true }); + const container3: Container = new Container({ autoBindInjectable: true }); container3.bind(Katana).toSelf().inSingletonScope(); - const katana3 = container3.get(Katana); - const ninja3 = container3.get(Ninja); + const katana3: Katana = container3.get(Katana); + const ninja3: Ninja = container3.get(Ninja); + expect(katana3).to.be.an.instanceof(Katana); expect(katana3).to.equal(container3.get(Katana)); expect(ninja3).to.be.an.instanceof(Ninja); @@ -656,10 +708,12 @@ describe('Container', () => { expect(ninja3.weapon).to.equal(container3.get(Ninja).weapon); expect(ninja3.weapon).to.equal(katana3); - const container4 = new Container({ autoBindInjectable: true }); + const container4: Container = new Container({ autoBindInjectable: true }); container4.bind(Katana).to(Shuriken); - const katana4 = container4.get(Katana); - const ninja4 = container4.get(Ninja); + + const katana4: Katana = container4.get(Katana); + const ninja4: Ninja = container4.get(Ninja); + expect(katana4).to.be.an.instanceof(Shuriken); expect(katana4).to.not.equal(container4.get(Katana)); expect(ninja4).to.be.an.instanceof(Ninja); @@ -668,145 +722,201 @@ describe('Container', () => { expect(ninja4.weapon).to.not.equal(container4.get(Ninja).weapon); expect(ninja4.weapon).to.not.equal(katana4); - const container5 = new Container({ autoBindInjectable: true }); + const container5: Container = new Container({ autoBindInjectable: true }); expect(() => container5.get(Samurai)).to.throw(ERROR_MSGS.NOT_REGISTERED); - }); it('Should be throw an exception if incorrect options is provided', () => { + const invalidOptions1: () => number = () => 0; + const wrong1: () => Container = () => + new Container(invalidOptions1 as unknown as interfaces.ContainerOptions); + expect(wrong1).to.throw(ERROR_MSGS.CONTAINER_OPTIONS_MUST_BE_AN_OBJECT); - const invalidOptions1 = () => 0; - const wrong1 = () => new Container(invalidOptions1 as unknown as interfaces.ContainerOptions); - expect(wrong1).to.throw(`${ERROR_MSGS.CONTAINER_OPTIONS_MUST_BE_AN_OBJECT}`); + const invalidOptions2: interfaces.ContainerOptions = { + autoBindInjectable: 'wrongValue' as unknown as boolean, + }; - const invalidOptions2 = { autoBindInjectable: 'wrongValue' }; - const wrong2 = () => new Container(invalidOptions2 as unknown as interfaces.ContainerOptions); - expect(wrong2).to.throw(`${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE}`); + const wrong2: () => Container = () => + new Container(invalidOptions2 as unknown as interfaces.ContainerOptions); - const invalidOptions3 = { defaultScope: 'wrongValue' }; - const wrong3 = () => new Container(invalidOptions3 as unknown as interfaces.ContainerOptions); - expect(wrong3).to.throw(`${ERROR_MSGS.CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE}`); + expect(wrong2).to.throw( + ERROR_MSGS.CONTAINER_OPTIONS_INVALID_AUTO_BIND_INJECTABLE, + ); + const invalidOptions3: interfaces.ContainerOptions = { + defaultScope: 'wrongValue' as unknown as interfaces.BindingScope, + }; + + const wrong3: () => Container = () => + new Container(invalidOptions3 as unknown as interfaces.ContainerOptions); + expect(wrong3).to.throw(ERROR_MSGS.CONTAINER_OPTIONS_INVALID_DEFAULT_SCOPE); }); it('Should be able to merge two containers', () => { - @injectable() class Ninja { - public name = 'Ninja'; + public name: string = 'Ninja'; } @injectable() class Shuriken { - public name = 'Shuriken'; + public name: string = 'Shuriken'; } + // eslint-disable-next-line @typescript-eslint/typedef const CHINA_EXPANSION_TYPES = { Ninja: 'Ninja', - Shuriken: 'Shuriken' + Shuriken: 'Shuriken', }; - const chinaExpansionContainer = new Container(); + const chinaExpansionContainer: Container = new Container(); chinaExpansionContainer.bind(CHINA_EXPANSION_TYPES.Ninja).to(Ninja); - chinaExpansionContainer.bind(CHINA_EXPANSION_TYPES.Shuriken).to(Shuriken); + chinaExpansionContainer + .bind(CHINA_EXPANSION_TYPES.Shuriken) + .to(Shuriken); @injectable() class Samurai { - public name = 'Samurai'; + public name: string = 'Samurai'; } @injectable() class Katana { - public name = 'Katana'; + public name: string = 'Katana'; } + // eslint-disable-next-line @typescript-eslint/typedef const JAPAN_EXPANSION_TYPES = { Katana: 'Katana', - Samurai: 'Samurai' + Samurai: 'Samurai', }; - const japanExpansionContainer = new Container(); - japanExpansionContainer.bind(JAPAN_EXPANSION_TYPES.Samurai).to(Samurai); - japanExpansionContainer.bind(JAPAN_EXPANSION_TYPES.Katana).to(Katana); - - const gameContainer = Container.merge(chinaExpansionContainer, japanExpansionContainer); - expect(gameContainer.get(CHINA_EXPANSION_TYPES.Ninja).name).to.equal('Ninja'); - expect(gameContainer.get(CHINA_EXPANSION_TYPES.Shuriken).name).to.equal('Shuriken'); - expect(gameContainer.get(JAPAN_EXPANSION_TYPES.Samurai).name).to.equal('Samurai'); - expect(gameContainer.get(JAPAN_EXPANSION_TYPES.Katana).name).to.equal('Katana'); - + const japanExpansionContainer: Container = new Container(); + japanExpansionContainer + .bind(JAPAN_EXPANSION_TYPES.Samurai) + .to(Samurai); + japanExpansionContainer + .bind(JAPAN_EXPANSION_TYPES.Katana) + .to(Katana); + + const gameContainer: interfaces.Container = Container.merge( + chinaExpansionContainer, + japanExpansionContainer, + ); + + expect(gameContainer.get(CHINA_EXPANSION_TYPES.Ninja).name).to.equal( + 'Ninja', + ); + expect( + gameContainer.get(CHINA_EXPANSION_TYPES.Shuriken).name, + ).to.equal('Shuriken'); + expect( + gameContainer.get(JAPAN_EXPANSION_TYPES.Samurai).name, + ).to.equal('Samurai'); + expect( + gameContainer.get(JAPAN_EXPANSION_TYPES.Katana).name, + ).to.equal('Katana'); }); it('Should be able to merge multiple containers', () => { @injectable() class Ninja { - public name = 'Ninja'; + public name: string = 'Ninja'; } @injectable() class Shuriken { - public name = 'Shuriken'; + public name: string = 'Shuriken'; } + // eslint-disable-next-line @typescript-eslint/typedef const CHINA_EXPANSION_TYPES = { Ninja: 'Ninja', - Shuriken: 'Shuriken' + Shuriken: 'Shuriken', }; - const chinaExpansionContainer = new Container(); + const chinaExpansionContainer: Container = new Container(); chinaExpansionContainer.bind(CHINA_EXPANSION_TYPES.Ninja).to(Ninja); - chinaExpansionContainer.bind(CHINA_EXPANSION_TYPES.Shuriken).to(Shuriken); + chinaExpansionContainer + .bind(CHINA_EXPANSION_TYPES.Shuriken) + .to(Shuriken); @injectable() class Samurai { - public name = 'Samurai'; + public name: string = 'Samurai'; } @injectable() class Katana { - public name = 'Katana'; + public name: string = 'Katana'; } + // eslint-disable-next-line @typescript-eslint/typedef const JAPAN_EXPANSION_TYPES = { Katana: 'Katana', - Samurai: 'Samurai' + Samurai: 'Samurai', }; - const japanExpansionContainer = new Container(); - japanExpansionContainer.bind(JAPAN_EXPANSION_TYPES.Samurai).to(Samurai); - japanExpansionContainer.bind(JAPAN_EXPANSION_TYPES.Katana).to(Katana); + const japanExpansionContainer: Container = new Container(); + japanExpansionContainer + .bind(JAPAN_EXPANSION_TYPES.Samurai) + .to(Samurai); + japanExpansionContainer + .bind(JAPAN_EXPANSION_TYPES.Katana) + .to(Katana); @injectable() class Sheriff { - public name = 'Sheriff'; + public name: string = 'Sheriff'; } @injectable() class Revolver { - public name = 'Revolver'; + public name: string = 'Revolver'; } + // eslint-disable-next-line @typescript-eslint/typedef const USA_EXPANSION_TYPES = { Revolver: 'Revolver', - Sheriff: 'Sheriff' + Sheriff: 'Sheriff', }; - const usaExpansionContainer = new Container(); - usaExpansionContainer.bind(USA_EXPANSION_TYPES.Sheriff).to(Sheriff); - usaExpansionContainer.bind(USA_EXPANSION_TYPES.Revolver).to(Revolver); - - const gameContainer = Container.merge(chinaExpansionContainer, japanExpansionContainer, usaExpansionContainer); - expect(gameContainer.get(CHINA_EXPANSION_TYPES.Ninja).name).to.equal('Ninja'); - expect(gameContainer.get(CHINA_EXPANSION_TYPES.Shuriken).name).to.equal('Shuriken'); - expect(gameContainer.get(JAPAN_EXPANSION_TYPES.Samurai).name).to.equal('Samurai'); - expect(gameContainer.get(JAPAN_EXPANSION_TYPES.Katana).name).to.equal('Katana'); - expect(gameContainer.get(USA_EXPANSION_TYPES.Sheriff).name).to.equal('Sheriff'); - expect(gameContainer.get(USA_EXPANSION_TYPES.Revolver).name).to.equal('Revolver'); + const usaExpansionContainer: Container = new Container(); + usaExpansionContainer + .bind(USA_EXPANSION_TYPES.Sheriff) + .to(Sheriff); + usaExpansionContainer + .bind(USA_EXPANSION_TYPES.Revolver) + .to(Revolver); + + const gameContainer: interfaces.Container = Container.merge( + chinaExpansionContainer, + japanExpansionContainer, + usaExpansionContainer, + ); + expect(gameContainer.get(CHINA_EXPANSION_TYPES.Ninja).name).to.equal( + 'Ninja', + ); + expect( + gameContainer.get(CHINA_EXPANSION_TYPES.Shuriken).name, + ).to.equal('Shuriken'); + expect( + gameContainer.get(JAPAN_EXPANSION_TYPES.Samurai).name, + ).to.equal('Samurai'); + expect( + gameContainer.get(JAPAN_EXPANSION_TYPES.Katana).name, + ).to.equal('Katana'); + expect( + gameContainer.get(USA_EXPANSION_TYPES.Sheriff).name, + ).to.equal('Sheriff'); + expect( + gameContainer.get(USA_EXPANSION_TYPES.Revolver).name, + ).to.equal('Revolver'); }); it('Should be able create a child containers', () => { - const parent = new Container(); - const child = parent.createChild(); + const parent: Container = new Container(); + const child: Container = parent.createChild(); if (child.parent === null) { throw new Error('Parent should not be null'); } @@ -815,44 +925,43 @@ describe('Container', () => { it('Should inherit parent container options', () => { @injectable() - class Warrior { } + class Warrior {} - const parent = new Container({ - defaultScope: BindingScopeEnum.Singleton + const parent: Container = new Container({ + defaultScope: BindingScopeEnum.Singleton, }); - const child = parent.createChild(); + const child: Container = parent.createChild(); child.bind(Warrior).toSelf(); - const singletonWarrior1 = child.get(Warrior); - const singletonWarrior2 = child.get(Warrior); + const singletonWarrior1: Warrior = child.get(Warrior); + const singletonWarrior2: Warrior = child.get(Warrior); expect(singletonWarrior1).to.equal(singletonWarrior2); }); it('Should be able to override options to child containers', () => { @injectable() - class Warrior { } + class Warrior {} - const parent = new Container({ - defaultScope: BindingScopeEnum.Request + const parent: Container = new Container({ + defaultScope: BindingScopeEnum.Request, }); - const child = parent.createChild({ - defaultScope: BindingScopeEnum.Singleton + const child: Container = parent.createChild({ + defaultScope: BindingScopeEnum.Singleton, }); child.bind(Warrior).toSelf(); - const singletonWarrior1 = child.get(Warrior); - const singletonWarrior2 = child.get(Warrior); + const singletonWarrior1: Warrior = child.get(Warrior); + const singletonWarrior2: Warrior = child.get(Warrior); expect(singletonWarrior1).to.equal(singletonWarrior2); }); it('Should be able check if a named binding is bound', () => { - - const zero = 'Zero'; - const invalidDivisor = 'InvalidDivisor'; - const validDivisor = 'ValidDivisor'; - const container = new Container(); + const zero: string = 'Zero'; + const invalidDivisor: string = 'InvalidDivisor'; + const validDivisor: string = 'ValidDivisor'; + const container: Container = new Container(); expect(container.isBound(zero)).to.equal(false); container.bind(zero).toConstantValue(0); @@ -860,70 +969,98 @@ describe('Container', () => { container.unbindAll(); expect(container.isBound(zero)).to.equal(false); - container.bind(zero).toConstantValue(0).whenTargetNamed(invalidDivisor); + container + .bind(zero) + .toConstantValue(0) + .whenTargetNamed(invalidDivisor); expect(container.isBoundNamed(zero, invalidDivisor)).to.equal(true); expect(container.isBoundNamed(zero, validDivisor)).to.equal(false); - container.bind(zero).toConstantValue(1).whenTargetNamed(validDivisor); + container + .bind(zero) + .toConstantValue(1) + .whenTargetNamed(validDivisor); expect(container.isBoundNamed(zero, invalidDivisor)).to.equal(true); expect(container.isBoundNamed(zero, validDivisor)).to.equal(true); - }); it('Should be able to check if a named binding is bound from parent container', () => { - - const zero = 'Zero'; - const invalidDivisor = 'InvalidDivisor'; - const validDivisor = 'ValidDivisor'; - const container = new Container(); - const childContainer = container.createChild(); - const secondChildContainer = childContainer.createChild(); - - container.bind(zero).toConstantValue(0).whenTargetNamed(invalidDivisor); - expect(secondChildContainer.isBoundNamed(zero, invalidDivisor)).to.equal(true); - expect(secondChildContainer.isBoundNamed(zero, validDivisor)).to.equal(false); - - container.bind(zero).toConstantValue(1).whenTargetNamed(validDivisor); - expect(secondChildContainer.isBoundNamed(zero, invalidDivisor)).to.equal(true); - expect(secondChildContainer.isBoundNamed(zero, validDivisor)).to.equal(true); - + const zero: string = 'Zero'; + const invalidDivisor: string = 'InvalidDivisor'; + const validDivisor: string = 'ValidDivisor'; + const container: Container = new Container(); + const childContainer: Container = container.createChild(); + const secondChildContainer: Container = childContainer.createChild(); + + container + .bind(zero) + .toConstantValue(0) + .whenTargetNamed(invalidDivisor); + expect(secondChildContainer.isBoundNamed(zero, invalidDivisor)).to.equal( + true, + ); + expect(secondChildContainer.isBoundNamed(zero, validDivisor)).to.equal( + false, + ); + + container + .bind(zero) + .toConstantValue(1) + .whenTargetNamed(validDivisor); + expect(secondChildContainer.isBoundNamed(zero, invalidDivisor)).to.equal( + true, + ); + expect(secondChildContainer.isBoundNamed(zero, validDivisor)).to.equal( + true, + ); }); it('Should be able to get a tagged binding', () => { - - const zero = 'Zero'; - const isValidDivisor = 'IsValidDivisor'; - const container = new Container(); - - container.bind(zero).toConstantValue(0).whenTargetTagged(isValidDivisor, false); + const zero: string = 'Zero'; + const isValidDivisor: string = 'IsValidDivisor'; + const container: Container = new Container(); + + container + .bind(zero) + .toConstantValue(0) + .whenTargetTagged(isValidDivisor, false); expect(container.getTagged(zero, isValidDivisor, false)).to.equal(0); - container.bind(zero).toConstantValue(1).whenTargetTagged(isValidDivisor, true); + container + .bind(zero) + .toConstantValue(1) + .whenTargetTagged(isValidDivisor, true); expect(container.getTagged(zero, isValidDivisor, false)).to.equal(0); expect(container.getTagged(zero, isValidDivisor, true)).to.equal(1); - }); it('Should be able to get a tagged binding from parent container', () => { - - const zero = 'Zero'; - const isValidDivisor = 'IsValidDivisor'; - const container = new Container(); - const childContainer = container.createChild(); - const secondChildContainer = childContainer.createChild(); - - container.bind(zero).toConstantValue(0).whenTargetTagged(isValidDivisor, false); - container.bind(zero).toConstantValue(1).whenTargetTagged(isValidDivisor, true); - expect(secondChildContainer.getTagged(zero, isValidDivisor, false)).to.equal(0); - expect(secondChildContainer.getTagged(zero, isValidDivisor, true)).to.equal(1); - + const zero: string = 'Zero'; + const isValidDivisor: string = 'IsValidDivisor'; + const container: Container = new Container(); + const childContainer: Container = container.createChild(); + const secondChildContainer: Container = childContainer.createChild(); + + container + .bind(zero) + .toConstantValue(0) + .whenTargetTagged(isValidDivisor, false); + container + .bind(zero) + .toConstantValue(1) + .whenTargetTagged(isValidDivisor, true); + expect( + secondChildContainer.getTagged(zero, isValidDivisor, false), + ).to.equal(0); + expect(secondChildContainer.getTagged(zero, isValidDivisor, true)).to.equal( + 1, + ); }); it('Should be able check if a tagged binding is bound', () => { - - const zero = 'Zero'; - const isValidDivisor = 'IsValidDivisor'; - const container = new Container(); + const zero: string = 'Zero'; + const isValidDivisor: string = 'IsValidDivisor'; + const container: Container = new Container(); expect(container.isBound(zero)).to.equal(false); container.bind(zero).toConstantValue(0); @@ -931,100 +1068,133 @@ describe('Container', () => { container.unbindAll(); expect(container.isBound(zero)).to.equal(false); - container.bind(zero).toConstantValue(0).whenTargetTagged(isValidDivisor, false); + container + .bind(zero) + .toConstantValue(0) + .whenTargetTagged(isValidDivisor, false); expect(container.isBoundTagged(zero, isValidDivisor, false)).to.equal(true); expect(container.isBoundTagged(zero, isValidDivisor, true)).to.equal(false); - container.bind(zero).toConstantValue(1).whenTargetTagged(isValidDivisor, true); + container + .bind(zero) + .toConstantValue(1) + .whenTargetTagged(isValidDivisor, true); expect(container.isBoundTagged(zero, isValidDivisor, false)).to.equal(true); expect(container.isBoundTagged(zero, isValidDivisor, true)).to.equal(true); - }); it('Should be able to check if a tagged binding is bound from parent container', () => { - - const zero = 'Zero'; - const isValidDivisor = 'IsValidDivisor'; - const container = new Container(); - const childContainer = container.createChild(); - const secondChildContainer = childContainer.createChild(); - - container.bind(zero).toConstantValue(0).whenTargetTagged(isValidDivisor, false); - expect(secondChildContainer.isBoundTagged(zero, isValidDivisor, false)).to.equal(true); - expect(secondChildContainer.isBoundTagged(zero, isValidDivisor, true)).to.equal(false); - - container.bind(zero).toConstantValue(1).whenTargetTagged(isValidDivisor, true); - expect(secondChildContainer.isBoundTagged(zero, isValidDivisor, false)).to.equal(true); - expect(secondChildContainer.isBoundTagged(zero, isValidDivisor, true)).to.equal(true); - + const zero: string = 'Zero'; + const isValidDivisor: string = 'IsValidDivisor'; + const container: Container = new Container(); + const childContainer: Container = container.createChild(); + const secondChildContainer: Container = childContainer.createChild(); + + container + .bind(zero) + .toConstantValue(0) + .whenTargetTagged(isValidDivisor, false); + expect( + secondChildContainer.isBoundTagged(zero, isValidDivisor, false), + ).to.equal(true); + expect( + secondChildContainer.isBoundTagged(zero, isValidDivisor, true), + ).to.equal(false); + + container + .bind(zero) + .toConstantValue(1) + .whenTargetTagged(isValidDivisor, true); + expect( + secondChildContainer.isBoundTagged(zero, isValidDivisor, false), + ).to.equal(true); + expect( + secondChildContainer.isBoundTagged(zero, isValidDivisor, true), + ).to.equal(true); }); it('Should be able to override a binding using rebind', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - someType: 'someType' + someType: 'someType', }; - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.someType).toConstantValue(1); + container.bind(TYPES.someType).toConstantValue(2); - const values1 = container.getAll(TYPES.someType); + const values1: unknown[] = container.getAll(TYPES.someType); expect(values1[0]).to.eq(1); + expect(values1[1]).to.eq(2); container.rebind(TYPES.someType).toConstantValue(3); - const values2 = container.getAll(TYPES.someType); + const values2: unknown[] = container.getAll(TYPES.someType); + expect(values2[0]).to.eq(3); expect(values2[1]).to.eq(undefined); - }); it('Should be able to override a binding using rebindAsync', async () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - someType: 'someType' + someType: 'someType', }; - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.someType).toConstantValue(1); + container.bind(TYPES.someType).toConstantValue(2); - container.onDeactivation(TYPES.someType, () => Promise.resolve()) + container.onDeactivation(TYPES.someType, async () => Promise.resolve()); - const values1 = container.getAll(TYPES.someType); + const values1: unknown[] = container.getAll(TYPES.someType); expect(values1[0]).to.eq(1); + expect(values1[1]).to.eq(2); (await container.rebindAsync(TYPES.someType)).toConstantValue(3); - const values2 = container.getAll(TYPES.someType); + const values2: unknown[] = container.getAll(TYPES.someType); + expect(values2[0]).to.eq(3); expect(values2[1]).to.eq(undefined); - }); it('Should be able to resolve named multi-injection (async)', async () => { - interface Intl { hello?: string; goodbye?: string; } - const container = new Container(); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ hello: 'bonjour' })).whenTargetNamed('fr'); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ goodbye: 'au revoir' })).whenTargetNamed('fr'); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ hello: 'hola' })).whenTargetNamed('es'); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ goodbye: 'adios' })).whenTargetNamed('es'); + const container: Container = new Container(); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ hello: 'bonjour' })) + .whenTargetNamed('fr'); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ goodbye: 'au revoir' })) + .whenTargetNamed('fr'); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ hello: 'hola' })) + .whenTargetNamed('es'); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ goodbye: 'adios' })) + .whenTargetNamed('es'); + + const fr: Intl[] = await container.getAllNamedAsync('Intl', 'fr'); - const fr = await container.getAllNamedAsync('Intl', 'fr'); expect(fr.length).to.equal(2); expect(fr[0]?.hello).to.equal('bonjour'); expect(fr[1]?.goodbye).to.equal('au revoir'); - const es = await container.getAllNamedAsync('Intl', 'es'); + const es: Intl[] = await container.getAllNamedAsync('Intl', 'es'); + expect(es.length).to.equal(2); expect(es[0]?.hello).to.equal('hola'); expect(es[1]?.goodbye).to.equal('adios'); - }); it('Should be able to resolve named (async)', async () => { @@ -1033,143 +1203,196 @@ describe('Container', () => { goodbye?: string; } - const container = new Container(); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ hello: 'bonjour' })).whenTargetNamed('fr'); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ hello: 'hola' })).whenTargetNamed('es'); - - const fr = await container.getNamedAsync('Intl', 'fr'); + const container: Container = new Container(); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ hello: 'bonjour' })) + .whenTargetNamed('fr'); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ hello: 'hola' })) + .whenTargetNamed('es'); + + const fr: Intl = await container.getNamedAsync('Intl', 'fr'); expect(fr.hello).to.equal('bonjour'); - const es = await container.getNamedAsync('Intl', 'es'); + const es: Intl = await container.getNamedAsync('Intl', 'es'); expect(es.hello).to.equal('hola'); }); it('Should be able to resolve tagged multi-injection (async)', async () => { - interface Intl { hello?: string; goodbye?: string; } - const container = new Container(); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ hello: 'bonjour' })).whenTargetTagged('lang', 'fr'); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ goodbye: 'au revoir' })).whenTargetTagged('lang', 'fr'); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ hello: 'hola' })).whenTargetTagged('lang', 'es'); - container.bind('Intl').toDynamicValue(() => Promise.resolve({ goodbye: 'adios' })).whenTargetTagged('lang', 'es'); + const container: Container = new Container(); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ hello: 'bonjour' })) + .whenTargetTagged('lang', 'fr'); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ goodbye: 'au revoir' })) + .whenTargetTagged('lang', 'fr'); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ hello: 'hola' })) + .whenTargetTagged('lang', 'es'); + container + .bind('Intl') + .toDynamicValue(async () => Promise.resolve({ goodbye: 'adios' })) + .whenTargetTagged('lang', 'es'); + + const fr: Intl[] = await container.getAllTaggedAsync( + 'Intl', + 'lang', + 'fr', + ); - const fr = await container.getAllTaggedAsync('Intl', 'lang', 'fr'); expect(fr.length).to.equal(2); expect(fr[0]?.hello).to.equal('bonjour'); expect(fr[1]?.goodbye).to.equal('au revoir'); - const es = await container.getAllTaggedAsync('Intl', 'lang', 'es'); + const es: Intl[] = await container.getAllTaggedAsync( + 'Intl', + 'lang', + 'es', + ); + expect(es.length).to.equal(2); expect(es[0]?.hello).to.equal('hola'); expect(es[1]?.goodbye).to.equal('adios'); - }); it('Should be able to get a tagged binding (async)', async () => { - - const zero = 'Zero'; - const isValidDivisor = 'IsValidDivisor'; - const container = new Container(); - - container.bind(zero).toDynamicValue(() => Promise.resolve(0)).whenTargetTagged(isValidDivisor, false); - expect(await container.getTaggedAsync(zero, isValidDivisor, false)).to.equal(0); - - container.bind(zero).toDynamicValue(() => Promise.resolve(1)).whenTargetTagged(isValidDivisor, true); - expect(await container.getTaggedAsync(zero, isValidDivisor, false)).to.equal(0); - expect(await container.getTaggedAsync(zero, isValidDivisor, true)).to.equal(1); - + const zero: string = 'Zero'; + const isValidDivisor: string = 'IsValidDivisor'; + const container: Container = new Container(); + + container + .bind(zero) + .toDynamicValue(async () => Promise.resolve(0)) + .whenTargetTagged(isValidDivisor, false); + expect( + await container.getTaggedAsync(zero, isValidDivisor, false), + ).to.equal(0); + + container + .bind(zero) + .toDynamicValue(async () => Promise.resolve(1)) + .whenTargetTagged(isValidDivisor, true); + expect( + await container.getTaggedAsync(zero, isValidDivisor, false), + ).to.equal(0); + expect(await container.getTaggedAsync(zero, isValidDivisor, true)).to.equal( + 1, + ); }); it('should be able to get all the services binded (async)', async () => { - const serviceIdentifier = 'service-identifier'; + const serviceIdentifier: string = 'service-identifier'; - const container = new Container(); + const container: Container = new Container(); - const firstValueBinded = 'value-one'; - const secondValueBinded = 'value-two'; - const thirdValueBinded = 'value-three'; + const firstValueBinded: string = 'value-one'; + const secondValueBinded: string = 'value-two'; + const thirdValueBinded: string = 'value-three'; container.bind(serviceIdentifier).toConstantValue(firstValueBinded); container.bind(serviceIdentifier).toConstantValue(secondValueBinded); - container.bind(serviceIdentifier).toDynamicValue(_ => Promise.resolve(thirdValueBinded)); - const services = await container.getAllAsync(serviceIdentifier); - - expect(services).to.deep.eq([firstValueBinded, secondValueBinded, thirdValueBinded]); + container + .bind(serviceIdentifier) + .toDynamicValue(async (_: interfaces.Context) => + Promise.resolve(thirdValueBinded), + ); + const services: string[] = + await container.getAllAsync(serviceIdentifier); + + expect(services).to.deep.eq([ + firstValueBinded, + secondValueBinded, + thirdValueBinded, + ]); }); it('should throw an error if skipBaseClassChecks is not a boolean', () => { - expect(() => - new Container({ - skipBaseClassChecks: 'Jolene, Jolene, Jolene, Jolene' as unknown as boolean - }) + expect( + () => + new Container({ + skipBaseClassChecks: + 'Jolene, Jolene, Jolene, Jolene' as unknown as boolean, + }), ).to.throw(ERROR_MSGS.CONTAINER_OPTIONS_INVALID_SKIP_BASE_CHECK); }); it('Should be able to inject when symbol property key ', () => { - const weaponProperty = Symbol(); - interface Weapon { } + const weaponProperty: unique symbol = Symbol(); + type Weapon = unknown; @injectable() - class Shuriken implements Weapon { } + class Shuriken {} @injectable() class Ninja { @inject('Weapon') - [weaponProperty]!: Weapon + public [weaponProperty]!: Weapon; } - const container = new Container(); + const container: Container = new Container(); container.bind('Weapon').to(Shuriken); - const myNinja = container.resolve(Ninja); - const weapon = myNinja[weaponProperty]; + const myNinja: Ninja = container.resolve(Ninja); + const weapon: Weapon = myNinja[weaponProperty]; expect(weapon).to.be.instanceOf(Shuriken); }); it('Should be possible to constrain to a symbol description', () => { - const throwableWeapon = Symbol('throwable'); - interface Weapon { } + const throwableWeapon: unique symbol = Symbol('throwable'); + type Weapon = unknown; @injectable() - class Shuriken implements Weapon { } + class Shuriken {} @injectable() class Ninja { @inject('Weapon') - [throwableWeapon]!: Weapon + public [throwableWeapon]!: Weapon; } - const container = new Container(); - container.bind('Weapon').to(Shuriken).when(request => { - return request.target.name.equals('throwable'); - }) - const myNinja = container.resolve(Ninja); - const weapon = myNinja[throwableWeapon]; + const container: Container = new Container(); + container + .bind('Weapon') + .to(Shuriken) + .when((request: interfaces.Request) => { + return request.target.name.equals('throwable'); + }); + const myNinja: Ninja = container.resolve(Ninja); + const weapon: Weapon = myNinja[throwableWeapon]; expect(weapon).to.be.instanceOf(Shuriken); }); it('container resolve should come from the same container', () => { @injectable() - class CompositionRoot { } + class CompositionRoot {} class DerivedContainer extends Container { public planningForCompositionRoot(): void { // } } - const middleware: interfaces.Middleware = (next) => - (nextArgs) => { - const contextInterceptor = nextArgs.contextInterceptor; - nextArgs.contextInterceptor = context => { + const middleware: interfaces.Middleware = + (next: interfaces.Next) => (nextArgs: interfaces.NextArgs) => { + const contextInterceptor: ( + contexts: interfaces.Context, + ) => interfaces.Context = nextArgs.contextInterceptor; + nextArgs.contextInterceptor = (context: interfaces.Context) => { if (context.plan.rootRequest.serviceIdentifier === CompositionRoot) { - (context.container as DerivedContainer).planningForCompositionRoot(); + ( + context.container as DerivedContainer + ).planningForCompositionRoot(); } return contextInterceptor(context); - } - return next(nextArgs) - } + }; + return next(nextArgs); + }; - const myContainer = new DerivedContainer(); + const myContainer: DerivedContainer = new DerivedContainer(); myContainer.applyMiddleware(middleware); myContainer.resolve(CompositionRoot); - // tslint:disable-next-line: no-unused-expression + // eslint-disable-next-line @typescript-eslint/no-unused-expressions expect(() => myContainer.resolve(CompositionRoot)).not.to.throw; - }) - + }); }); diff --git a/test/container/container_module.test.ts b/test/container/container_module.test.ts index 7c297eca4..0db79168e 100644 --- a/test/container/container_module.test.ts +++ b/test/container/container_module.test.ts @@ -1,152 +1,191 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; + import { NOT_REGISTERED } from '../../src/constants/error_msgs'; import { Container } from '../../src/container/container'; -import { AsyncContainerModule, ContainerModule } from '../../src/container/container_module'; +import { + AsyncContainerModule, + ContainerModule, +} from '../../src/container/container_module'; import { interfaces } from '../../src/interfaces/interfaces'; describe('ContainerModule', () => { - it('Should be able to set the registry of a container module', () => { - const registry = (bind: interfaces.Bind) => { /* do nothing */ }; - const warriors = new ContainerModule(registry); + const registry: (bind: interfaces.Bind) => void = ( + _bind: interfaces.Bind, + ) => {}; + + const warriors: ContainerModule = new ContainerModule(registry); + expect(warriors.id).to.be.a('number'); expect(warriors.registry).eql(registry); }); it('Should be able to remove some bindings from within a container module', () => { - - const container = new Container(); + const container: Container = new Container(); container.bind('A').toConstantValue('1'); expect(container.get('A')).to.eql('1'); - const warriors = new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind) => { - expect(container.get('A')).to.eql('1'); - unbind('A'); - expect(() => { container.get('A'); }).to.throw(); - bind('A').toConstantValue('2'); - expect(container.get('A')).to.eql('2'); - bind('B').toConstantValue('3'); - expect(container.get('B')).to.eql('3'); - }); + const warriors: ContainerModule = new ContainerModule( + (bind: interfaces.Bind, unbind: interfaces.Unbind) => { + expect(container.get('A')).to.eql('1'); + unbind('A'); + expect(() => { + container.get('A'); + }).to.throw(); + bind('A').toConstantValue('2'); + expect(container.get('A')).to.eql('2'); + bind('B').toConstantValue('3'); + expect(container.get('B')).to.eql('3'); + }, + ); container.load(warriors); expect(container.get('A')).to.eql('2'); expect(container.get('B')).to.eql('3'); - }); it('Should be able to check for existence of bindings within a container module', () => { - - const container = new Container(); + const container: Container = new Container(); container.bind('A').toConstantValue('1'); expect(container.get('A')).to.eql('1'); - const warriors = new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind, isBound: interfaces.IsBound) => { - expect(container.get('A')).to.eql('1'); - expect(isBound('A')).to.eql(true); - unbind('A'); - expect(isBound('A')).to.eql(false); - }); + const warriors: ContainerModule = new ContainerModule( + ( + _bind: interfaces.Bind, + unbind: interfaces.Unbind, + isBound: interfaces.IsBound, + ) => { + expect(container.get('A')).to.eql('1'); + expect(isBound('A')).to.eql(true); + unbind('A'); + expect(isBound('A')).to.eql(false); + }, + ); container.load(warriors); - }); it('Should be able to override a binding using rebind within a container module', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - someType: 'someType' + someType: 'someType', }; - const container = new Container(); + const container: Container = new Container(); - const module1 = new ContainerModule( - ( - bind: interfaces.Bind, - unbind: interfaces.Unbind, - isBound: interfaces.IsBound - ) => { + const module1: ContainerModule = new ContainerModule( + (bind: interfaces.Bind) => { bind(TYPES.someType).toConstantValue(1); + bind(TYPES.someType).toConstantValue(2); - } + }, ); - const module2 = new ContainerModule( + const module2: ContainerModule = new ContainerModule( ( - bind: interfaces.Bind, - unbind: interfaces.Unbind, - isBound: interfaces.IsBound, - rebind: interfaces.Rebind + _bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + rebind: interfaces.Rebind, ) => { rebind(TYPES.someType).toConstantValue(3); - } + }, ); container.load(module1); - const values1 = container.getAll(TYPES.someType); + const values1: unknown[] = container.getAll(TYPES.someType); expect(values1[0]).to.eq(1); + expect(values1[1]).to.eq(2); container.load(module2); - const values2 = container.getAll(TYPES.someType); + const values2: unknown[] = container.getAll(TYPES.someType); + expect(values2[0]).to.eq(3); expect(values2[1]).to.eq(undefined); - }); it('Should be able use await async functions in container modules', async () => { + const container: Container = new Container(); + const someAsyncFactory: () => Promise = async () => + new Promise( + (res: (value: number | PromiseLike) => void) => + setTimeout(() => { + res(1); + }, 100), + ); + const A: unique symbol = Symbol.for('A'); + const B: unique symbol = Symbol.for('B'); + + const moduleOne: AsyncContainerModule = new AsyncContainerModule( + async (bind: interfaces.Bind) => { + const val: number = await someAsyncFactory(); + bind(A).toConstantValue(val); + }, + ); - const container = new Container(); - const someAsyncFactory = () => new Promise((res) => setTimeout(() => res(1), 100)); - const A = Symbol.for('A'); - const B = Symbol.for('B'); - - const moduleOne = new AsyncContainerModule(async (bind) => { - const val = await someAsyncFactory(); - bind(A).toConstantValue(val); - }); - - const moduleTwo = new AsyncContainerModule(async (bind) => { - bind(B).toConstantValue(2); - }); + const moduleTwo: AsyncContainerModule = new AsyncContainerModule( + async (bind: interfaces.Bind) => { + bind(B).toConstantValue(2); + }, + ); await container.loadAsync(moduleOne, moduleTwo); - const AIsBound = container.isBound(A); - expect(AIsBound).to.eq(true); - const a = container.get(A); + const aIsBound: boolean = container.isBound(A); + expect(aIsBound).to.eq(true); + const a: unknown = container.get(A); expect(a).to.eq(1); - }); it('Should be able to add an activation hook through a container module', () => { - - const container = new Container(); + const container: Container = new Container(); container.bind('A').toDynamicValue(() => '1'); expect(container.get('A')).to.eql('1'); - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation) => { - bind('B').toConstantValue('2').onActivation(() => 'C'); - onActivation('A', () => 'B'); - }); + const module: ContainerModule = new ContainerModule( + ( + bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + onActivation: interfaces.Container['onActivation'], + ) => { + bind('B') + .toConstantValue('2') + .onActivation(() => 'C'); + onActivation('A', () => 'B'); + }, + ); container.load(module); expect(container.get('A')).to.eql('B'); - expect(container.get('B')).to.eql('C') + expect(container.get('B')).to.eql('C'); }); it('Should be able to add a deactivation hook through a container module', () => { - const container = new Container(); + const container: Container = new Container(); container.bind('A').toConstantValue('1'); - let deact = false; - const warriors = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - onDeactivation('A', () => { - deact = true; - }); - }); + let deact: boolean = false; + const warriors: ContainerModule = new ContainerModule( + ( + _bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + _onActivation: interfaces.Container['onActivation'], + onDeactivation: interfaces.Container['onDeactivation'], + ) => { + onDeactivation('A', () => { + deact = true; + }); + }, + ); container.load(warriors); container.get('A'); @@ -156,16 +195,26 @@ describe('ContainerModule', () => { }); it('Should be able to add an async deactivation hook through a container module (async)', async () => { - const container = new Container(); + const container: Container = new Container(); container.bind('A').toConstantValue('1'); - let deact = false; + let deact: boolean = false; - const warriors = new ContainerModule((bind, unbind, isBound, rebind, unBindAsync, onActivation, onDeactivation) => { - onDeactivation('A', async () => { - deact = true; - }); - }); + const warriors: ContainerModule = new ContainerModule( + ( + _bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + _onActivation: interfaces.Container['onActivation'], + onDeactivation: interfaces.Container['onDeactivation'], + ) => { + onDeactivation('A', async () => { + deact = true; + }); + }, + ); container.load(warriors); container.get('A'); @@ -175,16 +224,27 @@ describe('ContainerModule', () => { }); it('Should be able to add multiple async deactivation hook through a container module (async)', async () => { + const onActivationHandlerSpy: sinon.SinonSpy<[], Promise> = sinon.spy< + () => Promise + >(async () => undefined); - const onActivationHandlerSpy = sinon.spy<() => Promise>(async () => undefined); - - const serviceIdentifier = 'destroyable'; - const container = new Container(); + const serviceIdentifier: string = 'destroyable'; + const container: Container = new Container(); - const containerModule = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - onDeactivation(serviceIdentifier, onActivationHandlerSpy); - onDeactivation(serviceIdentifier, onActivationHandlerSpy); - }); + const containerModule: ContainerModule = new ContainerModule( + ( + _bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + _onActivation: interfaces.Container['onActivation'], + onDeactivation: interfaces.Container['onDeactivation'], + ) => { + onDeactivation(serviceIdentifier, onActivationHandlerSpy); + onDeactivation(serviceIdentifier, onActivationHandlerSpy); + }, + ); container.bind(serviceIdentifier).toConstantValue(serviceIdentifier); @@ -198,14 +258,16 @@ describe('ContainerModule', () => { }); it('Should remove module bindings when unload', () => { - const sid = 'sid'; - const container = new Container(); + const sid: string = 'sid'; + const container: Container = new Container(); container.bind(sid).toConstantValue('Not module'); - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - bind(sid).toConstantValue('Module') - }); + const module: ContainerModule = new ContainerModule( + (bind: interfaces.Bind) => { + bind(sid).toConstantValue('Module'); + }, + ); container.load(module); - let values = container.getAll(sid); + let values: unknown[] = container.getAll(sid); expect(values).to.deep.equal(['Not module', 'Module']); container.unload(module); @@ -214,14 +276,30 @@ describe('ContainerModule', () => { }); it('Should deactivate singletons from module bindings when unload', () => { - const sid = 'sid'; - const container = new Container(); - let moduleBindingDeactivated: string | undefined - let containerDeactivated: string | undefined - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - bind(sid).toConstantValue('Module').onDeactivation(injectable => { moduleBindingDeactivated = injectable }); - onDeactivation(sid, injectable => { containerDeactivated = injectable }) - }); + const sid: string = 'sid'; + const container: Container = new Container(); + let moduleBindingDeactivated: string | undefined; + let containerDeactivated: string | undefined; + const module: ContainerModule = new ContainerModule( + ( + bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + _onActivation: interfaces.Container['onActivation'], + onDeactivation: interfaces.Container['onDeactivation'], + ) => { + bind(sid) + .toConstantValue('Module') + .onDeactivation((injectable: string) => { + moduleBindingDeactivated = injectable; + }); + onDeactivation(sid, (injectable: string) => { + containerDeactivated = injectable; + }); + }, + ); container.load(module); container.get(sid); @@ -231,25 +309,42 @@ describe('ContainerModule', () => { }); it('Should remove container handlers from module when unload', () => { - const sid = 'sid'; - const container = new Container(); - let activatedNotModule: string | undefined - let deactivatedNotModule: string | undefined - container.onActivation(sid, (_, injected) => { - activatedNotModule = injected; - return injected; - }); - container.onDeactivation(sid, injected => { deactivatedNotModule = injected }) - container.bind(sid).toConstantValue('Value'); - let activationCount = 0; - let deactivationCount = 0; - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - onDeactivation(sid, _ => { deactivationCount++ }); - onActivation(sid, (_, injected) => { - activationCount++; + const sid: string = 'sid'; + const container: Container = new Container(); + let activatedNotModule: string | undefined; + let deactivatedNotModule: string | undefined; + container.onActivation( + sid, + (_: interfaces.Context, injected: string) => { + activatedNotModule = injected; return injected; - }); + }, + ); + container.onDeactivation(sid, (injected: string) => { + deactivatedNotModule = injected; }); + container.bind(sid).toConstantValue('Value'); + let activationCount: number = 0; + let deactivationCount: number = 0; + const module: ContainerModule = new ContainerModule( + ( + _bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + onActivation: interfaces.Container['onActivation'], + onDeactivation: interfaces.Container['onDeactivation'], + ) => { + onDeactivation(sid, (_: string) => { + deactivationCount++; + }); + onActivation(sid, (_: interfaces.Context, injected: string) => { + activationCount++; + return injected; + }); + }, + ); container.load(module); container.unload(module); @@ -260,19 +355,23 @@ describe('ContainerModule', () => { expect(deactivationCount).to.equal(0); expect(activatedNotModule).to.equal('Value'); - expect(deactivatedNotModule).to.equal('Value') - }) + expect(deactivatedNotModule).to.equal('Value'); + }); it('Should remove module bindings when unloadAsync', async () => { - const sid = 'sid'; - const container = new Container(); - container.onDeactivation(sid, injected => Promise.resolve()); + const sid: string = 'sid'; + const container: Container = new Container(); + container.onDeactivation(sid, async (_injected: unknown) => + Promise.resolve(), + ); container.bind(sid).toConstantValue('Not module'); - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - bind(sid).toConstantValue('Module') - }); + const module: ContainerModule = new ContainerModule( + (bind: interfaces.Bind) => { + bind(sid).toConstantValue('Module'); + }, + ); container.load(module); - let values = container.getAll(sid); + let values: unknown[] = container.getAll(sid); expect(values).to.deep.equal(['Not module', 'Module']); await container.unloadAsync(module); @@ -281,17 +380,31 @@ describe('ContainerModule', () => { }); it('Should deactivate singletons from module bindings when unloadAsync', async () => { - const sid = 'sid'; - const container = new Container(); - let moduleBindingDeactivated: string | undefined - let containerDeactivated: string | undefined - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - bind(sid).toConstantValue('Module').onDeactivation(injectable => { moduleBindingDeactivated = injectable }); - onDeactivation(sid, injectable => { - containerDeactivated = injectable; - return Promise.resolve(); - }) - }); + const sid: string = 'sid'; + const container: Container = new Container(); + let moduleBindingDeactivated: string | undefined; + let containerDeactivated: string | undefined; + const module: ContainerModule = new ContainerModule( + ( + bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + _onActivation: interfaces.Container['onActivation'], + onDeactivation: interfaces.Container['onDeactivation'], + ) => { + bind(sid) + .toConstantValue('Module') + .onDeactivation((injectable: string) => { + moduleBindingDeactivated = injectable; + }); + onDeactivation(sid, async (injectable: string) => { + containerDeactivated = injectable; + return Promise.resolve(); + }); + }, + ); container.load(module); container.get(sid); @@ -301,30 +414,43 @@ describe('ContainerModule', () => { }); it('Should remove container handlers from module when unloadAsync', async () => { - const sid = 'sid'; - const container = new Container(); - let activatedNotModule: string | undefined - let deactivatedNotModule: string | undefined - container.onActivation(sid, (_, injected) => { - activatedNotModule = injected; - return injected; - }); - container.onDeactivation(sid, injected => { - deactivatedNotModule = injected; - }) - container.bind(sid).toConstantValue('Value'); - let activationCount = 0; - let deactivationCount = 0; - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - onDeactivation(sid, _ => { - deactivationCount++ - return Promise.resolve(); - }); - onActivation(sid, (_, injected) => { - activationCount++; + const sid: string = 'sid'; + const container: Container = new Container(); + let activatedNotModule: string | undefined; + let deactivatedNotModule: string | undefined; + container.onActivation( + sid, + (_: interfaces.Context, injected: string) => { + activatedNotModule = injected; return injected; - }); + }, + ); + container.onDeactivation(sid, (injected: string) => { + deactivatedNotModule = injected; }); + container.bind(sid).toConstantValue('Value'); + let activationCount: number = 0; + let deactivationCount: number = 0; + const module: ContainerModule = new ContainerModule( + ( + _bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + _unbindAsync: interfaces.UnbindAsync, + onActivation: interfaces.Container['onActivation'], + onDeactivation: interfaces.Container['onDeactivation'], + ) => { + onDeactivation(sid, async (_: string) => { + deactivationCount++; + return Promise.resolve(); + }); + onActivation(sid, (_: interfaces.Context, injected: string) => { + activationCount++; + return injected; + }); + }, + ); container.load(module); await container.unloadAsync(module); @@ -339,25 +465,34 @@ describe('ContainerModule', () => { }); it('should be able to unbindAsync from a module', async () => { - let _unbindAsync: interfaces.UnbindAsync | undefined - const container = new Container(); - const module = new ContainerModule((bind, unbind, isBound, rebind, unbindAsync, onActivation, onDeactivation) => { - _unbindAsync = unbindAsync - }); - const sid = 'sid'; - container.bind(sid).toConstantValue('Value') - container.bind(sid).toConstantValue('Value2') - const deactivated: string[] = [] - container.onDeactivation(sid, injected => { + let unbindAsyncFn: interfaces.UnbindAsync | undefined; + const container: Container = new Container(); + const module: ContainerModule = new ContainerModule( + ( + _bind: interfaces.Bind, + _unbind: interfaces.Unbind, + _isBound: interfaces.IsBound, + _rebind: interfaces.Rebind, + unbindAsync: interfaces.UnbindAsync, + ) => { + unbindAsyncFn = unbindAsync; + }, + ); + const sid: string = 'sid'; + container.bind(sid).toConstantValue('Value'); + container.bind(sid).toConstantValue('Value2'); + const deactivated: string[] = []; + container.onDeactivation(sid, async (injected: string) => { deactivated.push(injected); return Promise.resolve(); - }) + }); container.getAll(sid); container.load(module); - await _unbindAsync!(sid); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await unbindAsyncFn!(sid); expect(deactivated).to.deep.equal(['Value', 'Value2']); //bindings removed - expect(() => container.getAll(sid)).to.throw(`${NOT_REGISTERED} sid`) + expect(() => container.getAll(sid)).to.throw(`${NOT_REGISTERED} sid`); }); -}); \ No newline at end of file +}); diff --git a/test/container/lookup.test.ts b/test/container/lookup.test.ts index 713e55680..d6bdec69d 100644 --- a/test/container/lookup.test.ts +++ b/test/container/lookup.test.ts @@ -1,5 +1,5 @@ - import { expect } from 'chai'; + import { Binding } from '../../src/bindings/binding'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; import { BindingScopeEnum } from '../../src/constants/literal_types'; @@ -8,7 +8,7 @@ import { interfaces } from '../../src/interfaces/interfaces'; class ClonableValue implements interfaces.Clonable> { public readonly val: T; - public constructor(val: T) { + constructor(val: T) { this.val = val; } public clone() { @@ -17,58 +17,77 @@ class ClonableValue implements interfaces.Clonable> { } describe('Lookup', () => { - - const invalid = null as unknown as interfaces.ServiceIdentifier; + const invalid: interfaces.ServiceIdentifier = + null as unknown as interfaces.ServiceIdentifier; it('Should throw when invoking get, remove or hasKey with a null key', () => { - const lookup = new Lookup(); - expect(() => { lookup.get(invalid); }).to.throw(ERROR_MSGS.NULL_ARGUMENT); - expect(() => { lookup.remove(invalid); }).to.throw(ERROR_MSGS.NULL_ARGUMENT); - expect(() => { lookup.hasKey(invalid); }).to.throw(ERROR_MSGS.NULL_ARGUMENT); + const lookup: Lookup = new Lookup(); + expect(() => { + lookup.get(invalid); + }).to.throw(ERROR_MSGS.NULL_ARGUMENT); + expect(() => { + lookup.remove(invalid); + }).to.throw(ERROR_MSGS.NULL_ARGUMENT); + expect(() => { + lookup.hasKey(invalid); + }).to.throw(ERROR_MSGS.NULL_ARGUMENT); }); it('Should throw when attempting to add a null key', () => { - const lookup = new Lookup(); - expect(() => { lookup.add(invalid, new ClonableValue(1)); }).to.throw(ERROR_MSGS.NULL_ARGUMENT); + const lookup: Lookup = new Lookup(); + expect(() => { + lookup.add(invalid, new ClonableValue(1)); + }).to.throw(ERROR_MSGS.NULL_ARGUMENT); }); it('Should throw when attempting to add a null value', () => { - const lookup = new Lookup(); - expect(() => { lookup.add('TEST_KEY', null); }).to.throw(ERROR_MSGS.NULL_ARGUMENT); + const lookup: Lookup = new Lookup(); + expect(() => { + lookup.add('TEST_KEY', null); + }).to.throw(ERROR_MSGS.NULL_ARGUMENT); }); it('Should be able to link multiple values to a string key', () => { - const lookup = new Lookup(); - const key = 'TEST_KEY'; + const lookup: Lookup = new Lookup(); + const key: string = 'TEST_KEY'; lookup.add(key, new ClonableValue(1)); + lookup.add(key, new ClonableValue(2)); - const result = lookup.get(key); + const result: unknown[] = lookup.get(key); + expect(result.length).to.eql(2); }); it('Should be able to link multiple values a symbol key', () => { - const lookup = new Lookup(); - const key = Symbol.for('TEST_KEY'); + const lookup: Lookup = new Lookup(); + const key: symbol = Symbol.for('TEST_KEY'); lookup.add(key, new ClonableValue(1)); + lookup.add(key, new ClonableValue(2)); - const result = lookup.get(key); + const result: unknown[] = lookup.get(key); + expect(result.length).to.eql(2); }); it('Should throws when key not found', () => { - const lookup = new Lookup(); - expect(() => { lookup.get('THIS_KEY_IS_NOT_AVAILABLE'); }).to.throw(ERROR_MSGS.KEY_NOT_FOUND); - expect(() => { lookup.remove('THIS_KEY_IS_NOT_AVAILABLE'); }).to.throw(ERROR_MSGS.KEY_NOT_FOUND); + const lookup: Lookup = new Lookup(); + expect(() => { + lookup.get('THIS_KEY_IS_NOT_AVAILABLE'); + }).to.throw(ERROR_MSGS.KEY_NOT_FOUND); + expect(() => { + lookup.remove('THIS_KEY_IS_NOT_AVAILABLE'); + }).to.throw(ERROR_MSGS.KEY_NOT_FOUND); }); it('Should be clonable', () => { - - const lookup = new Lookup>(); - const key1 = Symbol.for('TEST_KEY'); + const lookup: Lookup> = new Lookup< + interfaces.Clonable + >(); + const key1: symbol = Symbol.for('TEST_KEY'); class Warrior { public kind: string; - public constructor(kind: string) { + constructor(kind: string) { this.kind = kind; } public clone() { @@ -79,113 +98,127 @@ describe('Lookup', () => { lookup.add(key1, new Warrior('ninja')); lookup.add(key1, new Warrior('samurai')); - const copy = lookup.clone(); + const copy: interfaces.Lookup = lookup.clone(); expect(copy.hasKey(key1)).to.eql(true); lookup.remove(key1); expect(copy.hasKey(key1)).to.eql(true); - }); it('Should use use the original non clonable entry if it is not clonable', () => { - const lookup = new Lookup(); - const key1 = Symbol.for('TEST_KEY'); + const lookup: interfaces.Lookup = new Lookup(); + const key1: symbol = Symbol.for('TEST_KEY'); class Warrior { public kind: string; - public constructor(kind: string) { + constructor(kind: string) { this.kind = kind; } } - const warrior = new Warrior('ninja') + const warrior: Warrior = new Warrior('ninja'); lookup.add(key1, warrior); - const copy = lookup.clone(); + const copy: interfaces.Lookup = lookup.clone(); expect(copy.get(key1)[0] === warrior).to.eql(true); - - }) + }); it('Should be able to remove a binding by a condition', () => { - - const moduleId1 = 1; - const moduleId2 = 2; - const warriorId = 'Warrior'; - const weaponId = 'Weapon'; - - const getLookup = () => { - - interface Warrior { } - - class Ninja implements Warrior { } - const ninjaBinding = new Binding(warriorId, BindingScopeEnum.Transient); + const moduleId1: number = 1; + const moduleId2: number = 2; + const warriorId: string = 'Warrior'; + const weaponId: string = 'Weapon'; + + const getLookup: () => Lookup> = () => { + class Ninja {} + const ninjaBinding: Binding = new Binding( + warriorId, + BindingScopeEnum.Transient, + ); ninjaBinding.implementationType = Ninja; ninjaBinding.moduleId = moduleId1; - class Samurai implements Warrior { } - const samuraiBinding = new Binding(warriorId, BindingScopeEnum.Transient); + class Samurai {} + const samuraiBinding: Binding = new Binding( + warriorId, + BindingScopeEnum.Transient, + ); samuraiBinding.implementationType = Samurai; samuraiBinding.moduleId = moduleId2; - interface Weapon { } - - class Shuriken implements Weapon { } - const shurikenBinding = new Binding(weaponId, BindingScopeEnum.Transient); + class Shuriken {} + const shurikenBinding: Binding = new Binding( + weaponId, + BindingScopeEnum.Transient, + ); shurikenBinding.implementationType = Shuriken; shurikenBinding.moduleId = moduleId1; - class Katana implements Weapon { } - const katanaBinding = new Binding(weaponId, BindingScopeEnum.Transient); + class Katana {} + const katanaBinding: Binding = new Binding( + weaponId, + BindingScopeEnum.Transient, + ); katanaBinding.implementationType = Katana; katanaBinding.moduleId = moduleId2; - const lookup = new Lookup>(); + const lookup: Lookup> = new Lookup>(); + lookup.add(warriorId, ninjaBinding); lookup.add(warriorId, samuraiBinding); lookup.add(weaponId, shurikenBinding); lookup.add(weaponId, katanaBinding); return lookup; - }; - const removeByModule = (expected: unknown) => (item: interfaces.Binding): boolean => - item.moduleId === expected; + const removeByModule: ( + expected: unknown, + ) => (item: interfaces.Binding) => boolean = + (expected: unknown) => + (item: interfaces.Binding): boolean => + item.moduleId === expected; - const lookup1 = getLookup(); + const lookup1: Lookup> = getLookup(); expect(lookup1.hasKey(warriorId)).to.eql(true); expect(lookup1.hasKey(weaponId)).to.eql(true); + expect(lookup1.get(warriorId).length).to.eql(2); + expect(lookup1.get(weaponId).length).to.eql(2); - const removeByModule1 = removeByModule(moduleId1); + const removeByModule1: (item: interfaces.Binding) => boolean = + removeByModule(moduleId1); lookup1.removeByCondition(removeByModule1); expect(lookup1.hasKey(warriorId)).to.eql(true); expect(lookup1.hasKey(weaponId)).to.eql(true); expect(lookup1.get(warriorId).length).to.eql(1); expect(lookup1.get(weaponId).length).to.eql(1); - const lookup2 = getLookup(); + const lookup2: Lookup> = getLookup(); expect(lookup2.hasKey(warriorId)).to.eql(true); expect(lookup2.hasKey(weaponId)).to.eql(true); + expect(lookup2.get(warriorId).length).to.eql(2); + expect(lookup2.get(weaponId).length).to.eql(2); - const removeByModule2 = removeByModule(moduleId2); + const removeByModule2: (item: interfaces.Binding) => boolean = + removeByModule(moduleId2); lookup2.removeByCondition(removeByModule1); lookup2.removeByCondition(removeByModule2); expect(lookup2.hasKey(warriorId)).to.eql(false); expect(lookup2.hasKey(weaponId)).to.eql(false); - }); it('should be able to remove the intersection with another lookup', () => { - const lookup = new Lookup(); + const lookup: Lookup = new Lookup(); - const serviceIdentifier1 = 'service-identifier-1'; - const serviceIdentifier2 = 'service-identifier-2'; + const serviceIdentifier1: string = 'service-identifier-1'; + const serviceIdentifier2: string = 'service-identifier-2'; - const serviceIdentifier1Values = [11, 12, 13, 14]; - const serviceIdentifier2Values = [21, 22, 23, 24]; + const serviceIdentifier1Values: number[] = [11, 12, 13, 14]; + + const serviceIdentifier2Values: number[] = [21, 22, 23, 24]; for (const value of serviceIdentifier1Values) { lookup.add(serviceIdentifier1, value); @@ -195,13 +228,17 @@ describe('Lookup', () => { lookup.add(serviceIdentifier2, value); } - const lookupToIntersect = new Lookup(); + const lookupToIntersect: Lookup = new Lookup(); - const lookupToIntersectServiceIdentifier2Values = [23, 24, 25, 26]; + const lookupToIntersectServiceIdentifier2Values: number[] = [ + 23, 24, 25, 26, + ]; - const serviceIdentifier3 = 'service-identifier-3'; + const serviceIdentifier3: string = 'service-identifier-3'; - const lookupToIntersectServiceIdentifier3Values = [31, 32, 33, 34]; + const lookupToIntersectServiceIdentifier3Values: number[] = [ + 31, 32, 33, 34, + ]; for (const value of lookupToIntersectServiceIdentifier2Values) { lookupToIntersect.add(serviceIdentifier2, value); @@ -213,10 +250,12 @@ describe('Lookup', () => { lookup.removeIntersection(lookupToIntersect); - expect(lookup.getMap()).to.deep.equal(new Map([ - [serviceIdentifier1, [...serviceIdentifier1Values]], - [serviceIdentifier2, [21, 22]], - ])); - }); + expect(lookup.getMap()).to.deep.equal( + new Map([ + [serviceIdentifier1, [...serviceIdentifier1Values]], -}); \ No newline at end of file + [serviceIdentifier2, [21, 22]], + ]), + ); + }); +}); diff --git a/test/container/module_activation_store.test.ts b/test/container/module_activation_store.test.ts index 411ae4cc5..3ad7653e8 100644 --- a/test/container/module_activation_store.test.ts +++ b/test/container/module_activation_store.test.ts @@ -1,97 +1,230 @@ import { expect } from 'chai'; -import { ModuleActivationStore } from '../../src/container/module_activation_store' -import { interfaces } from '../../src/inversify' + +import { ModuleActivationStore } from '../../src/container/module_activation_store'; +import { interfaces } from '../../src/inversify'; describe('ModuleActivationStore', () => { it('should remove handlers added by the module', () => { - const moduleActivationStore = new ModuleActivationStore(); + const moduleActivationStore: ModuleActivationStore = + new ModuleActivationStore(); const moduleId1: number = 1; const moduleId2: number = 2; const serviceIdentifier1: string = 'some-service-1'; const serviceIdentifier2: string = 'some-service-2'; - const onActivation1: interfaces.BindingActivation = (c, a) => a; - const onActivation2: interfaces.BindingActivation = (c, a) => a; - const onActivation3: interfaces.BindingActivation = (c, a) => a; - const onDeactivation1: interfaces.BindingDeactivation = (d) => Promise.resolve(); - const onDeactivation2: interfaces.BindingDeactivation = (d) => Promise.resolve(); - const onDeactivation3: interfaces.BindingDeactivation = (d) => Promise.resolve(); - - moduleActivationStore.addActivation(moduleId1, serviceIdentifier1, onActivation1); - moduleActivationStore.addActivation(moduleId1, serviceIdentifier1, onActivation2); - moduleActivationStore.addActivation(moduleId1, serviceIdentifier2, onActivation3); - moduleActivationStore.addDeactivation(moduleId1, serviceIdentifier1, onDeactivation1); - moduleActivationStore.addDeactivation(moduleId1, serviceIdentifier1, onDeactivation2); - moduleActivationStore.addDeactivation(moduleId1, serviceIdentifier2, onDeactivation3); - - const onActivationMod2: interfaces.BindingActivation = (c, a) => a; - const onDeactivationMod2: interfaces.BindingDeactivation = (d) => Promise.resolve(); - moduleActivationStore.addActivation(moduleId2, serviceIdentifier1, onActivationMod2); - moduleActivationStore.addDeactivation(moduleId2, serviceIdentifier1, onDeactivationMod2); - - const handlers = moduleActivationStore.remove(moduleId1); - expect(handlers.onActivations.getMap()).to.deep.equal(new Map([ - [serviceIdentifier1, [onActivation1, onActivation2]], - [serviceIdentifier2, [onActivation3]] - ])); - expect(handlers.onDeactivations.getMap()).to.deep.equal(new Map([ - [serviceIdentifier1, [onDeactivation1, onDeactivation2]], - [serviceIdentifier2, [onDeactivation3]], - ])); - - const noHandlers = moduleActivationStore.remove(moduleId1); + const onActivation1: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onActivation2: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onActivation3: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onDeactivation1: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + const onDeactivation2: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + const onDeactivation3: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + + moduleActivationStore.addActivation( + moduleId1, + serviceIdentifier1, + onActivation1, + ); + moduleActivationStore.addActivation( + moduleId1, + serviceIdentifier1, + onActivation2, + ); + moduleActivationStore.addActivation( + moduleId1, + serviceIdentifier2, + onActivation3, + ); + moduleActivationStore.addDeactivation( + moduleId1, + serviceIdentifier1, + onDeactivation1, + ); + moduleActivationStore.addDeactivation( + moduleId1, + serviceIdentifier1, + onDeactivation2, + ); + moduleActivationStore.addDeactivation( + moduleId1, + serviceIdentifier2, + onDeactivation3, + ); + + const onActivationMod2: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onDeactivationMod2: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + moduleActivationStore.addActivation( + moduleId2, + serviceIdentifier1, + onActivationMod2, + ); + moduleActivationStore.addDeactivation( + moduleId2, + serviceIdentifier1, + onDeactivationMod2, + ); + + const handlers: interfaces.ModuleActivationHandlers = + moduleActivationStore.remove(moduleId1); + expect(handlers.onActivations.getMap()).to.deep.equal( + new Map([ + [serviceIdentifier1, [onActivation1, onActivation2]], + [serviceIdentifier2, [onActivation3]], + ]), + ); + expect(handlers.onDeactivations.getMap()).to.deep.equal( + new Map([ + [serviceIdentifier1, [onDeactivation1, onDeactivation2]], + [serviceIdentifier2, [onDeactivation3]], + ]), + ); + + const noHandlers: interfaces.ModuleActivationHandlers = + moduleActivationStore.remove(moduleId1); expect(noHandlers.onActivations.getMap()).to.deep.equal(new Map()); expect(noHandlers.onDeactivations.getMap()).to.deep.equal(new Map()); - const module2Handlers = moduleActivationStore.remove(moduleId2); - expect(module2Handlers.onActivations.getMap()).to.deep.equal(new Map([[serviceIdentifier1, [onActivationMod2]]])); - expect(module2Handlers.onDeactivations.getMap()).to.deep.equal(new Map([[serviceIdentifier1, [onDeactivationMod2]]])); + const module2Handlers: interfaces.ModuleActivationHandlers = + moduleActivationStore.remove(moduleId2); + expect(module2Handlers.onActivations.getMap()).to.deep.equal( + new Map([[serviceIdentifier1, [onActivationMod2]]]), + ); + expect(module2Handlers.onDeactivations.getMap()).to.deep.equal( + new Map([[serviceIdentifier1, [onDeactivationMod2]]]), + ); }); it('should be able to clone', () => { - const moduleActivationStore = new ModuleActivationStore(); + const moduleActivationStore: ModuleActivationStore = + new ModuleActivationStore(); const moduleId1: number = 1; const moduleId2: number = 2; const serviceIdentifier1: string = 'some-service-1'; const serviceIdentifier2: string = 'some-service-2'; - const onActivation1: interfaces.BindingActivation = (c, a) => a; - const onActivation2: interfaces.BindingActivation = (c, a) => a; - const onActivation3: interfaces.BindingActivation = (c, a) => a; - const onDeactivation1: interfaces.BindingDeactivation = (d) => Promise.resolve(); - const onDeactivation2: interfaces.BindingDeactivation = (d) => Promise.resolve(); - const onDeactivation3: interfaces.BindingDeactivation = (d) => Promise.resolve(); - - moduleActivationStore.addActivation(moduleId1, serviceIdentifier1, onActivation1); - moduleActivationStore.addActivation(moduleId1, serviceIdentifier1, onActivation2); - moduleActivationStore.addActivation(moduleId1, serviceIdentifier2, onActivation3); - moduleActivationStore.addDeactivation(moduleId1, serviceIdentifier1, onDeactivation1); - moduleActivationStore.addDeactivation(moduleId1, serviceIdentifier1, onDeactivation2); - moduleActivationStore.addDeactivation(moduleId1, serviceIdentifier2, onDeactivation3); + const onActivation1: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onActivation2: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onActivation3: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onDeactivation1: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + const onDeactivation2: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + const onDeactivation3: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + + moduleActivationStore.addActivation( + moduleId1, + serviceIdentifier1, + onActivation1, + ); + moduleActivationStore.addActivation( + moduleId1, + serviceIdentifier1, + onActivation2, + ); + moduleActivationStore.addActivation( + moduleId1, + serviceIdentifier2, + onActivation3, + ); + moduleActivationStore.addDeactivation( + moduleId1, + serviceIdentifier1, + onDeactivation1, + ); + moduleActivationStore.addDeactivation( + moduleId1, + serviceIdentifier1, + onDeactivation2, + ); + moduleActivationStore.addDeactivation( + moduleId1, + serviceIdentifier2, + onDeactivation3, + ); - const onActivationMod2: interfaces.BindingActivation = (c, a) => a; - const onDeactivationMod2: interfaces.BindingDeactivation = (d) => Promise.resolve(); - moduleActivationStore.addActivation(moduleId2, serviceIdentifier1, onActivationMod2); - moduleActivationStore.addDeactivation(moduleId2, serviceIdentifier1, onDeactivationMod2); + const onActivationMod2: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onDeactivationMod2: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + moduleActivationStore.addActivation( + moduleId2, + serviceIdentifier1, + onActivationMod2, + ); + moduleActivationStore.addDeactivation( + moduleId2, + serviceIdentifier1, + onDeactivationMod2, + ); - const clone = moduleActivationStore.clone(); + const clone: interfaces.ModuleActivationStore = + moduleActivationStore.clone(); //change original - const onActivation4: interfaces.BindingActivation = (c, a) => a; - const onDeactivation4: interfaces.BindingDeactivation = (d) => Promise.resolve(); - - moduleActivationStore.addActivation(moduleId1, serviceIdentifier1, onActivation4); - moduleActivationStore.addDeactivation(moduleId1, serviceIdentifier1, onDeactivation4); + const onActivation4: interfaces.BindingActivation = ( + _c: interfaces.Context, + a: unknown, + ) => a; + const onDeactivation4: interfaces.BindingDeactivation = async ( + _d: unknown, + ) => Promise.resolve(); + + moduleActivationStore.addActivation( + moduleId1, + serviceIdentifier1, + onActivation4, + ); + moduleActivationStore.addDeactivation( + moduleId1, + serviceIdentifier1, + onDeactivation4, + ); moduleActivationStore.remove(moduleId2); - const cloneModule1Handlers = clone.remove(moduleId1); + const cloneModule1Handlers: interfaces.ModuleActivationHandlers = + clone.remove(moduleId1); expect(cloneModule1Handlers.onActivations.getMap()).to.deep.equal( new Map([ [serviceIdentifier1, [onActivation1, onActivation2]], - [serviceIdentifier2, [onActivation3]] + [serviceIdentifier2, [onActivation3]], ]), ); @@ -102,7 +235,8 @@ describe('ModuleActivationStore', () => { ]), ); - const cloneModule2Handlers = clone.remove(moduleId2); + const cloneModule2Handlers: interfaces.ModuleActivationHandlers = + clone.remove(moduleId2); expect(cloneModule2Handlers.onActivations.getMap()).to.deep.equal( new Map([[serviceIdentifier1, [onActivationMod2]]]), @@ -112,4 +246,4 @@ describe('ModuleActivationStore', () => { new Map([[serviceIdentifier1, [onDeactivationMod2]]]), ); }); -}); \ No newline at end of file +}); diff --git a/test/features/metadata_reader.test.ts b/test/features/metadata_reader.test.ts index 57c814e9a..9382ac369 100644 --- a/test/features/metadata_reader.test.ts +++ b/test/features/metadata_reader.test.ts @@ -1,11 +1,16 @@ -import { expect } from "chai"; -import * as METADATA_KEY from "../../src/constants/metadata_keys"; -import { interfaces } from "../../src/interfaces/interfaces"; -import { Container, inject, injectable, MetadataReader } from "../../src/inversify"; -import { Metadata } from "../../src/planning/metadata"; - -describe("Custom Metadata Reader", () => { - +import { expect } from 'chai'; + +import * as METADATA_KEY from '../../src/constants/metadata_keys'; +import { interfaces } from '../../src/interfaces/interfaces'; +import { + Container, + inject, + injectable, + MetadataReader, +} from '../../src/inversify'; +import { Metadata } from '../../src/planning/metadata'; + +describe('Custom Metadata Reader', () => { interface FunctionWithMetadata extends NewableFunction { constructorInjections: interfaces.ServiceIdentifier[]; propertyInjections: PropertyInjectionMetadata[]; @@ -17,29 +22,40 @@ describe("Custom Metadata Reader", () => { } class StaticPropsMetadataReader implements interfaces.MetadataReader { - - public getConstructorMetadata(constructorFunc: FunctionWithMetadata): interfaces.ConstructorMetadata { - - const formatMetadata = (injections: interfaces.ServiceIdentifier[]) => { + public getConstructorMetadata( + constructorFunc: FunctionWithMetadata, + ): interfaces.ConstructorMetadata { + const formatMetadata: ( + injections: interfaces.ServiceIdentifier[], + ) => interfaces.MetadataMap = ( + injections: interfaces.ServiceIdentifier[], + ) => { const userGeneratedMetadata: interfaces.MetadataMap = {}; - injections.forEach((injection, index) => { - const metadata = new Metadata(METADATA_KEY.INJECT_TAG, injection); - if (Array.isArray(userGeneratedMetadata[index])) { - userGeneratedMetadata[index]?.push(metadata); - } else { - userGeneratedMetadata[index] = [metadata]; - } - }); + injections.forEach( + (injection: interfaces.ServiceIdentifier, index: number) => { + const metadata: Metadata = new Metadata( + METADATA_KEY.INJECT_TAG, + injection, + ); + if (Array.isArray(userGeneratedMetadata[index])) { + userGeneratedMetadata[index].push(metadata); + } else { + userGeneratedMetadata[index] = [metadata]; + } + }, + ); return userGeneratedMetadata; }; - const constructorInjections = constructorFunc.constructorInjections; + const constructorInjections: interfaces.ServiceIdentifier[] = + constructorFunc.constructorInjections; if (!Array.isArray(constructorInjections)) { - throw new Error("Missing constructorInjections annotation!"); + throw new Error('Missing constructorInjections annotation!'); } - const userGeneratedConsturctorMetadata = formatMetadata(constructorInjections); + const userGeneratedConsturctorMetadata: interfaces.MetadataMap = + formatMetadata(constructorInjections); return { // compilerGeneratedMetadata lenght must match userGeneratedMetadata @@ -47,285 +63,325 @@ describe("Custom Metadata Reader", () => { // system is powered by decorators. The TypeScript compiler could generate // some metadata when the emitDecoratorMetadata flag is enabled. compilerGeneratedMetadata: new Array(constructorInjections.length), - userGeneratedMetadata: userGeneratedConsturctorMetadata + userGeneratedMetadata: userGeneratedConsturctorMetadata, }; - } - public getPropertiesMetadata(constructorFunc: FunctionWithMetadata): interfaces.MetadataMap { - - const formatMetadata = (injections: PropertyInjectionMetadata[]) => { + public getPropertiesMetadata( + constructorFunc: FunctionWithMetadata, + ): interfaces.MetadataMap { + const formatMetadata: ( + injections: PropertyInjectionMetadata[], + ) => interfaces.MetadataMap = ( + injections: PropertyInjectionMetadata[], + ) => { const userGeneratedMetadata: interfaces.MetadataMap = {}; - injections.forEach((propInjection, index) => { - const metadata = new Metadata(METADATA_KEY.INJECT_TAG, propInjection.injection); - if (Array.isArray(userGeneratedMetadata[propInjection.propName])) { - userGeneratedMetadata[propInjection.propName]?.push(metadata); - } else { - userGeneratedMetadata[propInjection.propName] = [metadata]; - } - }); + injections.forEach( + (propInjection: PropertyInjectionMetadata, _index: number) => { + const metadata: Metadata = new Metadata( + METADATA_KEY.INJECT_TAG, + propInjection.injection, + ); + if (Array.isArray(userGeneratedMetadata[propInjection.propName])) { + userGeneratedMetadata[propInjection.propName]?.push(metadata); + } else { + userGeneratedMetadata[propInjection.propName] = [metadata]; + } + }, + ); return userGeneratedMetadata; }; - const propertyInjections = constructorFunc.propertyInjections; + const propertyInjections: PropertyInjectionMetadata[] = + constructorFunc.propertyInjections; if (!Array.isArray(propertyInjections)) { - throw new Error("Missing propertyInjections annotation!"); + throw new Error('Missing propertyInjections annotation!'); } - const userGeneratedPropertyMetadata = formatMetadata(propertyInjections); - return userGeneratedPropertyMetadata; + const userGeneratedPropertyMetadata: interfaces.MetadataMap = + formatMetadata(propertyInjections); + return userGeneratedPropertyMetadata; } - } - it("Should be able to use custom constructor injection metadata", () => { - - interface Ninja { + it('Should be able to use custom constructor injection metadata', () => { + interface NinjaInterface { fight(): string; sneak(): string; } - interface Katana { + interface KatanaInterface { hit(): string; } - interface Shuriken { + interface ShurikenInterface { throw(): string; } - class Katana implements Katana { - public static readonly constructorInjections = []; - public static readonly propertyInjections = []; + class Katana implements KatanaInterface { + public static readonly constructorInjections: [] = []; + public static readonly propertyInjections: [] = []; public hit() { - return "cut!"; + return 'cut!'; } } - class Shuriken implements Shuriken { - public static readonly constructorInjections = []; - public static readonly propertyInjections = []; + class Shuriken implements ShurikenInterface { + public static readonly constructorInjections: [] = []; + public static readonly propertyInjections: [] = []; public throw() { - return "hit!"; + return 'hit!'; } } - class Ninja implements Ninja { - - public static readonly constructorInjections = ["Katana", "Shuriken"]; - public static readonly propertyInjections = []; + class Ninja implements NinjaInterface { + public static readonly constructorInjections: [string, string] = [ + 'Katana', + 'Shuriken', + ]; + public static readonly propertyInjections: [] = []; - private _katana: Katana; - private _shuriken: Shuriken; + private readonly _katana: KatanaInterface; + private readonly _shuriken: ShurikenInterface; - public constructor( - katana: Katana, - shuriken: Shuriken - ) { + constructor(katana: Katana, shuriken: Shuriken) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.applyCustomMetadataReader(new StaticPropsMetadataReader()); - container.bind("Ninja").to(Ninja); - container.bind("Katana").to(Katana); - container.bind("Shuriken").to(Shuriken); + container.bind('Ninja').to(Ninja); + container.bind('Katana').to(Katana); + container.bind('Shuriken').to(Shuriken); - const ninja = container.get("Ninja"); - - expect(ninja.fight()).eql("cut!"); - expect(ninja.sneak()).eql("hit!"); + const ninja: NinjaInterface = container.get('Ninja'); + expect(ninja.fight()).eql('cut!'); + expect(ninja.sneak()).eql('hit!'); }); - it("Should be able to use custom prop injection metadata", () => { - - interface Ninja { + it('Should be able to use custom prop injection metadata', () => { + interface NinjaInterface { fight(): string; sneak(): string; } - interface Katana { + interface KatanaInterface { hit(): string; } - interface Shuriken { + interface ShurikenInterface { throw(): string; } - class Katana implements Katana { - public static readonly constructorInjections = []; - public static readonly propertyInjections = []; - public static readonly __brk = 1; // TEMP + class Katana implements KatanaInterface { + public static readonly constructorInjections: [] = []; + public static readonly propertyInjections: [] = []; + public static readonly brk: number = 1; public hit() { - return "cut!"; + return 'cut!'; } } - class Shuriken implements Shuriken { - public static readonly constructorInjections = []; - public static readonly propertyInjections = []; - public static readonly __brk = 1; // TEMP + class Shuriken implements ShurikenInterface { + public static readonly constructorInjections: [] = []; + public static readonly propertyInjections: [] = []; + public static readonly brk: number = 1; public throw() { - return "hit!"; + return 'hit!'; } } - class Ninja implements Ninja { + class Ninja implements NinjaInterface { + public static readonly constructorInjections: [] = []; - public static readonly constructorInjections = []; - - public static readonly propertyInjections = [ - { propName: "_katana", injection: "Katana" }, - { propName: "_shuriken", injection: "Shuriken" } + public static readonly propertyInjections: PropertyInjectionMetadata[] = [ + { injection: 'Katana', propName: '_katana' }, + { injection: 'Shuriken', propName: '_shuriken' }, ]; - public static readonly __brk = 1; // TEMP - - private _katana!: Katana; - private _shuriken!: Shuriken; - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } + public static readonly brk: number = 1; + private readonly _katana!: Katana; + private readonly _shuriken!: Shuriken; + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.applyCustomMetadataReader(new StaticPropsMetadataReader()); - container.bind("Ninja").to(Ninja); - container.bind("Katana").to(Katana); - container.bind("Shuriken").to(Shuriken); - - const ninja = container.get("Ninja"); + container.bind('Ninja').to(Ninja); + container.bind('Katana').to(Katana); + container.bind('Shuriken').to(Shuriken); - expect(ninja.fight()).eql("cut!"); - expect(ninja.sneak()).eql("hit!"); + const ninja: NinjaInterface = container.get('Ninja'); + expect(ninja.fight()).eql('cut!'); + expect(ninja.sneak()).eql('hit!'); }); - it("Should be able to use extend the default metadata reader", () => { - + it('Should be able to use extend the default metadata reader', () => { const constructorMetadataLog: interfaces.ConstructorMetadata[] = []; const propertyMetadataLog: interfaces.MetadataMap[] = []; class CustomMetadataReader extends MetadataReader { - public override getConstructorMetadata(constructorFunc: NewableFunction): interfaces.ConstructorMetadata { - const constructorMetadata = super.getConstructorMetadata(constructorFunc); + public override getConstructorMetadata( + constructorFunc: NewableFunction, + ): interfaces.ConstructorMetadata { + const constructorMetadata: interfaces.ConstructorMetadata = + super.getConstructorMetadata(constructorFunc); constructorMetadataLog.push(constructorMetadata); return constructorMetadata; } - public override getPropertiesMetadata(constructorFunc: NewableFunction): interfaces.MetadataMap { - const propertyMetadata = super.getPropertiesMetadata(constructorFunc); + public override getPropertiesMetadata( + constructorFunc: NewableFunction, + ): interfaces.MetadataMap { + const propertyMetadata: interfaces.MetadataMap = + super.getPropertiesMetadata(constructorFunc); propertyMetadataLog.push(propertyMetadata); return propertyMetadata; } } - interface Ninja { + interface AbstractNinja { fight(): string; sneak(): string; } - interface Katana { + interface AbstractKatana { hit(): string; } - interface Shuriken { + interface AbstractShuriken { throw(): string; } @injectable() - class Katana implements Katana { + class Katana implements AbstractKatana { public hit() { - return "cut!"; + return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken implements AbstractShuriken { public throw() { - return "hit!"; + return 'hit!'; } } @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - private _shuriken: Shuriken; + class Ninja implements AbstractNinja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - public constructor( - @inject("Katana") katana: Katana, - @inject("Shuriken") shuriken: Shuriken + constructor( + @inject('Katana') katana: Katana, + @inject('Shuriken') shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.applyCustomMetadataReader(new CustomMetadataReader()); - container.bind("Ninja").to(Ninja); - container.bind("Katana").to(Katana); - container.bind("Shuriken").to(Shuriken); + container.bind('Ninja').to(Ninja); + container.bind('Katana').to(Katana); + container.bind('Shuriken').to(Shuriken); - const ninja = container.get("Ninja"); + const ninja: Ninja = container.get('Ninja'); - expect(ninja.fight()).eql("cut!"); - expect(ninja.sneak()).eql("hit!"); + expect(ninja.fight()).eql('cut!'); + expect(ninja.sneak()).eql('hit!'); expect(Array.isArray(constructorMetadataLog)).eq(true); + expect(constructorMetadataLog.length).eq(3); - const constructorMetadataLogFirstElement = constructorMetadataLog[0] as interfaces.ConstructorMetadata; - const constructorMetadataLogSecondElement = constructorMetadataLog[1] as interfaces.ConstructorMetadata; - const constructorMetadataLogThirdElement = constructorMetadataLog[2] as interfaces.ConstructorMetadata; + const constructorMetadataLogFirstElement: interfaces.ConstructorMetadata = + constructorMetadataLog[0] as interfaces.ConstructorMetadata; + const constructorMetadataLogSecondElement: interfaces.ConstructorMetadata = + constructorMetadataLog[1] as interfaces.ConstructorMetadata; + const constructorMetadataLogThirdElement: interfaces.ConstructorMetadata = + constructorMetadataLog[2] as interfaces.ConstructorMetadata; - const compilerGeneratedMetadata0 = constructorMetadataLogFirstElement.compilerGeneratedMetadata; + const compilerGeneratedMetadata0: NewableFunction[] | undefined = + constructorMetadataLogFirstElement.compilerGeneratedMetadata; if (compilerGeneratedMetadata0) { expect(compilerGeneratedMetadata0[0]).eq(Katana); expect(compilerGeneratedMetadata0[1]).eq(Shuriken); } - const userGeneratedMetadataFirstElement = - constructorMetadataLogFirstElement.userGeneratedMetadata["0"] as interfaces.Metadata[]; - - const userGeneratedMetadataSecondElement = - constructorMetadataLogFirstElement.userGeneratedMetadata["1"] as interfaces.Metadata[]; - - expect((userGeneratedMetadataFirstElement[0] as interfaces.Metadata).key).eq("inject"); - expect((userGeneratedMetadataFirstElement[0] as interfaces.Metadata).value).eq("Katana"); - expect((userGeneratedMetadataSecondElement[0] as interfaces.Metadata).key).eq("inject"); - expect((userGeneratedMetadataSecondElement[0] as interfaces.Metadata).value).eq("Shuriken"); - - expect(JSON.stringify(constructorMetadataLogSecondElement.compilerGeneratedMetadata)).eq(JSON.stringify([])); - expect(JSON.stringify(constructorMetadataLogThirdElement.compilerGeneratedMetadata)).eq(JSON.stringify([])); - expect(JSON.stringify(constructorMetadataLogSecondElement.userGeneratedMetadata)).eq(JSON.stringify({})); - expect(JSON.stringify(constructorMetadataLogThirdElement.userGeneratedMetadata)).eq(JSON.stringify({})); + const userGeneratedMetadataFirstElement: interfaces.Metadata[] = + constructorMetadataLogFirstElement.userGeneratedMetadata[ + '0' + ] as interfaces.Metadata[]; + + const userGeneratedMetadataSecondElement: interfaces.Metadata[] = + constructorMetadataLogFirstElement.userGeneratedMetadata[ + '1' + ] as interfaces.Metadata[]; + + expect( + (userGeneratedMetadataFirstElement[0] as interfaces.Metadata).key, + ).eq('inject'); + expect( + (userGeneratedMetadataFirstElement[0] as interfaces.Metadata).value, + ).eq('Katana'); + expect( + (userGeneratedMetadataSecondElement[0] as interfaces.Metadata).key, + ).eq('inject'); + expect( + (userGeneratedMetadataSecondElement[0] as interfaces.Metadata).value, + ).eq('Shuriken'); + + expect( + JSON.stringify( + constructorMetadataLogSecondElement.compilerGeneratedMetadata, + ), + ).eq(JSON.stringify([])); + expect( + JSON.stringify( + constructorMetadataLogThirdElement.compilerGeneratedMetadata, + ), + ).eq(JSON.stringify([])); + expect( + JSON.stringify(constructorMetadataLogSecondElement.userGeneratedMetadata), + ).eq(JSON.stringify({})); + expect( + JSON.stringify(constructorMetadataLogThirdElement.userGeneratedMetadata), + ).eq(JSON.stringify({})); expect(propertyMetadataLog.length).eq(3); - const getLength = (metadata: interfaces.MetadataMap) => { - return (metadata as unknown as { length: number }).length - } - - expect(getLength(propertyMetadataLog[0] as interfaces.MetadataMap)).eq(0); - expect(getLength(propertyMetadataLog[1] as interfaces.MetadataMap)).eq(0); - expect(getLength(propertyMetadataLog[2] as interfaces.MetadataMap)).eq(0); - + expect(propertyMetadataLog[0] as interfaces.MetadataMap).to.deep.equal({}); + expect(propertyMetadataLog[1] as interfaces.MetadataMap).to.deep.equal({}); + expect(propertyMetadataLog[2] as interfaces.MetadataMap).to.deep.equal({}); }); - -}); \ No newline at end of file +}); diff --git a/test/features/named_default.test.ts b/test/features/named_default.test.ts index 7c6445906..f82d37f3a 100644 --- a/test/features/named_default.test.ts +++ b/test/features/named_default.test.ts @@ -1,19 +1,20 @@ -import { expect } from "chai"; -import { Container, inject, injectable, named } from "../../src/inversify"; +import { expect } from 'chai'; -describe("Named default", () => { - - it("Should be able to inject a default to avoid ambiguous binding exceptions", () => { +import { Container, inject, injectable, named } from '../../src/inversify'; +describe('Named default', () => { + it('Should be able to inject a default to avoid ambiguous binding exceptions', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: "Warrior", - Weapon: "Weapon" + Warrior: 'Warrior', + Weapon: 'Weapon', }; + // eslint-disable-next-line @typescript-eslint/typedef const TAG = { - chinese: "chinese", - japanese: "japanese", - throwable: "throwable" + chinese: 'chinese', + japanese: 'japanese', + throwable: 'throwable', }; interface Weapon { @@ -28,16 +29,16 @@ describe("Named default", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { - this.name = "Shuriken"; + constructor() { + this.name = 'Shuriken'; } } @@ -45,10 +46,8 @@ describe("Named default", () => { class Samurai implements Warrior { public name: string; public weapon: Weapon; - public constructor( - @inject(TYPES.Weapon) weapon: Weapon - ) { - this.name = "Samurai"; + constructor(@inject(TYPES.Weapon) weapon: Weapon) { + this.name = 'Samurai'; this.weapon = weapon; } } @@ -57,38 +56,51 @@ describe("Named default", () => { class Ninja implements Warrior { public name: string; public weapon: Weapon; - public constructor( - @inject(TYPES.Weapon) @named(TAG.throwable) weapon: Weapon - ) { - this.name = "Ninja"; + constructor(@inject(TYPES.Weapon) @named(TAG.throwable) weapon: Weapon) { + this.name = 'Ninja'; this.weapon = weapon; } } - const container = new Container(); - container.bind(TYPES.Warrior).to(Ninja).whenTargetNamed(TAG.chinese); - container.bind(TYPES.Warrior).to(Samurai).whenTargetNamed(TAG.japanese); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetNamed(TAG.throwable); + const container: Container = new Container(); + container + .bind(TYPES.Warrior) + .to(Ninja) + .whenTargetNamed(TAG.chinese); + container + .bind(TYPES.Warrior) + .to(Samurai) + .whenTargetNamed(TAG.japanese); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetNamed(TAG.throwable); container.bind(TYPES.Weapon).to(Katana).whenTargetIsDefault(); - const ninja = container.getNamed(TYPES.Warrior, TAG.chinese); - const samurai = container.getNamed(TYPES.Warrior, TAG.japanese); - - expect(ninja.name).to.eql("Ninja"); - expect(ninja.weapon.name).to.eql("Shuriken"); - expect(samurai.name).to.eql("Samurai"); - expect(samurai.weapon.name).to.eql("Katana"); - + const ninja: Warrior = container.getNamed( + TYPES.Warrior, + TAG.chinese, + ); + const samurai: Warrior = container.getNamed( + TYPES.Warrior, + TAG.japanese, + ); + + expect(ninja.name).to.eql('Ninja'); + expect(ninja.weapon.name).to.eql('Shuriken'); + expect(samurai.name).to.eql('Samurai'); + expect(samurai.weapon.name).to.eql('Katana'); }); - it("Should be able to select a default to avoid ambiguous binding exceptions", () => { - + it('Should be able to select a default to avoid ambiguous binding exceptions', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Weapon: "Weapon" + Weapon: 'Weapon', }; + // eslint-disable-next-line @typescript-eslint/typedef const TAG = { - throwable: "throwable" + throwable: 'throwable', }; interface Weapon { @@ -98,29 +110,37 @@ describe("Named default", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { - this.name = "Shuriken"; + constructor() { + this.name = 'Shuriken'; } } - const container = new Container(); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetNamed(TAG.throwable); - container.bind(TYPES.Weapon).to(Katana).inSingletonScope().whenTargetIsDefault(); - - const defaultWeapon = container.get(TYPES.Weapon); - const throwableWeapon = container.getNamed(TYPES.Weapon, TAG.throwable); - - expect(defaultWeapon.name).eql("Katana"); - expect(throwableWeapon.name).eql("Shuriken"); - + const container: Container = new Container(); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetNamed(TAG.throwable); + container + .bind(TYPES.Weapon) + .to(Katana) + .inSingletonScope() + .whenTargetIsDefault(); + + const defaultWeapon: Weapon = container.get(TYPES.Weapon); + const throwableWeapon: Weapon = container.getNamed( + TYPES.Weapon, + TAG.throwable, + ); + + expect(defaultWeapon.name).eql('Katana'); + expect(throwableWeapon.name).eql('Shuriken'); }); - -}); \ No newline at end of file +}); diff --git a/test/features/property_injection.test.ts b/test/features/property_injection.test.ts index 11ac46d41..abff19ae0 100644 --- a/test/features/property_injection.test.ts +++ b/test/features/property_injection.test.ts @@ -1,18 +1,22 @@ +import { expect } from 'chai'; -import { expect } from "chai"; import { - Container, inject, injectable, - multiInject, named, optional, - tagged, unmanaged -} from "../../src/inversify"; - -describe("Property Injection", () => { - - it("Should be able to inject a property", () => { - + Container, + inject, + injectable, + multiInject, + named, + optional, + tagged, + unmanaged, +} from '../../src/inversify'; + +describe('Property Injection', () => { + it('Should be able to inject a property', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: "Warrior", - Weapon: "Weapon" + Warrior: 'Warrior', + Weapon: 'Weapon', }; interface Weapon { @@ -22,8 +26,8 @@ describe("Property Injection", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @@ -34,35 +38,36 @@ describe("Property Injection", () => { @injectable() class Samurai implements Warrior { - public name: string; @inject(TYPES.Weapon) public weapon!: Weapon; - public constructor() { - this.name = "Samurai"; + public name: string; + + constructor() { + this.name = 'Samurai'; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).to(Samurai); container.bind(TYPES.Weapon).to(Katana); - const warrior = container.get(TYPES.Warrior); - expect(warrior.name).to.eql("Samurai"); + const warrior: Warrior = container.get(TYPES.Warrior); + expect(warrior.name).to.eql('Samurai'); expect(warrior.weapon).not.to.eql(undefined); - expect(warrior.weapon.name).to.eql("Katana"); - + expect(warrior.weapon.name).to.eql('Katana'); }); - it("Should be able to inject a property combined with constructor injection", () => { - + it('Should be able to inject a property combined with constructor injection', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: "Warrior", - Weapon: "Weapon" + Warrior: 'Warrior', + Weapon: 'Weapon', }; + // eslint-disable-next-line @typescript-eslint/typedef const TAGS = { - Primary: "Primary", - Secondary: "Secondary" + Primary: 'Primary', + Secondary: 'Secondary', }; interface Weapon { @@ -72,16 +77,16 @@ describe("Property Injection", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { - this.name = "Shuriken"; + constructor() { + this.name = 'Shuriken'; } } @@ -93,46 +98,48 @@ describe("Property Injection", () => { @injectable() class Samurai implements Warrior { - - public name: string; - public primaryWeapon: Weapon; - @inject(TYPES.Weapon) @named(TAGS.Secondary) public secondaryWeapon!: Weapon; + public name: string; + public primaryWeapon: Weapon; - public constructor( - @inject(TYPES.Weapon) @named(TAGS.Primary) weapon: Weapon - ) { - this.name = "Samurai"; + constructor(@inject(TYPES.Weapon) @named(TAGS.Primary) weapon: Weapon) { + this.name = 'Samurai'; this.primaryWeapon = weapon; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).to(Samurai); - container.bind(TYPES.Weapon).to(Katana).whenTargetNamed(TAGS.Primary); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetNamed(TAGS.Secondary); - - const warrior = container.get(TYPES.Warrior); - expect(warrior.name).to.eql("Samurai"); + container + .bind(TYPES.Weapon) + .to(Katana) + .whenTargetNamed(TAGS.Primary); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetNamed(TAGS.Secondary); + + const warrior: Warrior = container.get(TYPES.Warrior); + expect(warrior.name).to.eql('Samurai'); expect(warrior.primaryWeapon).not.to.eql(undefined); - expect(warrior.primaryWeapon.name).to.eql("Katana"); + expect(warrior.primaryWeapon.name).to.eql('Katana'); expect(warrior.secondaryWeapon).not.to.eql(undefined); - expect(warrior.secondaryWeapon.name).to.eql("Shuriken"); - + expect(warrior.secondaryWeapon.name).to.eql('Shuriken'); }); - it("Should be able to inject a named property", () => { - + it('Should be able to inject a named property', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: "Warrior", - Weapon: "Weapon" + Warrior: 'Warrior', + Weapon: 'Weapon', }; + // eslint-disable-next-line @typescript-eslint/typedef const TAGS = { - Primary: "Primary", - Secondary: "Secondary" + Primary: 'Primary', + Secondary: 'Secondary', }; interface Weapon { @@ -142,16 +149,16 @@ describe("Property Injection", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { - this.name = "Shuriken"; + constructor() { + this.name = 'Shuriken'; } } @@ -163,9 +170,6 @@ describe("Property Injection", () => { @injectable() class Samurai implements Warrior { - - public name: string; - @inject(TYPES.Weapon) @named(TAGS.Primary) public primaryWeapon!: Weapon; @@ -174,36 +178,44 @@ describe("Property Injection", () => { @named(TAGS.Secondary) public secondaryWeapon!: Weapon; - public constructor() { - this.name = "Samurai"; + public name: string; + + constructor() { + this.name = 'Samurai'; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).to(Samurai); - container.bind(TYPES.Weapon).to(Katana).whenTargetNamed(TAGS.Primary); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetNamed(TAGS.Secondary); - - const warrior = container.get(TYPES.Warrior); - expect(warrior.name).to.eql("Samurai"); + container + .bind(TYPES.Weapon) + .to(Katana) + .whenTargetNamed(TAGS.Primary); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetNamed(TAGS.Secondary); + + const warrior: Warrior = container.get(TYPES.Warrior); + expect(warrior.name).to.eql('Samurai'); expect(warrior.primaryWeapon).not.to.eql(undefined); - expect(warrior.primaryWeapon.name).to.eql("Katana"); + expect(warrior.primaryWeapon.name).to.eql('Katana'); expect(warrior.secondaryWeapon).not.to.eql(undefined); - expect(warrior.secondaryWeapon.name).to.eql("Shuriken"); - + expect(warrior.secondaryWeapon.name).to.eql('Shuriken'); }); - it("Should be able to inject a tagged property", () => { - + it('Should be able to inject a tagged property', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: "Warrior", - Weapon: "Weapon" + Warrior: 'Warrior', + Weapon: 'Weapon', }; + // eslint-disable-next-line @typescript-eslint/typedef const TAGS = { - Primary: "Primary", - Priority: "Priority", - Secondary: "Secondary" + Primary: 'Primary', + Priority: 'Priority', + Secondary: 'Secondary', }; interface Weapon { @@ -213,16 +225,16 @@ describe("Property Injection", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { - this.name = "Shuriken"; + constructor() { + this.name = 'Shuriken'; } } @@ -234,9 +246,6 @@ describe("Property Injection", () => { @injectable() class Samurai implements Warrior { - - public name: string; - @inject(TYPES.Weapon) @tagged(TAGS.Priority, TAGS.Primary) public primaryWeapon!: Weapon; @@ -245,30 +254,37 @@ describe("Property Injection", () => { @tagged(TAGS.Priority, TAGS.Secondary) public secondaryWeapon!: Weapon; - public constructor() { - this.name = "Samurai"; + public name: string; + + constructor() { + this.name = 'Samurai'; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).to(Samurai); - container.bind(TYPES.Weapon).to(Katana).whenTargetTagged(TAGS.Priority, TAGS.Primary); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetTagged(TAGS.Priority, TAGS.Secondary); - - const warrior = container.get(TYPES.Warrior); - expect(warrior.name).to.eql("Samurai"); + container + .bind(TYPES.Weapon) + .to(Katana) + .whenTargetTagged(TAGS.Priority, TAGS.Primary); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetTagged(TAGS.Priority, TAGS.Secondary); + + const warrior: Warrior = container.get(TYPES.Warrior); + expect(warrior.name).to.eql('Samurai'); expect(warrior.primaryWeapon).not.to.eql(undefined); - expect(warrior.primaryWeapon.name).to.eql("Katana"); + expect(warrior.primaryWeapon.name).to.eql('Katana'); expect(warrior.secondaryWeapon).not.to.eql(undefined); - expect(warrior.secondaryWeapon.name).to.eql("Shuriken"); - + expect(warrior.secondaryWeapon.name).to.eql('Shuriken'); }); - it("Should be able to multi-inject a property", () => { - + it('Should be able to multi-inject a property', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: "Warrior", - Weapon: "Weapon" + Warrior: 'Warrior', + Weapon: 'Weapon', }; interface Weapon { @@ -278,16 +294,16 @@ describe("Property Injection", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { - this.name = "Shuriken"; + constructor() { + this.name = 'Shuriken'; } } @@ -298,42 +314,40 @@ describe("Property Injection", () => { @injectable() class Samurai implements Warrior { - - public name: string; - @multiInject(TYPES.Weapon) public weapons!: Weapon[]; + public name: string; - public constructor() { - this.name = "Samurai"; + constructor() { + this.name = 'Samurai'; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).to(Samurai); container.bind(TYPES.Weapon).to(Katana); container.bind(TYPES.Weapon).to(Shuriken); - const warrior = container.get(TYPES.Warrior); - expect(warrior.name).to.eql("Samurai"); + const warrior: Warrior = container.get(TYPES.Warrior); + expect(warrior.name).to.eql('Samurai'); expect(warrior.weapons[0]).not.to.eql(undefined); - expect(warrior.weapons[0]?.name).to.eql("Katana"); + expect(warrior.weapons[0]?.name).to.eql('Katana'); expect(warrior.weapons[1]).not.to.eql(undefined); - expect(warrior.weapons[1]?.name).to.eql("Shuriken"); - + expect(warrior.weapons[1]?.name).to.eql('Shuriken'); }); - it("Should be able to inject a property in a base class", () => { - + it('Should be able to inject a property in a base class', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: "Warrior", - Weapon: "Weapon" + Warrior: 'Warrior', + Weapon: 'Weapon', }; + // eslint-disable-next-line @typescript-eslint/typedef const TAGS = { - Primary: "Primary", - Priority: "Priority", - Secondary: "Secondary" + Primary: 'Primary', + Priority: 'Priority', + Secondary: 'Secondary', }; interface Weapon { @@ -343,16 +357,16 @@ describe("Property Injection", () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { - this.name = "Katana"; + constructor() { + this.name = 'Katana'; } } @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { - this.name = "Shuriken"; + constructor() { + this.name = 'Shuriken'; } } @@ -363,49 +377,51 @@ describe("Property Injection", () => { @injectable() class BaseWarrior implements Warrior { - - public name: string; - @inject(TYPES.Weapon) @tagged(TAGS.Priority, TAGS.Primary) public primaryWeapon!: Weapon; + public name: string; - public constructor(@unmanaged() name: string) { + constructor(@unmanaged() name: string) { this.name = name; } } @injectable() class Samurai extends BaseWarrior { - @inject(TYPES.Weapon) @tagged(TAGS.Priority, TAGS.Secondary) public secondaryWeapon!: Weapon; - public constructor() { - super("Samurai"); + constructor() { + super('Samurai'); } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).to(Samurai); - container.bind(TYPES.Weapon).to(Katana).whenTargetTagged(TAGS.Priority, TAGS.Primary); - container.bind(TYPES.Weapon).to(Shuriken).whenTargetTagged(TAGS.Priority, TAGS.Secondary); - - const samurai = container.get(TYPES.Warrior); - expect(samurai.name).to.eql("Samurai"); + container + .bind(TYPES.Weapon) + .to(Katana) + .whenTargetTagged(TAGS.Priority, TAGS.Primary); + container + .bind(TYPES.Weapon) + .to(Shuriken) + .whenTargetTagged(TAGS.Priority, TAGS.Secondary); + + const samurai: Samurai = container.get(TYPES.Warrior); + expect(samurai.name).to.eql('Samurai'); expect(samurai.secondaryWeapon).not.to.eql(undefined); - expect(samurai.secondaryWeapon.name).to.eql("Shuriken"); + expect(samurai.secondaryWeapon.name).to.eql('Shuriken'); expect(samurai.primaryWeapon).not.to.eql(undefined); - expect(samurai.primaryWeapon.name).to.eql("Katana"); - + expect(samurai.primaryWeapon.name).to.eql('Katana'); }); - it("Should be able to flag a property injection as optional", () => { - + it('Should be able to flag a property injection as optional', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Route: "Route", - Router: "Router" + Route: 'Route', + Router: 'Router', }; interface Route { @@ -414,28 +430,25 @@ describe("Property Injection", () => { @injectable() class Router { - - @inject(TYPES.Route) @optional() - private route!: Route; + @inject(TYPES.Route) + @optional() + private readonly route!: Route; public getRoute(): Route { return this.route; } - } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Router).to(Router); - const router1 = container.get(TYPES.Router); + const router1: Router = container.get(TYPES.Router); expect(router1.getRoute()).to.eql(undefined); - container.bind(TYPES.Route).toConstantValue({ name: "route1" }); - - const router2 = container.get(TYPES.Router); - expect(router2.getRoute().name).to.eql("route1"); + container.bind(TYPES.Route).toConstantValue({ name: 'route1' }); + const router2: Router = container.get(TYPES.Router); + expect(router2.getRoute().name).to.eql('route1'); }); - -}); \ No newline at end of file +}); diff --git a/test/features/provider.test.ts b/test/features/provider.test.ts index 063c027a4..f03af6f60 100644 --- a/test/features/provider.test.ts +++ b/test/features/provider.test.ts @@ -1,81 +1,98 @@ import { expect } from 'chai'; -import { Container, injectable } from '../../src/inversify'; -describe('Provider', () => { - - it('Should support complex asynchronous initialization processes', (done) => { +import { Container, injectable, interfaces } from '../../src/inversify'; +describe('Provider', () => { + it('Should support complex asynchronous initialization processes', (done: Mocha.Done) => { @injectable() class Ninja { public level: number; public rank: string; - public constructor() { + constructor() { this.level = 0; this.rank = 'Ninja'; } - public train(): Promise { - return new Promise((resolve) => { - setTimeout(() => { - this.level += 10; - resolve(this.level); - }, 10); - }); + public async train(): Promise { + return new Promise( + (resolve: (value: number | PromiseLike) => void) => { + setTimeout(() => { + this.level += 10; + resolve(this.level); + }, 10); + }, + ); } } @injectable() class NinjaMaster { public rank: string; - public constructor() { + constructor() { this.rank = 'NinjaMaster'; } } type NinjaMasterProvider = () => Promise; - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(Ninja).inSingletonScope(); - container.bind('Provider').toProvider((context) => - () => - new Promise((resolve, reject) => { - const ninja = context.container.get('Ninja'); - ninja.train().then((level) => { - if (level >= 20) { - resolve(new NinjaMaster()); - } else { - reject('Not enough training'); - } - }); - })); - - const ninjaMasterProvider = container.get('Provider'); + container.bind('Provider').toProvider( + (context: interfaces.Context) => async () => + new Promise( + ( + resolve: (value: NinjaMaster | PromiseLike) => void, + reject: (reason?: unknown) => void, + ) => { + const ninja: Ninja = context.container.get('Ninja'); + + void ninja.train().then((level: number) => { + if (level >= 20) { + resolve(new NinjaMaster()); + } else { + // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors + reject('Not enough training'); + } + }); + }, + ), + ); + + const ninjaMasterProvider: NinjaMasterProvider = + container.get('Provider'); // helper - function valueOrDefault(provider: () => Promise, defaultValue: T) { - return new Promise((resolve, reject) => { - provider().then((value) => { - resolve(value); - }).catch(() => { - resolve(defaultValue); - }); + async function valueOrDefault( + provider: () => Promise, + defaultValue: T, + ) { + return new Promise((resolve: (value: T | PromiseLike) => void) => { + provider() + .then((value: T) => { + resolve(value); + }) + .catch(() => { + resolve(defaultValue); + }); }); } - valueOrDefault(ninjaMasterProvider, { rank: 'DefaultNinjaMaster' }).then((ninjaMaster) => { + void valueOrDefault(ninjaMasterProvider, { + rank: 'DefaultNinjaMaster', + }).then((ninjaMaster: NinjaMaster) => { expect(ninjaMaster.rank).to.eql('DefaultNinjaMaster'); }); - valueOrDefault(ninjaMasterProvider, { rank: 'DefaultNinjaMaster' }).then((ninjaMaster) => { + void valueOrDefault(ninjaMasterProvider, { + rank: 'DefaultNinjaMaster', + }).then((ninjaMaster: NinjaMaster) => { expect(ninjaMaster.rank).to.eql('NinjaMaster'); done(); }); - }); - it('Should support custom arguments', (done) => { - - const container = new Container(); + it('Should support custom arguments', (done: Mocha.Done) => { + const container: Container = new Container(); interface Sword { material: string; @@ -92,37 +109,40 @@ describe('Provider', () => { container.bind('Sword').to(Katana); - container.bind('SwordProvider').toProvider((context) => - (material: string, damage: number) => - new Promise((resolve) => { - setTimeout(() => { - const katana = context.container.get('Sword'); - katana.material = material; - katana.damage = damage; - resolve(katana); - }, 10); - })); - - const katanaProvider = container.get('SwordProvider'); - - katanaProvider('gold', 100).then((powerfulGoldKatana) => { - + container.bind('SwordProvider').toProvider( + (context: interfaces.Context) => + async (material: string, damage: number) => + new Promise( + (resolve: (value: Sword | PromiseLike) => void) => { + setTimeout(() => { + const katana: Sword = context.container.get('Sword'); + katana.material = material; + katana.damage = damage; + resolve(katana); + }, 10); + }, + ), + ); + + const katanaProvider: SwordProvider = + container.get('SwordProvider'); + + void katanaProvider('gold', 100).then((powerfulGoldKatana: Sword) => { expect(powerfulGoldKatana.material).to.eql('gold'); + expect(powerfulGoldKatana.damage).to.eql(100); - katanaProvider('gold', 10).then((notSoPowerfulGoldKatana) => { + void katanaProvider('gold', 10).then((notSoPowerfulGoldKatana: Sword) => { expect(notSoPowerfulGoldKatana.material).to.eql('gold'); + expect(notSoPowerfulGoldKatana.damage).to.eql(10); done(); }); - }); - }); - it('Should support partial application of custom arguments', (done) => { - - const container = new Container(); + it('Should support partial application of custom arguments', (done: Mocha.Done) => { + const container: Container = new Container(); interface Sword { material: string; @@ -135,43 +155,44 @@ describe('Provider', () => { public damage!: number; } - type SwordProvider = (material: string) => (damage: number) => Promise; + type SwordProvider = ( + material: string, + ) => (damage: number) => Promise; container.bind('Sword').to(Katana); - container.bind('SwordProvider').toProvider((context) => - (material: string) => - (damage: number) => - new Promise((resolve) => { + container.bind('SwordProvider').toProvider( + (context: interfaces.Context) => + (material: string) => + async (damage: number) => + new Promise((resolve: (value: Sword) => void) => { setTimeout(() => { - const katana = context.container.get('Sword'); + const katana: Sword = context.container.get('Sword'); katana.material = material; katana.damage = damage; resolve(katana); }, 10); - })); - - const katanaProvider = container.get('SwordProvider'); - const goldKatanaProvider = katanaProvider('gold'); + }), + ); - goldKatanaProvider(100).then((powerfulGoldKatana) => { + const katanaProvider: SwordProvider = container.get('SwordProvider'); + const goldKatanaProvider: (damage: number) => Promise = + katanaProvider('gold'); + void goldKatanaProvider(100).then((powerfulGoldKatana: Sword) => { expect(powerfulGoldKatana.material).to.eql('gold'); expect(powerfulGoldKatana.damage).to.eql(100); - goldKatanaProvider(10).then((notSoPowerfulGoldKatana) => { + void goldKatanaProvider(10).then((notSoPowerfulGoldKatana: Sword) => { expect(notSoPowerfulGoldKatana.material).to.eql('gold'); expect(notSoPowerfulGoldKatana.damage).to.eql(10); done(); }); - }); - }); - it('Should support the declaration of singletons', (done) => { - - const container = new Container(); + it('Should support the declaration of singletons', (done: Mocha.Done) => { + const container: Container = new Container(); interface Warrior { level: number; @@ -180,7 +201,7 @@ describe('Provider', () => { @injectable() class Ninja implements Warrior { public level: number; - public constructor() { + constructor() { this.level = 0; } } @@ -189,29 +210,26 @@ describe('Provider', () => { container.bind('Warrior').to(Ninja).inSingletonScope(); // Value is singleton! - container.bind('WarriorProvider').toProvider((context) => - (increaseLevel: number) => - new Promise((resolve) => { + container.bind('WarriorProvider').toProvider( + (context: interfaces.Context) => async (increaseLevel: number) => + new Promise((resolve: (value: Warrior) => void) => { setTimeout(() => { - const warrior = context.container.get('Warrior'); // Get singleton! + const warrior: Warrior = context.container.get('Warrior'); warrior.level += increaseLevel; resolve(warrior); }, 100); - })); - - const warriorProvider = container.get('WarriorProvider'); + }), + ); - warriorProvider(10).then((warrior) => { + const warriorProvider: WarriorProvider = container.get('WarriorProvider'); + void warriorProvider(10).then((warrior: Warrior) => { expect(warrior.level).to.eql(10); - warriorProvider(10).then((warrior2) => { - expect(warrior.level).to.eql(20); + void warriorProvider(10).then((warrior2: Warrior) => { + expect(warrior2.level).to.eql(20); done(); }); - }); - }); - -}); \ No newline at end of file +}); diff --git a/test/features/request_scope.test.ts b/test/features/request_scope.test.ts index ce1bb16e9..98459d44f 100644 --- a/test/features/request_scope.test.ts +++ b/test/features/request_scope.test.ts @@ -1,14 +1,14 @@ -import { expect } from "chai"; -import { Container, inject, injectable, named } from "../../src/inversify"; +import { expect } from 'chai'; import { performance } from 'perf_hooks'; -describe("inRequestScope", () => { - - it("Should support request scope in basic bindings", () => { +import { Container, inject, injectable, named } from '../../src/inversify'; +describe('inRequestScope', () => { + it('Should support request scope in basic bindings', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPE = { - Warrior: Symbol("Warrior"), - Weapon: Symbol("Weapon") + Warrior: Symbol('Warrior'), + Weapon: Symbol('Weapon'), }; interface Weapon { @@ -22,12 +22,12 @@ describe("inRequestScope", () => { @injectable() class Katana implements Weapon { - private _madeOn: number; - public constructor() { + private readonly _madeOn: number; + constructor() { this._madeOn = performance.now(); } public use() { - return `Used Katana made on ${this._madeOn}!`; + return `Used Katana made on ${this._madeOn.toString()}!`; } } @@ -35,9 +35,9 @@ describe("inRequestScope", () => { class Samurai implements Warrior { public primaryWeapon: Weapon; public secondaryWeapon: Weapon; - public constructor( + constructor( @inject(TYPE.Weapon) primaryWeapon: Weapon, - @inject(TYPE.Weapon) secondaryWeapon: Weapon + @inject(TYPE.Weapon) secondaryWeapon: Weapon, ) { this.primaryWeapon = primaryWeapon; this.secondaryWeapon = secondaryWeapon; @@ -45,29 +45,37 @@ describe("inRequestScope", () => { } // Without request scope - const container = new Container(); + const container: Container = new Container(); container.bind(TYPE.Weapon).to(Katana); container.bind(TYPE.Warrior).to(Samurai); - const samurai = container.get(TYPE.Warrior); - const samurai2 = container.get(TYPE.Warrior); + const samurai: Warrior = container.get(TYPE.Warrior); + const samurai2: Warrior = container.get(TYPE.Warrior); // One requests should use two instances because scope is transient - expect(samurai.primaryWeapon.use()).not.to.eql(samurai.secondaryWeapon.use()); + expect(samurai.primaryWeapon.use()).not.to.eql( + samurai.secondaryWeapon.use(), + ); // One requests should use two instances because scope is transient - expect(samurai2.primaryWeapon.use()).not.to.eql(samurai2.secondaryWeapon.use()); + expect(samurai2.primaryWeapon.use()).not.to.eql( + samurai2.secondaryWeapon.use(), + ); // Two request should use two Katana instances // for each instance of Samuari because scope is transient - expect(samurai.primaryWeapon.use()).not.to.eql(samurai2.primaryWeapon.use()); - expect(samurai.secondaryWeapon.use()).not.to.eql(samurai2.secondaryWeapon.use()); + expect(samurai.primaryWeapon.use()).not.to.eql( + samurai2.primaryWeapon.use(), + ); + expect(samurai.secondaryWeapon.use()).not.to.eql( + samurai2.secondaryWeapon.use(), + ); // With request scope - const container1 = new Container(); + const container1: Container = new Container(); container1.bind(TYPE.Weapon).to(Katana).inRequestScope(); // Important container1.bind(TYPE.Warrior).to(Samurai); - const samurai3 = container1.get(TYPE.Warrior); - const samurai4 = container1.get(TYPE.Warrior); + const samurai3: Warrior = container1.get(TYPE.Warrior); + const samurai4: Warrior = container1.get(TYPE.Warrior); // One requests should use one instance because scope is request scope expect(samurai3.primaryWeapon.use()).to.eql(samurai3.secondaryWeapon.use()); @@ -77,16 +85,19 @@ describe("inRequestScope", () => { // Two request should use one instances of Katana // for each instance of Samurai because scope is request scope - expect(samurai3.primaryWeapon.use()).not.to.eql(samurai4.primaryWeapon.use()); - expect(samurai3.secondaryWeapon.use()).not.to.eql(samurai4.secondaryWeapon.use()); - + expect(samurai3.primaryWeapon.use()).not.to.eql( + samurai4.primaryWeapon.use(), + ); + expect(samurai3.secondaryWeapon.use()).not.to.eql( + samurai4.secondaryWeapon.use(), + ); }); - it("Should support request scope when using contraints", () => { - + it('Should support request scope when using contraints', () => { + // eslint-disable-next-line @typescript-eslint/typedef const TYPE = { - Warrior: Symbol("Warrior"), - Weapon: Symbol("Weapon") + Warrior: Symbol('Warrior'), + Weapon: Symbol('Weapon'), }; interface Weapon { @@ -101,23 +112,23 @@ describe("inRequestScope", () => { @injectable() class Katana implements Weapon { - private _madeOn: number; - public constructor() { + private readonly _madeOn: number; + constructor() { this._madeOn = performance.now(); } public use() { - return `Used Katana made on ${this._madeOn}!`; + return `Used Katana made on ${this._madeOn.toString()}!`; } } @injectable() class Shuriken implements Weapon { - private _madeOn: number; - public constructor() { + private readonly _madeOn: number; + constructor() { this._madeOn = performance.now(); } public use() { - return `Used Shuriken made on ${this._madeOn}!`; + return `Used Shuriken made on ${this._madeOn.toString()}!`; } } @@ -126,10 +137,10 @@ describe("inRequestScope", () => { public primaryWeapon: Weapon; public secondaryWeapon: Weapon; public tertiaryWeapon: Weapon; - public constructor( - @inject(TYPE.Weapon) @named("sword") primaryWeapon: Weapon, - @inject(TYPE.Weapon) @named("throwable") secondaryWeapon: Weapon, - @inject(TYPE.Weapon) @named("throwable") tertiaryWeapon: Weapon + constructor( + @inject(TYPE.Weapon) @named('sword') primaryWeapon: Weapon, + @inject(TYPE.Weapon) @named('throwable') secondaryWeapon: Weapon, + @inject(TYPE.Weapon) @named('throwable') tertiaryWeapon: Weapon, ) { this.primaryWeapon = primaryWeapon; this.secondaryWeapon = secondaryWeapon; @@ -137,38 +148,52 @@ describe("inRequestScope", () => { } } - const container = new Container(); + const container: Container = new Container(); - container.bind(TYPE.Weapon).to(Katana) + container + .bind(TYPE.Weapon) + .to(Katana) .inRequestScope() - .whenTargetNamed("sword"); + .whenTargetNamed('sword'); - container.bind(TYPE.Weapon).to(Shuriken) + container + .bind(TYPE.Weapon) + .to(Shuriken) .inRequestScope() - .whenTargetNamed("throwable"); + .whenTargetNamed('throwable'); container.bind(TYPE.Warrior).to(Samurai); - const samurai1 = container.get(TYPE.Warrior); - const samurai2 = container.get(TYPE.Warrior); + const samurai1: Warrior = container.get(TYPE.Warrior); + const samurai2: Warrior = container.get(TYPE.Warrior); // Katana and Shuriken are two instances - expect(samurai1.primaryWeapon.use()).not.to.eql(samurai1.secondaryWeapon.use()); + expect(samurai1.primaryWeapon.use()).not.to.eql( + samurai1.secondaryWeapon.use(), + ); // Shuriken should be one shared instance because scope is request scope - expect(samurai1.secondaryWeapon.use()).to.eql(samurai1.tertiaryWeapon.use()); + expect(samurai1.secondaryWeapon.use()).to.eql( + samurai1.tertiaryWeapon.use(), + ); // Katana and Shuriken are two instances - expect(samurai2.primaryWeapon.use()).not.to.eql(samurai2.secondaryWeapon.use()); + expect(samurai2.primaryWeapon.use()).not.to.eql( + samurai2.secondaryWeapon.use(), + ); // Shuriken should be one shared instance because scope is request scope - expect(samurai2.secondaryWeapon.use()).to.eql(samurai2.tertiaryWeapon.use()); + expect(samurai2.secondaryWeapon.use()).to.eql( + samurai2.tertiaryWeapon.use(), + ); // Two request should use one instances of Katana // for each instance of Samurai because scope is request scope - expect(samurai1.secondaryWeapon.use()).not.to.eql(samurai2.secondaryWeapon.use()); - expect(samurai1.tertiaryWeapon.use()).not.to.eql(samurai2.tertiaryWeapon.use()); - + expect(samurai1.secondaryWeapon.use()).not.to.eql( + samurai2.secondaryWeapon.use(), + ); + expect(samurai1.tertiaryWeapon.use()).not.to.eql( + samurai2.tertiaryWeapon.use(), + ); }); - -}); \ No newline at end of file +}); diff --git a/test/features/resolve_unbinded.test.ts b/test/features/resolve_unbinded.test.ts index c3f4be5e6..dfd991066 100644 --- a/test/features/resolve_unbinded.test.ts +++ b/test/features/resolve_unbinded.test.ts @@ -1,63 +1,67 @@ -import { expect } from "chai"; -import { Container, injectable, interfaces } from "../../src/inversify"; +import { expect } from 'chai'; -describe("Container.prototype.resolve", () => { - - it("Should be able to resolve a class that has not binded", () => { +import { Container, injectable, interfaces } from '../../src/inversify'; +describe('Container.prototype.resolve', () => { + it('Should be able to resolve a class that has not binded', () => { @injectable() class Katana { public hit() { - return "cut!"; + return 'cut!'; } } @injectable() class Ninja implements Ninja { public katana: Katana; - public constructor(katana: Katana) { + constructor(katana: Katana) { this.katana = katana; } - public fight() { return this.katana.hit(); } + public fight() { + return this.katana.hit(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind(Katana).toSelf(); - const tryGet = () => container.get(Ninja); - expect(tryGet).to.throw("No matching bindings found for serviceIdentifier: Ninja"); + const tryGet: () => Ninja = () => container.get(Ninja); + expect(tryGet).to.throw( + 'No matching bindings found for serviceIdentifier: Ninja', + ); - const ninja = container.resolve(Ninja); - expect(ninja.fight()).to.eql("cut!"); + const ninja: Ninja = container.resolve(Ninja); + expect(ninja.fight()).to.eql('cut!'); expect(container.isBound(Ninja)).to.equal(false); - }); - it("Should be able to resolve a class that has already been bound", () => { + it('Should be able to resolve a class that has already been bound', () => { @injectable() class Katana { public hit() { - return "cut!"; + return 'cut!'; } } @injectable() class Ninja implements Ninja { public katana: Katana; - public constructor(katana: Katana) { + constructor(katana: Katana) { this.katana = katana; } - public fight() { return this.katana.hit(); } + public fight() { + return this.katana.hit(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind(Katana).toSelf(); container.bind(Ninja).toSelf(); - const ninja = container.resolve(Ninja); - expect(ninja.fight()).to.eql("cut!"); - }) - describe("Should use middleware", () => { + const ninja: Ninja = container.resolve(Ninja); + expect(ninja.fight()).to.eql('cut!'); + }); + describe('Should use middleware', () => { interface TestMiddlewareAppliedInCorrectOrder { description: string; applyMiddleware: ( @@ -65,74 +69,110 @@ describe("Container.prototype.resolve", () => { middleware1: interfaces.Middleware, middleware2: interfaces.Middleware, middleware3: interfaces.Middleware, - middleware4: interfaces.Middleware) => void; + middleware4: interfaces.Middleware, + ) => void; } const middlewareOrderTests: TestMiddlewareAppliedInCorrectOrder[] = [ { - applyMiddleware: (container, middleware1, middleware2, middleware3, middleware4) => { - container.applyMiddleware(middleware1, middleware2, middleware3, middleware4); + applyMiddleware: ( + container: interfaces.Container, + middleware1: interfaces.Middleware, + middleware2: interfaces.Middleware, + middleware3: interfaces.Middleware, + middleware4: interfaces.Middleware, + ) => { + container.applyMiddleware( + middleware1, + middleware2, + middleware3, + middleware4, + ); }, - description: "All at once", + description: 'All at once', }, { - applyMiddleware: (container, middleware1, middleware2, middleware3, middleware4) => { + applyMiddleware: ( + container: interfaces.Container, + middleware1: interfaces.Middleware, + middleware2: interfaces.Middleware, + middleware3: interfaces.Middleware, + middleware4: interfaces.Middleware, + ) => { container.applyMiddleware(middleware1, middleware2); container.applyMiddleware(middleware3, middleware4); }, - description: "Two calls", - } + description: 'Two calls', + }, ]; - middlewareOrderTests.forEach((t) => testApplyMiddlewareSameOrder(t)); - function testApplyMiddlewareSameOrder(test: TestMiddlewareAppliedInCorrectOrder) { + middlewareOrderTests.forEach((t: TestMiddlewareAppliedInCorrectOrder) => { + testApplyMiddlewareSameOrder(t); + }); + function testApplyMiddlewareSameOrder( + test: TestMiddlewareAppliedInCorrectOrder, + ) { it(test.description, () => { @injectable() class Katana { public hit() { - return "cut!"; + return 'cut!'; } } @injectable() class Ninja implements Ninja { public katana: Katana; - public constructor(katana: Katana) { + constructor(katana: Katana) { this.katana = katana; } - public fight() { return this.katana.hit(); } + public fight() { + return this.katana.hit(); + } } const middlewareOrder: number[] = []; - const middleware1: interfaces.Middleware = (next) => { - return (args) => { + const middleware1: interfaces.Middleware = (next: interfaces.Next) => { + return (args: interfaces.NextArgs) => { middlewareOrder.push(1); return next(args); }; }; - const middleware2: interfaces.Middleware = (next) => { - return (args) => { + const middleware2: interfaces.Middleware = (next: interfaces.Next) => { + return (args: interfaces.NextArgs) => { middlewareOrder.push(2); return next(args); }; }; - const middleware3: interfaces.Middleware = (next) => { - return (args) => { + const middleware3: interfaces.Middleware = (next: interfaces.Next) => { + return (args: interfaces.NextArgs) => { middlewareOrder.push(3); return next(args); }; }; - const middleware4: interfaces.Middleware = (next) => { - return (args) => { + const middleware4: interfaces.Middleware = (next: interfaces.Next) => { + return (args: interfaces.NextArgs) => { middlewareOrder.push(4); return next(args); }; }; - const resolveContainer = new Container(); + const resolveContainer: Container = new Container(); resolveContainer.bind(Katana).toSelf(); - test.applyMiddleware(resolveContainer, middleware1, middleware2, middleware3, middleware4); - - const getContainer = new Container(); + test.applyMiddleware( + resolveContainer, + middleware1, + middleware2, + middleware3, + middleware4, + ); + + const getContainer: Container = new Container(); getContainer.bind(Katana).toSelf(); getContainer.bind(Ninja).toSelf(); - test.applyMiddleware(getContainer, middleware1, middleware2, middleware3, middleware4); + test.applyMiddleware( + getContainer, + middleware1, + middleware2, + middleware3, + middleware4, + ); resolveContainer.resolve(Ninja); getContainer.get(Ninja); @@ -145,5 +185,4 @@ describe("Container.prototype.resolve", () => { }); } }); - -}); \ No newline at end of file +}); diff --git a/test/features/transitive_bindings.test.ts b/test/features/transitive_bindings.test.ts index c382fc240..6cb64a9da 100644 --- a/test/features/transitive_bindings.test.ts +++ b/test/features/transitive_bindings.test.ts @@ -1,17 +1,21 @@ -import { expect } from "chai"; -import { Container, injectable, multiBindToService } from "../../src/inversify"; +import { expect } from 'chai'; -describe("Transitive bindings", () => { - - it("Should be able to bind to a service", () => { +import { + Container, + injectable, + interfaces, + multiBindToService, +} from '../../src/inversify'; +describe('Transitive bindings', () => { + it('Should be able to bind to a service', () => { @injectable() class MySqlDatabaseTransactionLog { public time: number; public name: string; - public constructor() { + constructor() { this.time = new Date().getTime(); - this.name = "MySqlDatabaseTransactionLog"; + this.name = 'MySqlDatabaseTransactionLog'; } } @@ -27,32 +31,37 @@ describe("Transitive bindings", () => { public name!: string; } - const container = new Container(); + const container: Container = new Container(); container.bind(MySqlDatabaseTransactionLog).toSelf().inSingletonScope(); - container.bind(DatabaseTransactionLog).toService(MySqlDatabaseTransactionLog); + container + .bind(DatabaseTransactionLog) + .toService(MySqlDatabaseTransactionLog); container.bind(TransactionLog).toService(DatabaseTransactionLog); - const mySqlDatabaseTransactionLog = container.get(MySqlDatabaseTransactionLog); - const databaseTransactionLog = container.get(DatabaseTransactionLog); - const transactionLog = container.get(TransactionLog); - - expect(mySqlDatabaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog"); - expect(databaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog"); - expect(transactionLog.name).to.eq("MySqlDatabaseTransactionLog"); + const mySqlDatabaseTransactionLog: MySqlDatabaseTransactionLog = + container.get(MySqlDatabaseTransactionLog); + const databaseTransactionLog: DatabaseTransactionLog = container.get( + DatabaseTransactionLog, + ); + const transactionLog: TransactionLog = container.get(TransactionLog); + + expect(mySqlDatabaseTransactionLog.name).to.eq( + 'MySqlDatabaseTransactionLog', + ); + expect(databaseTransactionLog.name).to.eq('MySqlDatabaseTransactionLog'); + expect(transactionLog.name).to.eq('MySqlDatabaseTransactionLog'); expect(mySqlDatabaseTransactionLog.time).to.eq(databaseTransactionLog.time); expect(databaseTransactionLog.time).to.eq(transactionLog.time); - }); - it("Should be able to bulk bind to a service", () => { - + it('Should be able to bulk bind to a service', () => { @injectable() class MySqlDatabaseTransactionLog { public time: number; public name: string; - public constructor() { + constructor() { this.time = new Date().getTime(); - this.name = "MySqlDatabaseTransactionLog"; + this.name = 'MySqlDatabaseTransactionLog'; } } @@ -68,21 +77,27 @@ describe("Transitive bindings", () => { public name!: string; } - const container = new Container(); - const mbts = multiBindToService(container); + const container: Container = new Container(); + const mbts: ( + service: interfaces.ServiceIdentifier, + ) => (...types: interfaces.ServiceIdentifier[]) => void = + multiBindToService(container); container.bind(MySqlDatabaseTransactionLog).toSelf().inSingletonScope(); mbts(MySqlDatabaseTransactionLog)(DatabaseTransactionLog, TransactionLog); - const mySqlDatabaseTransactionLog = container.get(MySqlDatabaseTransactionLog); - const databaseTransactionLog = container.get(DatabaseTransactionLog); - const transactionLog = container.get(TransactionLog); - - expect(mySqlDatabaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog"); - expect(databaseTransactionLog.name).to.eq("MySqlDatabaseTransactionLog"); - expect(transactionLog.name).to.eq("MySqlDatabaseTransactionLog"); + const mySqlDatabaseTransactionLog: MySqlDatabaseTransactionLog = + container.get(MySqlDatabaseTransactionLog); + const databaseTransactionLog: DatabaseTransactionLog = container.get( + DatabaseTransactionLog, + ); + const transactionLog: TransactionLog = container.get(TransactionLog); + + expect(mySqlDatabaseTransactionLog.name).to.eq( + 'MySqlDatabaseTransactionLog', + ); + expect(databaseTransactionLog.name).to.eq('MySqlDatabaseTransactionLog'); + expect(transactionLog.name).to.eq('MySqlDatabaseTransactionLog'); expect(mySqlDatabaseTransactionLog.time).to.eq(databaseTransactionLog.time); expect(databaseTransactionLog.time).to.eq(transactionLog.time); - }); - -}); \ No newline at end of file +}); diff --git a/test/inversify.test.ts b/test/inversify.test.ts index 6a14d3ee1..562d124ee 100644 --- a/test/inversify.test.ts +++ b/test/inversify.test.ts @@ -1,68 +1,83 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */ import { expect } from 'chai'; + +import { DecoratorTarget } from '../src/annotation/decorator_utils'; import * as ERROR_MSGS from '../src/constants/error_msgs'; import { interfaces } from '../src/interfaces/interfaces'; -import { Container, ContainerModule, decorate, inject, injectable, LazyServiceIdentifier, multiInject, named, tagged, targetName, typeConstraint, unmanaged } from '../src/inversify'; +import { + Container, + ContainerModule, + decorate, + inject, + injectable, + LazyServiceIdentifier, + multiInject, + named, + tagged, + targetName, + typeConstraint, + unmanaged, +} from '../src/inversify'; describe('InversifyJS', () => { - it('Should be able to resolve and inject dependencies', () => { - - interface Ninja { + interface NinjaInterface { fight(): string; sneak(): string; } - interface Katana { + interface KatanaInterface { hit(): string; } - interface Shuriken { + interface ShurikenInterface { throw(): string; } @injectable() - class Katana implements Katana { + class Katana implements KatanaInterface { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken implements ShurikenInterface { public throw() { return 'hit!'; } } @injectable() - class Ninja implements Ninja { + class Ninja implements NinjaInterface { + private readonly _katana: KatanaInterface; + private readonly _shuriken: ShurikenInterface; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor( - @inject('Katana') katana: Katana, - @inject('Shuriken') shuriken: Shuriken + constructor( + @inject('Katana') katana: KatanaInterface, + @inject('Shuriken') shuriken: ShurikenInterface, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); - container.bind('Ninja').to(Ninja); - container.bind('Katana').to(Katana); - container.bind('Shuriken').to(Shuriken); + const container: Container = new Container(); + container.bind('Ninja').to(Ninja); + container.bind('Katana').to(Katana); + container.bind('Shuriken').to(Shuriken); - const ninja = container.get('Ninja'); + const ninja: NinjaInterface = container.get('Ninja'); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - }); it('Should be able to do setter injection and property injection', () => { @@ -81,36 +96,40 @@ describe('InversifyJS', () => { @injectable() class Ninja { + @inject(Katana) + public katana!: Katana; private _shuriken!: Shuriken; @inject(Shuriken) public set Shuriken(shuriken: Shuriken) { this._shuriken = shuriken; } - @inject(Katana) - public katana!: Katana; - public sneak() { return this._shuriken.throw(); } - public fight() { return this.katana.hit(); } + public sneak() { + return this._shuriken.throw(); + } + public fight() { + return this.katana.hit(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(Ninja); container.bind(Shuriken).toSelf(); container.bind(Katana).toSelf(); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja.sneak()).to.eql('hit!'); expect(ninja.fight()).to.eql('cut!'); }); it('Should be able to resolve and inject dependencies in VanillaJS', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { + Blowgun: 'Blowgun', Katana: 'Katana', Ninja: 'Ninja', Shuriken: 'Shuriken', - Blowgun: 'Blowgun' }; class Blowgun { @@ -132,22 +151,28 @@ describe('InversifyJS', () => { } class Ninja { - public _katana: Katana; public _shuriken: Shuriken; public _blowgun!: Blowgun; - public constructor(katana: Katana, shuriken: Shuriken) { + constructor(katana: Katana, shuriken: Shuriken) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - public poisonDart() { return this._blowgun.blow(); } public set blowgun(blowgun: Blowgun) { this._blowgun = blowgun; } + + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } + public poisonDart() { + return this._blowgun.blow(); + } } decorate(injectable(), Katana); @@ -158,22 +183,20 @@ describe('InversifyJS', () => { decorate(inject(TYPES.Shuriken), Ninja, 1); decorate(inject(TYPES.Blowgun), Ninja.prototype, 'blowgun'); - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Ninja).to(Ninja); container.bind(TYPES.Katana).to(Katana); container.bind(TYPES.Shuriken).to(Shuriken); container.bind(TYPES.Blowgun).to(Blowgun); - const ninja = container.get(TYPES.Ninja); + const ninja: Ninja = container.get(TYPES.Ninja); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); expect(ninja.poisonDart()).eql('poison!'); - }); it('Should be able to use classes as runtime identifiers', () => { - @injectable() class Katana { public hit() { @@ -190,231 +213,210 @@ describe('InversifyJS', () => { @injectable() class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor(katana: Katana, shuriken: Shuriken) { + constructor(katana: Katana, shuriken: Shuriken) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind(Ninja).to(Ninja); container.bind(Katana).to(Katana); container.bind(Shuriken).to(Shuriken); - const ninja = container.get(Ninja); + const ninja: Ninja = container.get(Ninja); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - }); it('Should be able to use Symbols as runtime identifiers', () => { - - interface Ninja { - fight(): string; - sneak(): string; - } - - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } } + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Katana: Symbol.for('Katana'), Ninja: Symbol.for('Ninja'), - Shuriken: Symbol.for('Shuriken') + Shuriken: Symbol.for('Shuriken'), }; @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - private _shuriken: Shuriken; + class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - public constructor( + constructor( @inject(TYPES.Katana) katana: Katana, - @inject(TYPES.Shuriken) shuriken: Shuriken + @inject(TYPES.Shuriken) shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Ninja).to(Ninja); container.bind(TYPES.Katana).to(Katana); container.bind(TYPES.Shuriken).to(Shuriken); - const ninja = container.get(TYPES.Ninja); + const ninja: Ninja = container.get(TYPES.Ninja); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - }); it('Should be able to wrap Symbols with LazyServiceIdentifier', () => { - - interface Ninja { - fight(): string; - sneak(): string; - } - - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } } + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Katana: Symbol.for('Katana'), Ninja: Symbol.for('Ninja'), - Shuriken: Symbol.for('Shuriken') + Shuriken: Symbol.for('Shuriken'), }; @injectable() class Ninja implements Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor( + constructor( @inject(new LazyServiceIdentifier(() => TYPES.Katana)) katana: Katana, - @inject(new LazyServiceIdentifier(() => TYPES.Shuriken)) shuriken: Shuriken + @inject(new LazyServiceIdentifier(() => TYPES.Shuriken)) + shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Ninja).to(Ninja); container.bind(TYPES.Katana).to(Katana); container.bind(TYPES.Shuriken).to(Shuriken); - const ninja = container.get(TYPES.Ninja); + const ninja: Ninja = container.get(TYPES.Ninja); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - }); it('Should support Container modules', () => { - - interface Ninja { - fight(): string; - sneak(): string; - } - - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } } @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - private _shuriken: Shuriken; + class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - public constructor(@inject('Katana') katana: Katana, @inject('Shuriken') shuriken: Shuriken) { + constructor( + @inject('Katana') katana: Katana, + @inject('Shuriken') shuriken: Shuriken, + ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const warriors = new ContainerModule((bind: interfaces.Bind) => { - bind('Ninja').to(Ninja); - }); + const warriors: ContainerModule = new ContainerModule( + (bind: interfaces.Bind) => { + bind('Ninja').to(Ninja); + }, + ); - const weapons = new ContainerModule((bind: interfaces.Bind) => { - bind('Katana').to(Katana); - bind('Shuriken').to(Shuriken); - }); + const weapons: ContainerModule = new ContainerModule( + (bind: interfaces.Bind) => { + bind('Katana').to(Katana); + bind('Shuriken').to(Shuriken); + }, + ); - const container = new Container(); + const container: Container = new Container(); // load container.load(warriors, weapons); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - const tryGetNinja = () => { container.get('Ninja'); }; - const tryGetKatana = () => { container.get('Katana'); }; - const tryGetShuruken = () => { container.get('Shuriken'); }; + const tryGetNinja: () => void = () => { + container.get('Ninja'); + }; + const tryGetKatana: () => void = () => { + container.get('Katana'); + }; + const tryGetShuruken: () => void = () => { + container.get('Shuriken'); + }; // unload container.unload(warriors); @@ -426,142 +428,120 @@ describe('InversifyJS', () => { expect(tryGetNinja).to.throw(ERROR_MSGS.NOT_REGISTERED); expect(tryGetKatana).to.throw(ERROR_MSGS.NOT_REGISTERED); expect(tryGetShuruken).to.throw(ERROR_MSGS.NOT_REGISTERED); - }); it('Should support control over the scope of the dependencies', () => { - - interface Ninja { - fight(): string; - sneak(): string; - } - - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { private _usageCount: number; - public constructor() { + constructor() { this._usageCount = 0; } public hit() { this._usageCount = this._usageCount + 1; - return `This katana was used ${this._usageCount} times!`; + return `This katana was used ${this._usageCount.toString()} times!`; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { private _shurikenCount: number; - public constructor() { + constructor() { this._shurikenCount = 10; } public throw() { this._shurikenCount = this._shurikenCount - 1; - return `Only ${this._shurikenCount} items left!`; + return `Only ${this._shurikenCount.toString()} items left!`; } } @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - private _shuriken: Shuriken; + class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') katana: Katana, - @inject('Shuriken') shuriken: Shuriken + @inject('Shuriken') shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(Ninja); container.bind('Katana').to(Katana).inSingletonScope(); container.bind('Shuriken').to(Shuriken); - const ninja1 = container.get('Ninja'); + const ninja1: Ninja = container.get('Ninja'); expect(ninja1.fight()).eql('This katana was used 1 times!'); expect(ninja1.fight()).eql('This katana was used 2 times!'); expect(ninja1.sneak()).eql('Only 9 items left!'); expect(ninja1.sneak()).eql('Only 8 items left!'); - const ninja2 = container.get('Ninja'); + const ninja2: Ninja = container.get('Ninja'); expect(ninja2.fight()).eql('This katana was used 3 times!'); expect(ninja2.sneak()).eql('Only 9 items left!'); - }); it('Should support the injection of classes to itself', () => { - - const heroName = 'superman'; + const heroName: string = 'superman'; @injectable() class Hero { public name: string; - public constructor() { + constructor() { this.name = heroName; } } - const container = new Container(); + const container: Container = new Container(); container.bind(Hero).toSelf(); - const hero = container.get(Hero); + const hero: Hero = container.get(Hero); expect(hero.name).eql(heroName); - }); it('Should support the injection of constant values', () => { - interface Warrior { name: string; } + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { - Warrior: 'Warrior' + Warrior: 'Warrior', }; - const heroName = 'superman'; + const heroName: string = 'superman'; @injectable() class Hero implements Warrior { public name: string; - public constructor() { + constructor() { this.name = heroName; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).toConstantValue(new Hero()); - const hero = container.get(TYPES.Warrior); + const hero: Warrior = container.get(TYPES.Warrior); expect(hero.name).eql(heroName); - }); it('Should support the injection of dynamic values', () => { - - interface UseDate { - doSomething(): Date; - } - @injectable() - class UseDate implements UseDate { + class UseDate { public currentDate: Date; - public constructor(@inject('Date') currentDate: Date) { + constructor(@inject('Date') currentDate: Date) { this.currentDate = currentDate; } public doSomething() { @@ -569,40 +549,36 @@ describe('InversifyJS', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('UseDate').to(UseDate); - container.bind('Date').toDynamicValue((context: interfaces.Context) => new Date()); + container + .bind('Date') + .toDynamicValue((_context: interfaces.Context) => new Date()); - const subject1 = container.get('UseDate'); - const subject2 = container.get('UseDate'); + const subject1: UseDate = container.get('UseDate'); + const subject2: UseDate = container.get('UseDate'); expect(subject1.doSomething() === subject2.doSomething()).eql(false); container.unbind('Date'); container.bind('Date').toConstantValue(new Date()); - const subject3 = container.get('UseDate'); - const subject4 = container.get('UseDate'); + const subject3: UseDate = container.get('UseDate'); + const subject4: UseDate = container.get('UseDate'); expect(subject3.doSomething() === subject4.doSomething()).eql(true); - }); it('Should support the injection of Functions', () => { - - const ninjaId = 'Ninja'; - const longDistanceWeaponId = 'LongDistanceWeapon'; - const shortDistanceWeaponFactoryId = 'ShortDistanceWeaponFactory'; + const ninjaId: string = 'Ninja'; + const longDistanceWeaponId: string = 'LongDistanceWeapon'; + const shortDistanceWeaponFactoryId: string = 'ShortDistanceWeaponFactory'; type ShortDistanceWeaponFactory = () => ShortDistanceWeapon; - interface KatanaBlade { } - @injectable() - class KatanaBlade implements KatanaBlade { } - - interface KatanaHandler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements KatanaHandler { } + class KatanaHandler {} interface ShortDistanceWeapon { handler: KatanaHandler; @@ -613,63 +589,65 @@ describe('InversifyJS', () => { class Katana implements ShortDistanceWeapon { public handler: KatanaHandler; public blade: KatanaBlade; - public constructor(handler: KatanaHandler, blade: KatanaBlade) { + constructor(handler: KatanaHandler, blade: KatanaBlade) { this.handler = handler; this.blade = blade; } } - interface LongDistanceWeapon { } - @injectable() - class Shuriken implements LongDistanceWeapon { } + class Shuriken {} interface Warrior { shortDistanceWeaponFactory: ShortDistanceWeaponFactory; - longDistanceWeapon: LongDistanceWeapon; + longDistanceWeapon: Shuriken; } @injectable() class Ninja implements Warrior { public shortDistanceWeaponFactory: ShortDistanceWeaponFactory; - public longDistanceWeapon: LongDistanceWeapon; - public constructor( - @inject(shortDistanceWeaponFactoryId) @targetName('katana') shortDistanceWeaponFactory: ShortDistanceWeaponFactory, - @inject(longDistanceWeaponId) @targetName('shuriken') longDistanceWeapon: LongDistanceWeapon + public longDistanceWeapon: Shuriken; + constructor( + @inject(shortDistanceWeaponFactoryId) + @targetName('katana') + shortDistanceWeaponFactory: ShortDistanceWeaponFactory, + @inject(longDistanceWeaponId) + @targetName('shuriken') + longDistanceWeapon: Shuriken, ) { this.shortDistanceWeaponFactory = shortDistanceWeaponFactory; this.longDistanceWeapon = longDistanceWeapon; } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind(longDistanceWeaponId).to(Shuriken); + container.bind(longDistanceWeaponId).to(Shuriken); - const katanaFactory = function () { + const katanaFactory: () => Katana = function () { return new Katana(new KatanaHandler(), new KatanaBlade()); }; - container.bind(shortDistanceWeaponFactoryId).toFunction(katanaFactory); // IMPORTANT! - const ninja = container.get(ninjaId); + container + .bind(shortDistanceWeaponFactoryId) + .toFunction(katanaFactory); + const ninja: Ninja = container.get(ninjaId); expect(ninja instanceof Ninja).eql(true); expect(typeof ninja.shortDistanceWeaponFactory === 'function').eql(true); expect(ninja.shortDistanceWeaponFactory() instanceof Katana).eql(true); - expect(ninja.shortDistanceWeaponFactory().handler instanceof KatanaHandler).eql(true); - expect(ninja.shortDistanceWeaponFactory().blade instanceof KatanaBlade).eql(true); + expect( + ninja.shortDistanceWeaponFactory().handler instanceof KatanaHandler, + ).eql(true); + expect(ninja.shortDistanceWeaponFactory().blade instanceof KatanaBlade).eql( + true, + ); expect(ninja.longDistanceWeapon instanceof Shuriken).eql(true); - }); it('Should support the injection of class constructors', () => { - - interface Katana { - hit(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } @@ -677,45 +655,38 @@ describe('InversifyJS', () => { @injectable() class Ninja { + private readonly _katana: Katana; - private _katana: Katana; - - public constructor( - @inject('Newable') katana: interfaces.Newable + constructor( + @inject('Newable') katana: interfaces.Newable, ) { this._katana = new katana(); } - public fight() { return this._katana.hit(); } + public fight() { + return this._katana.hit(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(Ninja); - container.bind>('Newable').toConstructor(Katana); + container + .bind>('Newable') + .toConstructor(Katana); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja.fight()).eql('cut!'); - }); it('Should support the injection of user defined factories', () => { - interface Ninja { fight(): string; sneak(): string; } - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } @@ -730,40 +701,43 @@ describe('InversifyJS', () => { @injectable() class NinjaWithUserDefinedFactory implements Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor( + constructor( @inject('Factory') katanaFactory: () => Katana, - @inject('Shuriken') shuriken: Shuriken + @inject('Shuriken') shuriken: Shuriken, ) { this._katana = katanaFactory(); this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(NinjaWithUserDefinedFactory); container.bind('Shuriken').to(Shuriken); container.bind('Katana').to(Katana); - container.bind>('Factory').toFactory((context) => - () => - context.container.get('Katana')); + container + .bind>('Factory') + .toFactory( + (context: interfaces.Context) => () => + context.container.get('Katana'), + ); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - }); it('Should support the injection of user defined factories with args', () => { - interface Ninja { fight(): string; sneak(): string; @@ -789,49 +763,55 @@ describe('InversifyJS', () => { @injectable() class NinjaWithUserDefinedFactory implements Ninja { + private readonly _katana: Weapon; + private readonly _shuriken: Weapon; - private _katana: Weapon; - private _shuriken: Weapon; - - public constructor( - @inject('Factory') weaponFactory: (throwable: boolean) => Weapon + constructor( + @inject('Factory') + weaponFactory: (throwable: boolean) => Weapon, ) { this._katana = weaponFactory(false); this._shuriken = weaponFactory(true); } - public fight() { return this._katana.use(); } - public sneak() { return this._shuriken.use(); } - + public fight() { + return this._katana.use(); + } + public sneak() { + return this._shuriken.use(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(NinjaWithUserDefinedFactory); - container.bind('Weapon').to(Shuriken).whenTargetTagged('throwable', true); - container.bind('Weapon').to(Katana).whenTargetTagged('throwable', false); - - container.bind>('Factory').toFactory((context) => - (throwable: boolean) => - context.container.getTagged('Weapon', 'throwable', throwable)); - - const ninja = container.get('Ninja'); + container + .bind('Weapon') + .to(Shuriken) + .whenTargetTagged('throwable', true); + container + .bind('Weapon') + .to(Katana) + .whenTargetTagged('throwable', false); + + container + .bind>('Factory') + .toFactory< + Weapon, + [boolean] + >((context: interfaces.Context) => (throwable: boolean) => context.container.getTagged('Weapon', 'throwable', throwable)); + + const ninja: Ninja = container.get('Ninja'); expect(ninja.fight()).eql('katana!'); expect(ninja.sneak()).eql('shuriken!'); - }); it('Should support the injection of user defined factories with partial application', () => { - - interface InjectorPump { } - @injectable() - class InjectorPump implements InjectorPump { } - - interface SparkPlugs { } + class InjectorPump {} @injectable() - class SparkPlugs implements SparkPlugs { } + class SparkPlugs {} class Engine { public displacement!: number | null; @@ -840,10 +820,8 @@ describe('InversifyJS', () => { @injectable() class DieselEngine implements Engine { public displacement: number | null; - private _injectorPump: InjectorPump; - public constructor( - @inject('InjectorPump') injectorPump: InjectorPump - ) { + private readonly _injectorPump: InjectorPump; + constructor(@inject('InjectorPump') injectorPump: InjectorPump) { this._injectorPump = injectorPump; this.displacement = null; } @@ -855,10 +833,8 @@ describe('InversifyJS', () => { @injectable() class PetrolEngine implements Engine { public displacement: number | null; - private _sparkPlugs: SparkPlugs; - public constructor( - @inject('SparkPlugs') sparkPlugs: SparkPlugs - ) { + private readonly _sparkPlugs: SparkPlugs; + constructor(@inject('SparkPlugs') sparkPlugs: SparkPlugs) { this._sparkPlugs = sparkPlugs; this.displacement = null; } @@ -873,9 +849,10 @@ describe('InversifyJS', () => { @injectable() class DieselCarFactory implements CarFactory { - private _dieselFactory: (displacement: number) => Engine; - public constructor( - @inject('Factory') factory: (category: string) => (displacement: number) => Engine + private readonly _dieselFactory: (displacement: number) => Engine; + constructor( + @inject('Factory') + factory: (category: string) => (displacement: number) => Engine, ) { this._dieselFactory = factory('diesel'); } @@ -884,53 +861,52 @@ describe('InversifyJS', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('SparkPlugs').to(SparkPlugs); container.bind('InjectorPump').to(InjectorPump); container.bind('Engine').to(PetrolEngine).whenTargetNamed('petrol'); container.bind('Engine').to(DieselEngine).whenTargetNamed('diesel'); - container.bind>('Factory').toFactory((context: interfaces.Context) => - (theNamed: string) => (displacement: number) => { - const theEngine = context.container.getNamed('Engine', theNamed); + container + .bind>('Factory') + .toFactory< + Engine, + [string], + [number] + >((context: interfaces.Context) => (theNamed: string) => (displacement: number) => { + const theEngine: Engine = context.container.getNamed( + 'Engine', + theNamed, + ); theEngine.displacement = displacement; return theEngine; }); container.bind('DieselCarFactory').to(DieselCarFactory); - const dieselCarFactory = container.get('DieselCarFactory'); - const engine = dieselCarFactory.createEngine(300); + const dieselCarFactory: CarFactory = + container.get('DieselCarFactory'); + const engine: Engine = dieselCarFactory.createEngine(300); expect(engine.displacement).eql(300); expect(engine instanceof DieselEngine).eql(true); - }); it('Should support the injection of auto factories', () => { - interface Ninja { fight(): string; sneak(): string; } - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } @@ -938,61 +914,54 @@ describe('InversifyJS', () => { @injectable() class NinjaWithAutoFactory implements Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor( + constructor( @inject('Factory') katanaAutoFactory: () => Katana, - @inject('Shuriken') shuriken: Shuriken + @inject('Shuriken') shuriken: Shuriken, ) { this._katana = katanaAutoFactory(); this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(NinjaWithAutoFactory); container.bind('Shuriken').to(Shuriken); container.bind('Katana').to(Katana); - container.bind>('Factory').toAutoFactory('Katana'); + container + .bind>('Factory') + .toAutoFactory('Katana'); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - }); it('Should support the injection of auto named factories', () => { - interface Ninja { fight(): string; sneak(): string; } - interface Weapon { } - - interface Katana extends Weapon { - hit(): string; - } - - interface Shuriken extends Weapon { - throw(): string; - } @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } @@ -1000,39 +969,42 @@ describe('InversifyJS', () => { @injectable() class NinjaWithAutoNamedFactory implements Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor( - @inject('Factory') weaponFactory: (named: string) => TWeapon + constructor( + @inject('Factory') + weaponFactory: (named: string) => TWeapon, ) { this._katana = weaponFactory('katana'); this._shuriken = weaponFactory('shuriken'); } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(NinjaWithAutoNamedFactory); container.bind('Shuriken').to(Shuriken); container.bind('Katana').to(Katana); - container.bind('Weapon').to(Katana).whenTargetNamed('katana'); - container.bind('Weapon').to(Shuriken).whenTargetNamed('shuriken'); - container.bind>('Factory').toAutoNamedFactory('Weapon'); + container.bind('Weapon').to(Katana).whenTargetNamed('katana'); + container.bind('Weapon').to(Shuriken).whenTargetNamed('shuriken'); + container + .bind>('Factory') + .toAutoNamedFactory('Weapon'); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja.fight()).eql('cut!'); expect(ninja.sneak()).eql('hit!'); - }); - it('Should support the injection of providers', (done) => { - + it('Should support the injection of providers', (done: Mocha.Done) => { type KatanaProvider = () => Promise; interface Ninja { @@ -1040,12 +1012,8 @@ describe('InversifyJS', () => { katanaProvider: KatanaProvider; } - interface Katana { - hit(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } @@ -1053,48 +1021,45 @@ describe('InversifyJS', () => { @injectable() class NinjaWithProvider implements Ninja { - public katana: Katana | null; public katanaProvider: KatanaProvider; - public constructor( - @inject('Provider') katanaProvider: KatanaProvider - ) { + constructor(@inject('Provider') katanaProvider: KatanaProvider) { this.katanaProvider = katanaProvider; this.katana = null; } - } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(NinjaWithProvider); container.bind('Katana').to(Katana); - container.bind('Provider').toProvider((context: interfaces.Context) => - () => - new Promise((resolve) => { - const katana = context.container.get('Katana'); + container.bind('Provider').toProvider( + (context: interfaces.Context) => async () => + new Promise((resolve: (value: Katana) => void) => { + const katana: Katana = context.container.get('Katana'); resolve(katana); - })); + }), + ); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); - ninja.katanaProvider() - .then((katana) => { + ninja + .katanaProvider() + .then((katana: Katana) => { ninja.katana = katana; expect(ninja.katana.hit()).eql('cut!'); done(); }) - .catch((e) => { /* do nothing */ }); - + .catch((_e: unknown) => { + /* do nothing */ + }); }); describe('Injection of multiple values with string as keys', () => { - it('Should support the injection of multiple values', () => { - - const warriorId = 'Warrior'; - const weaponId = 'Weapon'; + const warriorId: string = 'Warrior'; + const weaponId: string = 'Weapon'; interface Weapon { name: string; @@ -1102,12 +1067,12 @@ describe('InversifyJS', () => { @injectable() class Katana implements Weapon { - public name = 'Katana'; + public name: string = 'Katana'; } @injectable() class Shuriken implements Weapon { - public name = 'Shuriken'; + public name: string = 'Shuriken'; } interface Warrior { @@ -1119,77 +1084,64 @@ describe('InversifyJS', () => { class Ninja implements Warrior { public katana: Weapon; public shuriken: Weapon; - public constructor(@multiInject(weaponId) weapons: Weapon[]) { + constructor(@multiInject(weaponId) weapons: Weapon[]) { this.katana = weapons[0] as Weapon; this.shuriken = weapons[1] as Weapon; } } - const container = new Container(); + const container: Container = new Container(); container.bind(warriorId).to(Ninja); container.bind(weaponId).to(Katana); container.bind(weaponId).to(Shuriken); - const ninja = container.get(warriorId); + const ninja: Warrior = container.get(warriorId); expect(ninja.katana.name).eql('Katana'); expect(ninja.shuriken.name).eql('Shuriken'); // if only one value is bound to Weapon - const container2 = new Container(); + const container2: Container = new Container(); container2.bind(warriorId).to(Ninja); container2.bind(weaponId).to(Katana); - const ninja2 = container2.get(warriorId); + const ninja2: Ninja = container2.get(warriorId); expect(ninja2.katana.name).eql('Katana'); - }); it('Should support the injection of multiple values with nested inject', () => { - - interface Ninja { - fight(): string; - sneak(): string; - } - - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } } @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - private _shuriken: Shuriken; + class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') katana: Katana, - @inject('Shuriken') shuriken: Shuriken + @inject('Shuriken') shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } interface School { @@ -1202,37 +1154,33 @@ describe('InversifyJS', () => { public ninjaMaster: Ninja; public student: Ninja; - public constructor( - @multiInject('Ninja') ninja: Ninja[] - ) { + constructor(@multiInject('Ninja') ninja: Ninja[]) { this.ninjaMaster = ninja[0] as Ninja; this.student = ninja[1] as Ninja; } } - const container = new Container(); + const container: Container = new Container(); container.bind('Katana').to(Katana); container.bind('Shuriken').to(Shuriken); container.bind('Ninja').to(Ninja); container.bind('Ninja').to(Ninja); container.bind('School').to(NinjaSchool); - const ninjaSchool = container.get('School'); + const ninjaSchool: School = container.get('School'); expect(ninjaSchool.ninjaMaster.fight()).eql('cut!'); expect(ninjaSchool.ninjaMaster.sneak()).eql('hit!'); expect(ninjaSchool.student.fight()).eql('cut!'); expect(ninjaSchool.student.sneak()).eql('hit!'); - }); it('Should support the injection of multiple values with nested multiInject', () => { - - const warriorId = 'Warrior'; - const swordId = 'Sword'; - const shurikenId = 'Shuriken'; - const schoolId = 'School'; - const organisationId = 'Organisation'; + const warriorId: string = 'Warrior'; + const swordId: string = 'Sword'; + const shurikenId: string = 'Shuriken'; + const schoolId: string = 'School'; + const organisationId: string = 'Organisation'; interface Warrior { fight(): string; @@ -1243,10 +1191,6 @@ describe('InversifyJS', () => { hit(): string; } - interface Shuriken { - throw(): string; - } - @injectable() class Katana implements Sword { public hit() { @@ -1255,7 +1199,7 @@ describe('InversifyJS', () => { } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } @@ -1263,21 +1207,23 @@ describe('InversifyJS', () => { @injectable() class Ninja implements Warrior { + private readonly _katana: Sword; + private readonly _shuriken: Shuriken; - private _katana: Sword; - private _shuriken: Shuriken; - - public constructor( + constructor( @inject(swordId) katana: Sword, - @inject(shurikenId) shuriken: Shuriken + @inject(shurikenId) shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } interface School { @@ -1287,13 +1233,10 @@ describe('InversifyJS', () => { @injectable() class NinjaSchool implements School { - public ninjaMaster: Warrior; public student: Warrior; - public constructor( - @multiInject(warriorId) ninjas: Ninja[] - ) { + constructor(@multiInject(warriorId) ninjas: Ninja[]) { this.ninjaMaster = ninjas[0] as Ninja; this.student = ninjas[1] as Ninja; } @@ -1307,14 +1250,12 @@ describe('InversifyJS', () => { class NinjaOrganisation implements Organisation { public schools: School[]; - public constructor( - @multiInject(schoolId) schools: School[] - ) { + constructor(@multiInject(schoolId) schools: School[]) { this.schools = schools; } } - const container = new Container(); + const container: Container = new Container(); container.bind(swordId).to(Katana); container.bind(shurikenId).to(Shuriken); container.bind(warriorId).to(Ninja); @@ -1323,23 +1264,24 @@ describe('InversifyJS', () => { container.bind(schoolId).to(NinjaSchool); container.bind(organisationId).to(NinjaOrganisation); - const ninjaOrganisation = container.get(organisationId); + const ninjaOrganisation: Organisation = + container.get(organisationId); - for (let i = 0; i < 2; i++) { - const ithNinjaOrganizationSchool = ninjaOrganisation.schools[i] as School; + for (let i: number = 0; i < 2; i++) { + const ithNinjaOrganizationSchool: School = ninjaOrganisation.schools[ + i + ] as School; expect(ithNinjaOrganizationSchool.ninjaMaster.fight()).eql('cut!'); expect(ithNinjaOrganizationSchool.ninjaMaster.sneak()).eql('hit!'); expect(ithNinjaOrganizationSchool.student.fight()).eql('cut!'); expect(ithNinjaOrganizationSchool.student.sneak()).eql('hit!'); } - }); }); describe('Injection of multiple values with class as keys', () => { it('Should support the injection of multiple values when using classes as keys', () => { - @injectable() class Weapon { public name!: string; @@ -1347,7 +1289,7 @@ describe('InversifyJS', () => { @injectable() class Katana extends Weapon { - public constructor() { + constructor() { super(); this.name = 'Katana'; } @@ -1355,7 +1297,7 @@ describe('InversifyJS', () => { @injectable() class Shuriken extends Weapon { - public constructor() { + constructor() { super(); this.name = 'Shuriken'; } @@ -1365,33 +1307,31 @@ describe('InversifyJS', () => { class Ninja { public katana: Weapon; public shuriken: Weapon; - public constructor(@multiInject(Weapon) weapons: Weapon[]) { + constructor(@multiInject(Weapon) weapons: Weapon[]) { this.katana = weapons[0] as Weapon; this.shuriken = weapons[1] as Weapon; } } - const container = new Container(); + const container: Container = new Container(); container.bind(Ninja).to(Ninja); container.bind(Weapon).to(Katana); container.bind(Weapon).to(Shuriken); - const ninja = container.get(Ninja); + const ninja: Ninja = container.get(Ninja); expect(ninja.katana.name).eql('Katana'); expect(ninja.shuriken.name).eql('Shuriken'); // if only one value is bound to Weapon - const container2 = new Container(); + const container2: Container = new Container(); container2.bind(Ninja).to(Ninja); container2.bind(Weapon).to(Katana); - const ninja2 = container2.get(Ninja); + const ninja2: Ninja = container2.get(Ninja); expect(ninja2.katana.name).eql('Katana'); - }); it('Should support the injection of multiple values with nested inject', () => { - @injectable() class Katana { public hit() { @@ -1408,21 +1348,20 @@ describe('InversifyJS', () => { @injectable() class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor( - katana: Katana, - shuriken: Shuriken - ) { + constructor(katana: Katana, shuriken: Shuriken) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } @injectable() @@ -1430,32 +1369,28 @@ describe('InversifyJS', () => { public ninjaMaster: Ninja; public student: Ninja; - public constructor( - @multiInject(Ninja) ninja: Ninja[] - ) { + constructor(@multiInject(Ninja) ninja: Ninja[]) { this.ninjaMaster = ninja[0] as Ninja; this.student = ninja[1] as Ninja; } } - const container = new Container(); + const container: Container = new Container(); container.bind(Katana).to(Katana); container.bind(Shuriken).to(Shuriken); container.bind(Ninja).to(Ninja); container.bind(Ninja).to(Ninja); container.bind(NinjaSchool).to(NinjaSchool); - const ninjaSchool = container.get(NinjaSchool); + const ninjaSchool: NinjaSchool = container.get(NinjaSchool); expect(ninjaSchool.ninjaMaster.fight()).eql('cut!'); expect(ninjaSchool.ninjaMaster.sneak()).eql('hit!'); expect(ninjaSchool.student.fight()).eql('cut!'); expect(ninjaSchool.student.sneak()).eql('hit!'); - }); it('Should support the injection of multiple values with nested multiInject', () => { - @injectable() class Katana { public hit() { @@ -1472,21 +1407,20 @@ describe('InversifyJS', () => { @injectable() class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - private _katana: Katana; - private _shuriken: Shuriken; - - public constructor( - katana: Katana, - shuriken: Shuriken - ) { + constructor(katana: Katana, shuriken: Shuriken) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } @injectable() @@ -1494,9 +1428,7 @@ describe('InversifyJS', () => { public ninjaMaster: Ninja; public student: Ninja; - public constructor( - @multiInject(Ninja) ninjas: Ninja[] - ) { + constructor(@multiInject(Ninja) ninjas: Ninja[]) { this.ninjaMaster = ninjas[0] as Ninja; this.student = ninjas[1] as Ninja; } @@ -1506,43 +1438,43 @@ describe('InversifyJS', () => { class NinjaOrganisation { public schools: NinjaSchool[]; - public constructor( - @multiInject(NinjaSchool) schools: NinjaSchool[] - ) { + constructor(@multiInject(NinjaSchool) schools: NinjaSchool[]) { this.schools = schools; } } - const container = new Container(); + const container: Container = new Container(); container.bind(Katana).to(Katana); container.bind(Shuriken).to(Shuriken); container.bind(Ninja).to(Ninja); container.bind(Ninja).to(Ninja); container.bind(NinjaSchool).to(NinjaSchool); container.bind(NinjaSchool).to(NinjaSchool); - container.bind(NinjaOrganisation).to(NinjaOrganisation); + container + .bind(NinjaOrganisation) + .to(NinjaOrganisation); - const ninjaOrganisation = container.get(NinjaOrganisation); + const ninjaOrganisation: NinjaOrganisation = + container.get(NinjaOrganisation); - for (let i = 0; i < 2; i++) { - const ithNinjaOrganizationSchool = ninjaOrganisation.schools[i] as NinjaSchool; + for (let i: number = 0; i < 2; i++) { + const ithNinjaOrganizationSchool: NinjaSchool = ninjaOrganisation + .schools[i] as NinjaSchool; expect(ithNinjaOrganizationSchool.ninjaMaster.fight()).eql('cut!'); expect(ithNinjaOrganizationSchool.ninjaMaster.sneak()).eql('hit!'); expect(ithNinjaOrganizationSchool.student.fight()).eql('cut!'); expect(ithNinjaOrganizationSchool.student.sneak()).eql('hit!'); } - }); - }); describe('Injection of multiple values with Symbol as keys', () => { it('Should support the injection of multiple values when using Symbols as keys', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Warrior: Symbol.for('Warrior'), - Weapon: Symbol.for('Weapon') + Weapon: Symbol.for('Weapon'), }; interface Weapon { @@ -1551,12 +1483,12 @@ describe('InversifyJS', () => { @injectable() class Katana implements Weapon { - public name = 'Katana'; + public name: string = 'Katana'; } @injectable() class Shuriken implements Weapon { - public name = 'Shuriken'; + public name: string = 'Shuriken'; } interface Warrior { @@ -1568,33 +1500,32 @@ describe('InversifyJS', () => { class Ninja implements Warrior { public katana: Weapon; public shuriken: Weapon; - public constructor(@multiInject(TYPES.Weapon) weapons: Weapon[]) { + constructor(@multiInject(TYPES.Weapon) weapons: Weapon[]) { this.katana = weapons[0] as Weapon; this.shuriken = weapons[1] as Weapon; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Warrior).to(Ninja); container.bind(TYPES.Weapon).to(Katana); container.bind(TYPES.Weapon).to(Shuriken); - const ninja = container.get(TYPES.Warrior); + const ninja: Ninja = container.get(TYPES.Warrior); expect(ninja.katana.name).eql('Katana'); expect(ninja.shuriken.name).eql('Shuriken'); // if only one value is bound to Weapon - const container2 = new Container(); + const container2: Container = new Container(); container2.bind(TYPES.Warrior).to(Ninja); container2.bind(TYPES.Weapon).to(Katana); - const ninja2 = container2.get(TYPES.Warrior); + const ninja2: Ninja = container2.get(TYPES.Warrior); expect(ninja2.katana.name).eql('Katana'); - }); it('Should support the injection of multiple values with nested inject', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Katana: Symbol.for('Katana'), Ninja: Symbol.for('Ninja'), @@ -1602,50 +1533,39 @@ describe('InversifyJS', () => { Shuriken: Symbol.for('Shuriken'), }; - interface Ninja { - fight(): string; - sneak(): string; - } - - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } } @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - private _shuriken: Shuriken; + class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - public constructor( + constructor( @inject(TYPES.Katana) katana: Katana, - @inject(TYPES.Shuriken) shuriken: Shuriken + @inject(TYPES.Shuriken) shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } interface School { @@ -1658,32 +1578,29 @@ describe('InversifyJS', () => { public ninjaMaster: Ninja; public student: Ninja; - public constructor( - @multiInject(TYPES.Ninja) ninja: Ninja[] - ) { + constructor(@multiInject(TYPES.Ninja) ninja: Ninja[]) { this.ninjaMaster = ninja[0] as Ninja; this.student = ninja[1] as Ninja; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Katana).to(Katana); container.bind(TYPES.Shuriken).to(Shuriken); container.bind(TYPES.Ninja).to(Ninja); container.bind(TYPES.Ninja).to(Ninja); container.bind(TYPES.School).to(NinjaSchool); - const ninjaSchool = container.get(TYPES.School); + const ninjaSchool: School = container.get(TYPES.School); expect(ninjaSchool.ninjaMaster.fight()).eql('cut!'); expect(ninjaSchool.ninjaMaster.sneak()).eql('hit!'); expect(ninjaSchool.student.fight()).eql('cut!'); expect(ninjaSchool.student.sneak()).eql('hit!'); - }); it('Should support the injection of multiple values with nested multiInject', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Katana: Symbol.for('Katana'), Ninja: Symbol.for('Ninja'), @@ -1692,50 +1609,39 @@ describe('InversifyJS', () => { Shuriken: Symbol.for('Shuriken'), }; - interface Ninja { - fight(): string; - sneak(): string; - } - - interface Katana { - hit(): string; - } - - interface Shuriken { - throw(): string; - } - @injectable() - class Katana implements Katana { + class Katana { public hit() { return 'cut!'; } } @injectable() - class Shuriken implements Shuriken { + class Shuriken { public throw() { return 'hit!'; } } @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - private _shuriken: Shuriken; + class Ninja { + private readonly _katana: Katana; + private readonly _shuriken: Shuriken; - public constructor( + constructor( @inject(TYPES.Katana) katana: Katana, - @inject(TYPES.Shuriken) shuriken: Shuriken + @inject(TYPES.Shuriken) shuriken: Shuriken, ) { this._katana = katana; this._shuriken = shuriken; } - public fight() { return this._katana.hit(); } - public sneak() { return this._shuriken.throw(); } - + public fight() { + return this._katana.hit(); + } + public sneak() { + return this._shuriken.throw(); + } } interface School { @@ -1748,9 +1654,7 @@ describe('InversifyJS', () => { public ninjaMaster: Ninja; public student: Ninja; - public constructor( - @multiInject(TYPES.Ninja) ninjas: Ninja[] - ) { + constructor(@multiInject(TYPES.Ninja) ninjas: Ninja[]) { this.ninjaMaster = ninjas[0] as Ninja; this.student = ninjas[1] as Ninja; } @@ -1764,14 +1668,12 @@ describe('InversifyJS', () => { class NinjaOrganisation implements Organisation { public schools: NinjaSchool[]; - public constructor( - @multiInject(TYPES.School) schools: School[] - ) { + constructor(@multiInject(TYPES.School) schools: School[]) { this.schools = schools; } } - const container = new Container(); + const container: Container = new Container(); container.bind(TYPES.Katana).to(Katana); container.bind(TYPES.Shuriken).to(Shuriken); container.bind(TYPES.Ninja).to(Ninja); @@ -1780,189 +1682,199 @@ describe('InversifyJS', () => { container.bind(TYPES.School).to(NinjaSchool); container.bind(TYPES.Organisation).to(NinjaOrganisation); - const ninjaOrganisation = container.get(TYPES.Organisation); + const ninjaOrganisation: Organisation = container.get( + TYPES.Organisation, + ); - for (let i = 0; i < 2; i++) { - const ithNinjaOrganizationSchool = ninjaOrganisation.schools[i] as School; + for (let i: number = 0; i < 2; i++) { + const ithNinjaOrganizationSchool: School = ninjaOrganisation.schools[ + i + ] as School; expect(ithNinjaOrganizationSchool.ninjaMaster.fight()).eql('cut!'); expect(ithNinjaOrganizationSchool.ninjaMaster.sneak()).eql('hit!'); expect(ithNinjaOrganizationSchool.student.fight()).eql('cut!'); expect(ithNinjaOrganizationSchool.student.sneak()).eql('hit!'); } - }); }); it('Should support tagged bindings', () => { - - enum Tag { CanThrow } - - interface Weapon { } + enum Tag { + CanThrow, + } @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} interface Warrior { - katana: Weapon; - shuriken: Weapon; + katana: unknown; + shuriken: unknown; } @injectable() class Ninja implements Warrior { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject('Weapon') @tagged('canThrow', false) katana: Weapon, - @inject('Weapon') @tagged(Tag.CanThrow, true) shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject('Weapon') @tagged('canThrow', false) katana: unknown, + @inject('Weapon') @tagged(Tag.CanThrow, true) shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind('Warrior').to(Ninja); - container.bind('Weapon').to(Katana).whenTargetTagged('canThrow', false); - container.bind('Weapon').to(Shuriken).whenTargetTagged(Tag.CanThrow, true); + container.bind('Weapon').to(Katana).whenTargetTagged('canThrow', false); + container.bind('Weapon').to(Shuriken).whenTargetTagged(Tag.CanThrow, true); - const ninja = container.get('Warrior'); + const ninja: Ninja = container.get('Warrior'); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should support custom tag decorators', () => { - - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} interface Warrior { - katana: Weapon; - shuriken: Weapon; + katana: unknown; + shuriken: unknown; } - const throwable = tagged('canThrow', true); - const notThrowable = tagged('canThrow', false); + const throwable: ( + target: DecoratorTarget, + targetKey?: string | symbol, + indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, + ) => void = tagged('canThrow', true); + const notThrowable: ( + target: DecoratorTarget, + targetKey?: string | symbol, + indexOrPropertyDescriptor?: number | TypedPropertyDescriptor, + ) => void = tagged('canThrow', false); @injectable() class Ninja implements Warrior { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject('Weapon') @notThrowable katana: Weapon, - @inject('Weapon') @throwable shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject('Weapon') @notThrowable katana: unknown, + @inject('Weapon') @throwable shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind('Warrior').to(Ninja); - container.bind('Weapon').to(Katana).whenTargetTagged('canThrow', false); - container.bind('Weapon').to(Shuriken).whenTargetTagged('canThrow', true); + container.bind('Weapon').to(Katana).whenTargetTagged('canThrow', false); + container.bind('Weapon').to(Shuriken).whenTargetTagged('canThrow', true); - const ninja = container.get('Warrior'); + const ninja: Warrior = container.get('Warrior'); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should support named bindings', () => { - const name: symbol = Symbol.for('Weak'); - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} interface Warrior { - katana: Weapon; - shuriken: Weapon; + katana: unknown; + shuriken: unknown; } @injectable() class Ninja implements Warrior { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject('Weapon') @named('strong') katana: Weapon, - @inject('Weapon') @named(name) shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject('Weapon') @named('strong') katana: unknown, + @inject('Weapon') @named(name) shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind('Warrior').to(Ninja); - container.bind('Weapon').to(Katana).whenTargetNamed('strong'); - container.bind('Weapon').to(Shuriken).whenTargetNamed(name); + container.bind('Weapon').to(Katana).whenTargetNamed('strong'); + container.bind('Weapon').to(Shuriken).whenTargetNamed(name); - const ninja = container.get('Warrior'); + const ninja: Warrior = container.get('Warrior'); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should support contextual bindings and targetName annotation', () => { - - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} interface Warrior { - katana: Weapon; - shuriken: Weapon; + katana: unknown; + shuriken: unknown; } @injectable() class Ninja implements Warrior { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject('Weapon') @targetName('katana') katana: Weapon, - @inject('Weapon') @targetName('shuriken') shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject('Weapon') @targetName('katana') katana: unknown, + @inject('Weapon') @targetName('shuriken') shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind('Warrior').to(Ninja); - container.bind('Weapon').to(Katana).when((request: interfaces.Request) => - request !== null && request.target !== null && request.target.name.equals('katana')); - - container.bind('Weapon').to(Shuriken).when((request: interfaces.Request) => - request !== null && request.target !== null && request.target.name.equals('shuriken')); - - const ninja = container.get('Warrior'); + container + .bind('Weapon') + .to(Katana) + .when( + (request: interfaces.Request | null) => + request !== null && + (request.target as interfaces.Target | null) !== null && + request.target.name.equals('katana'), + ); + + container + .bind('Weapon') + .to(Shuriken) + .when( + (request: interfaces.Request | null) => + request !== null && + (request.target as interfaces.Target | null) !== null && + request.target.name.equals('shuriken'), + ); + + const ninja: Warrior = container.get('Warrior'); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should be able to resolve a ambiguous binding by providing a named tag', () => { - interface Weapon { name: string; } @@ -1970,7 +1882,7 @@ describe('InversifyJS', () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'katana'; } } @@ -1978,25 +1890,23 @@ describe('InversifyJS', () => { @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'shuriken'; } } - const container = new Container(); + const container: Container = new Container(); container.bind('Weapon').to(Katana).whenTargetNamed('japonese'); container.bind('Weapon').to(Shuriken).whenTargetNamed('chinese'); - const katana = container.getNamed('Weapon', 'japonese'); - const shuriken = container.getNamed('Weapon', 'chinese'); + const katana: Weapon = container.getNamed('Weapon', 'japonese'); + const shuriken: Weapon = container.getNamed('Weapon', 'chinese'); expect(katana.name).eql('katana'); expect(shuriken.name).eql('shuriken'); - }); it('Should be able to resolve a ambiguous binding by providing a custom tag', () => { - interface Weapon { name: string; } @@ -2004,7 +1914,7 @@ describe('InversifyJS', () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'katana'; } } @@ -2012,30 +1922,43 @@ describe('InversifyJS', () => { @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'shuriken'; } } - const container = new Container(); - container.bind('Weapon').to(Katana).whenTargetTagged('faction', 'samurai'); - container.bind('Weapon').to(Shuriken).whenTargetTagged('faction', 'ninja'); - - const katana = container.getTagged('Weapon', 'faction', 'samurai'); - const shuriken = container.getTagged('Weapon', 'faction', 'ninja'); + const container: Container = new Container(); + container + .bind('Weapon') + .to(Katana) + .whenTargetTagged('faction', 'samurai'); + container + .bind('Weapon') + .to(Shuriken) + .whenTargetTagged('faction', 'ninja'); + + const katana: Weapon = container.getTagged( + 'Weapon', + 'faction', + 'samurai', + ); + const shuriken: Weapon = container.getTagged( + 'Weapon', + 'faction', + 'ninja', + ); expect(katana.name).eql('katana'); expect(shuriken.name).eql('shuriken'); - }); it('Should be able to inject into a super constructor', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const SYMBOLS = { Samurai: Symbol.for('Samurai'), SamuraiMaster: Symbol.for('SamuraiMaster'), SamuraiMaster2: Symbol.for('SamuraiMaster2'), - Weapon: Symbol.for('Weapon') + Weapon: Symbol.for('Weapon'), }; interface Weapon { @@ -2049,17 +1972,16 @@ describe('InversifyJS', () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'katana'; } } @injectable() class Samurai implements Warrior { - public weapon: Weapon; - public constructor(weapon: Weapon) { + constructor(weapon: Weapon) { this.weapon = weapon; } } @@ -2075,89 +1997,93 @@ describe('InversifyJS', () => { @injectable() class SamuraiMaster2 extends Samurai implements Warrior { public isMaster: boolean; - public constructor(@inject(SYMBOLS.Weapon) weapon: Weapon) { + constructor(@inject(SYMBOLS.Weapon) weapon: Weapon) { super(weapon); this.isMaster = true; } } - const container = new Container(); + const container: Container = new Container(); container.bind(SYMBOLS.Weapon).to(Katana); container.bind(SYMBOLS.Samurai).to(Samurai); container.bind(SYMBOLS.SamuraiMaster).to(SamuraiMaster); container.bind(SYMBOLS.SamuraiMaster2).to(SamuraiMaster2); - const errorFunction = () => { container.get(SYMBOLS.SamuraiMaster); }; - const error = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH('SamuraiMaster'); + const errorFunction: () => void = () => { + container.get(SYMBOLS.SamuraiMaster); + }; + const error: string = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH('SamuraiMaster'); expect(errorFunction).to.throw(error); - const samuraiMaster2 = container.get(SYMBOLS.SamuraiMaster2); + const samuraiMaster2: SamuraiMaster2 = container.get( + SYMBOLS.SamuraiMaster2, + ); expect(samuraiMaster2.weapon.name).eql('katana'); expect(typeof samuraiMaster2.isMaster).eql('boolean'); - }); it('Should allow to flag arguments as unmanaged', () => { - - const container = new Container(); + const container: Container = new Container(); // CASE 1: should throw - const Base1Id = 'Base1'; + const base1Id: string = 'Base1'; @injectable() class Base1 { public prop: string; - public constructor(arg: string) { + constructor(arg: string) { this.prop = arg; } } @injectable() class Derived1 extends Base1 { - public constructor() { + constructor() { super('unmanaged-injected-value'); } } - container.bind(Base1Id).to(Derived1); - const tryGet = () => { container.get(Base1Id); }; - const error = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH('Derived1'); + container.bind(base1Id).to(Derived1); + const tryGet: () => void = () => { + container.get(base1Id); + }; + const error: string = ERROR_MSGS.ARGUMENTS_LENGTH_MISMATCH('Derived1'); expect(tryGet).to.throw(error); // CASE 2: Use @unmanaged to overcome issue - const Base2Id = 'Base2'; + const base2Id: string = 'Base2'; @injectable() class Base2 { public prop1: string; - public constructor(@unmanaged() arg1: string) { + constructor(@unmanaged() arg1: string) { this.prop1 = arg1; } } @injectable() class Derived2 extends Base2 { - public constructor() { + constructor() { super('unmanaged-injected-value'); } } - container.bind(Base2Id).to(Derived2); - const derived1 = container.get(Base2Id); + container.bind(base2Id).to(Derived2); + const derived1: Base2 = container.get(base2Id); expect(derived1 instanceof Derived2).to.eql(true); expect(derived1.prop1).to.eql('unmanaged-injected-value'); // CASE 3: Use @unmanaged to overcome issue when multiple args - const Base3Id = 'Base3'; + const base3Id: string = 'Base3'; @injectable() class Base3 { public prop1: string; public prop2: string; - public constructor(@unmanaged() arg1: string, arg2: string) { + constructor(@unmanaged() arg1: string, arg2: string) { this.prop1 = arg1; this.prop2 = arg2; } @@ -2165,25 +2091,24 @@ describe('InversifyJS', () => { @injectable() class Derived3 extends Base3 { - public constructor(@inject('SomeId') arg1: string) { + constructor(@inject('SomeId') arg1: string) { super('unmanaged-injected-value', arg1); } } - container.bind(Base3Id).to(Derived3); + container.bind(base3Id).to(Derived3); container.bind('SomeId').toConstantValue('managed-injected-value'); - const derived2 = container.get(Base3Id); + const derived2: Base3 = container.get(base3Id); expect(derived2 instanceof Base3).to.eql(true); expect(derived2.prop1).to.eql('unmanaged-injected-value'); expect(derived2.prop2).to.eql('managed-injected-value'); - }); it('Should support a whenInjectedInto contextual bindings constraint', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Ninja: 'Ninja', - Weapon: 'Weapon' + Weapon: 'Weapon', }; interface Weapon { @@ -2193,7 +2118,7 @@ describe('InversifyJS', () => { @injectable() class Katana implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'katana'; } } @@ -2201,7 +2126,7 @@ describe('InversifyJS', () => { @injectable() class Bokken implements Weapon { public name: string; - public constructor() { + constructor() { this.name = 'bokken'; } } @@ -2212,51 +2137,64 @@ describe('InversifyJS', () => { @injectable() class NinjaStudent implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') @targetName('weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') @targetName('weapon') weapon: Weapon) { this.weapon = weapon; } } @injectable() class NinjaMaster implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') @targetName('weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') @targetName('weapon') weapon: Weapon) { this.weapon = weapon; } } - const container = new Container(); - container.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('master', false); - container.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('master', true); - container.bind(TYPES.Weapon).to(Katana).whenInjectedInto(NinjaMaster); - container.bind(TYPES.Weapon).to(Bokken).whenInjectedInto(NinjaStudent); - - const master = container.getTagged(TYPES.Ninja, 'master', true); - const student = container.getTagged(TYPES.Ninja, 'master', false); + const container: Container = new Container(); + container + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('master', false); + container + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('master', true); + container + .bind(TYPES.Weapon) + .to(Katana) + .whenInjectedInto(NinjaMaster); + container + .bind(TYPES.Weapon) + .to(Bokken) + .whenInjectedInto(NinjaStudent); + + const master: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + true, + ); + const student: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + false, + ); expect(master instanceof NinjaMaster).eql(true); expect(student instanceof NinjaStudent).eql(true); expect(master.weapon.name).eql('katana'); expect(student.weapon.name).eql('bokken'); - }); it('Should support a whenParentNamed contextual bindings constraint', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Material: 'Material', Ninja: 'Ninja', - Weapon: 'Weapon' + Weapon: 'Weapon', }; interface Material { @@ -2266,7 +2204,7 @@ describe('InversifyJS', () => { @injectable() class Wood implements Material { public name: string; - public constructor() { + constructor() { this.name = 'wood'; } } @@ -2274,7 +2212,7 @@ describe('InversifyJS', () => { @injectable() class Iron implements Material { public name: string; - public constructor() { + constructor() { this.name = 'iron'; } } @@ -2286,7 +2224,7 @@ describe('InversifyJS', () => { @injectable() class Sword implements Weapon { public material: Material; - public constructor(@inject('Material') material: Material) { + constructor(@inject('Material') material: Material) { this.material = material; } } @@ -2297,49 +2235,59 @@ describe('InversifyJS', () => { @injectable() class NinjaStudent implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') @named('non-lethal') weapon: Weapon - ) { + constructor(@inject('Weapon') @named('non-lethal') weapon: Weapon) { this.weapon = weapon; } } @injectable() class NinjaMaster implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') @named('lethal') weapon: Weapon - ) { + constructor(@inject('Weapon') @named('lethal') weapon: Weapon) { this.weapon = weapon; } } - const container = new Container(); - container.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('master', false); - container.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('master', true); + const container: Container = new Container(); + container + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('master', false); + container + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('master', true); container.bind(TYPES.Weapon).to(Sword); container.bind(TYPES.Material).to(Iron).whenParentNamed('lethal'); - container.bind(TYPES.Material).to(Wood).whenParentNamed('non-lethal'); - - const master = container.getTagged(TYPES.Ninja, 'master', true); - const student = container.getTagged(TYPES.Ninja, 'master', false); + container + .bind(TYPES.Material) + .to(Wood) + .whenParentNamed('non-lethal'); + + const master: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + true, + ); + const student: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + false, + ); expect(master.weapon.material.name).eql('iron'); expect(student.weapon.material.name).eql('wood'); - }); it('Should support a whenParentTagged contextual bindings constraint', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Material: 'Material', Ninja: 'Ninja', - Weapon: 'Weapon' + Weapon: 'Weapon', }; interface Material { @@ -2349,7 +2297,7 @@ describe('InversifyJS', () => { @injectable() class Wood implements Material { public name: string; - public constructor() { + constructor() { this.name = 'wood'; } } @@ -2357,7 +2305,7 @@ describe('InversifyJS', () => { @injectable() class Iron implements Material { public name: string; - public constructor() { + constructor() { this.name = 'iron'; } } @@ -2369,7 +2317,7 @@ describe('InversifyJS', () => { @injectable() class Sword implements Weapon { public material: Material; - public constructor(@inject('Material') material: Material) { + constructor(@inject('Material') material: Material) { this.material = material; } } @@ -2380,49 +2328,62 @@ describe('InversifyJS', () => { @injectable() class NinjaStudent implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') @tagged('lethal', false) weapon: Weapon - ) { + constructor(@inject('Weapon') @tagged('lethal', false) weapon: Weapon) { this.weapon = weapon; } } @injectable() class NinjaMaster implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') @tagged('lethal', true) weapon: Weapon - ) { + constructor(@inject('Weapon') @tagged('lethal', true) weapon: Weapon) { this.weapon = weapon; } } - const container = new Container(); - container.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('master', false); - container.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('master', true); + const container: Container = new Container(); + container + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('master', false); + container + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('master', true); container.bind(TYPES.Weapon).to(Sword); - container.bind(TYPES.Material).to(Iron).whenParentTagged('lethal', true); - container.bind(TYPES.Material).to(Wood).whenParentTagged('lethal', false); - - const master = container.getTagged(TYPES.Ninja, 'master', true); - const student = container.getTagged(TYPES.Ninja, 'master', false); + container + .bind(TYPES.Material) + .to(Iron) + .whenParentTagged('lethal', true); + container + .bind(TYPES.Material) + .to(Wood) + .whenParentTagged('lethal', false); + + const master: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + true, + ); + const student: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + false, + ); expect(master.weapon.material.name).eql('iron'); expect(student.weapon.material.name).eql('wood'); - }); it('Should support a whenAnyAncestorIs and whenNoAncestorIs contextual bindings constraint', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Material: 'Material', Ninja: 'Ninja', - Weapon: 'Weapon' + Weapon: 'Weapon', }; interface Material { @@ -2432,7 +2393,7 @@ describe('InversifyJS', () => { @injectable() class Wood implements Material { public name: string; - public constructor() { + constructor() { this.name = 'wood'; } } @@ -2440,7 +2401,7 @@ describe('InversifyJS', () => { @injectable() class Iron implements Material { public name: string; - public constructor() { + constructor() { this.name = 'iron'; } } @@ -2452,7 +2413,7 @@ describe('InversifyJS', () => { @injectable() class Sword implements Weapon { public material: Material; - public constructor(@inject('Material') material: Material) { + constructor(@inject('Material') material: Material) { this.material = material; } } @@ -2463,64 +2424,97 @@ describe('InversifyJS', () => { @injectable() class NinjaStudent implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } @injectable() class NinjaMaster implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } // whenAnyAncestorIs - const container = new Container(); - container.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('master', false); - container.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('master', true); + const container: Container = new Container(); + container + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('master', false); + container + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('master', true); container.bind(TYPES.Weapon).to(Sword); - container.bind(TYPES.Material).to(Iron).whenAnyAncestorIs(NinjaMaster); - container.bind(TYPES.Material).to(Wood).whenAnyAncestorIs(NinjaStudent); - - const master = container.getTagged(TYPES.Ninja, 'master', true); - const student = container.getTagged(TYPES.Ninja, 'master', false); + container + .bind(TYPES.Material) + .to(Iron) + .whenAnyAncestorIs(NinjaMaster); + container + .bind(TYPES.Material) + .to(Wood) + .whenAnyAncestorIs(NinjaStudent); + + const master: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + true, + ); + const student: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + false, + ); expect(master.weapon.material.name).eql('iron'); expect(student.weapon.material.name).eql('wood'); // whenNoAncestorIs - const container2 = new Container(); - container2.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('master', false); - container2.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('master', true); + const container2: Container = new Container(); + container2 + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('master', false); + container2 + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('master', true); container2.bind(TYPES.Weapon).to(Sword); - container2.bind(TYPES.Material).to(Iron).whenNoAncestorIs(NinjaStudent); - container2.bind(TYPES.Material).to(Wood).whenNoAncestorIs(NinjaMaster); - - const master2 = container2.getTagged(TYPES.Ninja, 'master', true); - const student2 = container2.getTagged(TYPES.Ninja, 'master', false); + container2 + .bind(TYPES.Material) + .to(Iron) + .whenNoAncestorIs(NinjaStudent); + container2 + .bind(TYPES.Material) + .to(Wood) + .whenNoAncestorIs(NinjaMaster); + + const master2: Ninja = container2.getTagged( + TYPES.Ninja, + 'master', + true, + ); + const student2: Ninja = container2.getTagged( + TYPES.Ninja, + 'master', + false, + ); expect(master2.weapon.material.name).eql('iron'); expect(student2.weapon.material.name).eql('wood'); - }); it('Should support a whenAnyAncestorNamed and whenNoAncestorNamed contextual bindings constraint', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Material: 'Material', Ninja: 'Ninja', - Weapon: 'Weapon' + Weapon: 'Weapon', }; interface Material { @@ -2530,7 +2524,7 @@ describe('InversifyJS', () => { @injectable() class Wood implements Material { public name: string; - public constructor() { + constructor() { this.name = 'wood'; } } @@ -2538,7 +2532,7 @@ describe('InversifyJS', () => { @injectable() class Iron implements Material { public name: string; - public constructor() { + constructor() { this.name = 'iron'; } } @@ -2550,7 +2544,7 @@ describe('InversifyJS', () => { @injectable() class Sword implements Weapon { public material: Material; - public constructor(@inject('Material') material: Material) { + constructor(@inject('Material') material: Material) { this.material = material; } } @@ -2561,64 +2555,84 @@ describe('InversifyJS', () => { @injectable() class NinjaStudent implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } @injectable() class NinjaMaster implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } // whenAnyAncestorNamed - const container = new Container(); - container.bind(TYPES.Ninja).to(NinjaStudent).whenTargetNamed('non-lethal'); - container.bind(TYPES.Ninja).to(NinjaMaster).whenTargetNamed('lethal'); + const container: Container = new Container(); + container + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetNamed('non-lethal'); + container + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetNamed('lethal'); container.bind(TYPES.Weapon).to(Sword); - container.bind(TYPES.Material).to(Iron).whenAnyAncestorNamed('lethal'); - container.bind(TYPES.Material).to(Wood).whenAnyAncestorNamed('non-lethal'); - - const master = container.getNamed(TYPES.Ninja, 'lethal'); - const student = container.getNamed(TYPES.Ninja, 'non-lethal'); + container + .bind(TYPES.Material) + .to(Iron) + .whenAnyAncestorNamed('lethal'); + container + .bind(TYPES.Material) + .to(Wood) + .whenAnyAncestorNamed('non-lethal'); + + const master: Ninja = container.getNamed(TYPES.Ninja, 'lethal'); + const student: Ninja = container.getNamed(TYPES.Ninja, 'non-lethal'); expect(master.weapon.material.name).eql('iron'); expect(student.weapon.material.name).eql('wood'); // whenNoAncestorNamed - const container2 = new Container(); - container2.bind(TYPES.Ninja).to(NinjaStudent).whenTargetNamed('non-lethal'); - container2.bind(TYPES.Ninja).to(NinjaMaster).whenTargetNamed('lethal'); + const container2: Container = new Container(); + container2 + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetNamed('non-lethal'); + container2 + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetNamed('lethal'); container2.bind(TYPES.Weapon).to(Sword); - container2.bind(TYPES.Material).to(Iron).whenNoAncestorNamed('non-lethal'); - container2.bind(TYPES.Material).to(Wood).whenNoAncestorNamed('lethal'); - - const master2 = container.getNamed(TYPES.Ninja, 'lethal'); - const student2 = container.getNamed(TYPES.Ninja, 'non-lethal'); + container2 + .bind(TYPES.Material) + .to(Iron) + .whenNoAncestorNamed('non-lethal'); + container2 + .bind(TYPES.Material) + .to(Wood) + .whenNoAncestorNamed('lethal'); + + const master2: Ninja = container.getNamed(TYPES.Ninja, 'lethal'); + const student2: Ninja = container.getNamed( + TYPES.Ninja, + 'non-lethal', + ); expect(master2.weapon.material.name).eql('iron'); expect(student2.weapon.material.name).eql('wood'); - }); it('Should support a whenAnyAncestorTagged and whenNoAncestorTaggedcontextual bindings constraint', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Material: 'Material', Ninja: 'Ninja', - Weapon: 'Weapon' + Weapon: 'Weapon', }; interface Material { @@ -2628,7 +2642,7 @@ describe('InversifyJS', () => { @injectable() class Wood implements Material { public name: string; - public constructor() { + constructor() { this.name = 'wood'; } } @@ -2636,7 +2650,7 @@ describe('InversifyJS', () => { @injectable() class Iron implements Material { public name: string; - public constructor() { + constructor() { this.name = 'iron'; } } @@ -2648,7 +2662,7 @@ describe('InversifyJS', () => { @injectable() class Sword implements Weapon { public material: Material; - public constructor(@inject('Material') material: Material) { + constructor(@inject('Material') material: Material) { this.material = material; } } @@ -2659,64 +2673,97 @@ describe('InversifyJS', () => { @injectable() class NinjaStudent implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } @injectable() class NinjaMaster implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } // whenAnyAncestorTagged - const container = new Container(); - container.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('lethal', false); - container.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('lethal', true); + const container: Container = new Container(); + container + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('lethal', false); + container + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('lethal', true); container.bind(TYPES.Weapon).to(Sword); - container.bind(TYPES.Material).to(Iron).whenAnyAncestorTagged('lethal', true); - container.bind(TYPES.Material).to(Wood).whenAnyAncestorTagged('lethal', false); - - const master = container.getTagged(TYPES.Ninja, 'lethal', true); - const student = container.getTagged(TYPES.Ninja, 'lethal', false); + container + .bind(TYPES.Material) + .to(Iron) + .whenAnyAncestorTagged('lethal', true); + container + .bind(TYPES.Material) + .to(Wood) + .whenAnyAncestorTagged('lethal', false); + + const master: Ninja = container.getTagged( + TYPES.Ninja, + 'lethal', + true, + ); + const student: Ninja = container.getTagged( + TYPES.Ninja, + 'lethal', + false, + ); expect(master.weapon.material.name).eql('iron'); expect(student.weapon.material.name).eql('wood'); // whenNoAncestorTagged - const container2 = new Container(); - container2.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('lethal', false); - container2.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('lethal', true); + const container2: Container = new Container(); + container2 + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('lethal', false); + container2 + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('lethal', true); container2.bind(TYPES.Weapon).to(Sword); - container2.bind(TYPES.Material).to(Iron).whenNoAncestorTagged('lethal', false); - container2.bind(TYPES.Material).to(Wood).whenNoAncestorTagged('lethal', true); - - const master2 = container.getTagged(TYPES.Ninja, 'lethal', true); - const student2 = container.getTagged(TYPES.Ninja, 'lethal', false); + container2 + .bind(TYPES.Material) + .to(Iron) + .whenNoAncestorTagged('lethal', false); + container2 + .bind(TYPES.Material) + .to(Wood) + .whenNoAncestorTagged('lethal', true); + + const master2: Ninja = container.getTagged( + TYPES.Ninja, + 'lethal', + true, + ); + const student2: Ninja = container.getTagged( + TYPES.Ninja, + 'lethal', + false, + ); expect(master2.weapon.material.name).eql('iron'); expect(student2.weapon.material.name).eql('wood'); - }); it('Should support a whenAnyAncestorMatches and whenNoAncestorMatches contextual bindings constraint', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const TYPES = { Material: 'Material', Ninja: 'Ninja', - Weapon: 'Weapon' + Weapon: 'Weapon', }; interface Material { @@ -2726,7 +2773,7 @@ describe('InversifyJS', () => { @injectable() class Wood implements Material { public name: string; - public constructor() { + constructor() { this.name = 'wood'; } } @@ -2734,7 +2781,7 @@ describe('InversifyJS', () => { @injectable() class Iron implements Material { public name: string; - public constructor() { + constructor() { this.name = 'iron'; } } @@ -2746,7 +2793,7 @@ describe('InversifyJS', () => { @injectable() class Sword implements Weapon { public material: Material; - public constructor(@inject('Material') material: Material) { + constructor(@inject('Material') material: Material) { this.material = material; } } @@ -2757,67 +2804,104 @@ describe('InversifyJS', () => { @injectable() class NinjaStudent implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } @injectable() class NinjaMaster implements Ninja { - public weapon: Weapon; - public constructor( - @inject('Weapon') weapon: Weapon - ) { + constructor(@inject('Weapon') weapon: Weapon) { this.weapon = weapon; } } // custom constraints - const anyAncestorIsNinjaMasterConstraint = typeConstraint(NinjaMaster); - const anyAncestorIsNinjaStudentConstraint = typeConstraint(NinjaStudent); + const anyAncestorIsNinjaMasterConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(NinjaMaster); + const anyAncestorIsNinjaStudentConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(NinjaStudent); // whenAnyAncestorMatches - const container = new Container(); - container.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('master', false); - container.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('master', true); + const container: Container = new Container(); + container + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('master', false); + container + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('master', true); container.bind(TYPES.Weapon).to(Sword); - container.bind(TYPES.Material).to(Iron).whenAnyAncestorMatches(anyAncestorIsNinjaMasterConstraint); - container.bind(TYPES.Material).to(Wood).whenAnyAncestorMatches(anyAncestorIsNinjaStudentConstraint); - - const master = container.getTagged(TYPES.Ninja, 'master', true); - const student = container.getTagged(TYPES.Ninja, 'master', false); + container + .bind(TYPES.Material) + .to(Iron) + .whenAnyAncestorMatches(anyAncestorIsNinjaMasterConstraint); + container + .bind(TYPES.Material) + .to(Wood) + .whenAnyAncestorMatches(anyAncestorIsNinjaStudentConstraint); + + const master: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + true, + ); + const student: Ninja = container.getTagged( + TYPES.Ninja, + 'master', + false, + ); expect(master.weapon.material.name).eql('iron'); expect(student.weapon.material.name).eql('wood'); // whenNoAncestorMatches - const container2 = new Container(); - container2.bind(TYPES.Ninja).to(NinjaStudent).whenTargetTagged('master', false); - container2.bind(TYPES.Ninja).to(NinjaMaster).whenTargetTagged('master', true); + const container2: Container = new Container(); + container2 + .bind(TYPES.Ninja) + .to(NinjaStudent) + .whenTargetTagged('master', false); + container2 + .bind(TYPES.Ninja) + .to(NinjaMaster) + .whenTargetTagged('master', true); container2.bind(TYPES.Weapon).to(Sword); - container2.bind(TYPES.Material).to(Iron).whenNoAncestorMatches(anyAncestorIsNinjaStudentConstraint); - container2.bind(TYPES.Material).to(Wood).whenNoAncestorMatches(anyAncestorIsNinjaMasterConstraint); - - const master2 = container2.getTagged(TYPES.Ninja, 'master', true); - const student2 = container2.getTagged(TYPES.Ninja, 'master', false); + container2 + .bind(TYPES.Material) + .to(Iron) + .whenNoAncestorMatches(anyAncestorIsNinjaStudentConstraint); + container2 + .bind(TYPES.Material) + .to(Wood) + .whenNoAncestorMatches(anyAncestorIsNinjaMasterConstraint); + + const master2: Ninja = container2.getTagged( + TYPES.Ninja, + 'master', + true, + ); + const student2: Ninja = container2.getTagged( + TYPES.Ninja, + 'master', + false, + ); expect(master2.weapon.material.name).eql('iron'); expect(student2.weapon.material.name).eql('wood'); - }); it('Should be able to inject a regular derived class', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const SYMBOLS = { RANK: Symbol.for('RANK'), - SamuraiMaster: Symbol.for('SamuraiMaster') + SamuraiMaster: Symbol.for('SamuraiMaster'), }; interface Warrior { @@ -2826,34 +2910,34 @@ describe('InversifyJS', () => { @injectable() class Samurai implements Warrior { - public rank: string; - public constructor(rank: string) { + constructor(rank: string) { this.rank = rank; } } @injectable() class SamuraiMaster extends Samurai implements Warrior { - public constructor(@inject(SYMBOLS.RANK) rank: string) { + constructor(@inject(SYMBOLS.RANK) rank: string) { super(rank); } } - const container = new Container(); + const container: Container = new Container(); container.bind(SYMBOLS.SamuraiMaster).to(SamuraiMaster); container.bind(SYMBOLS.RANK).toConstantValue('Master'); - const samurai = container.get(SYMBOLS.SamuraiMaster); + const samurai: SamuraiMaster = container.get( + SYMBOLS.SamuraiMaster, + ); expect(samurai.rank).eql('Master'); - }); it('Should be able to identify missing @injectable in a base class', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const SYMBOLS = { - SamuraiMaster: Symbol.for('SamuraiMaster') + SamuraiMaster: Symbol.for('SamuraiMaster'), }; interface Warrior { @@ -2862,30 +2946,29 @@ describe('InversifyJS', () => { // IMPORTANT: Missing @injectable() class Samurai implements Warrior { - public rank: string; - public constructor(rank: string) { + constructor(rank: string) { this.rank = rank; } } @injectable() class SamuraiMaster extends Samurai implements Warrior { - public constructor() { + constructor() { super('master'); } } - const container = new Container(); + const container: Container = new Container(); container.bind(SYMBOLS.SamuraiMaster).to(SamuraiMaster); function throws() { return container.get(SYMBOLS.SamuraiMaster); } - expect(throws).to.throw(`${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} Samurai`); - + expect(throws).to.throw( + `${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} Samurai`, + ); }); - }); diff --git a/test/middleware/middleware.test.ts b/test/middleware/middleware.test.ts index b82b9369e..344564d03 100644 --- a/test/middleware/middleware.test.ts +++ b/test/middleware/middleware.test.ts @@ -1,12 +1,12 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; + import { injectable } from '../../src/annotation/injectable'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; import { Container } from '../../src/container/container'; import { interfaces } from '../../src/interfaces/interfaces'; describe('Middleware', () => { - let sandbox: sinon.SinonSandbox; beforeEach(() => { @@ -18,37 +18,36 @@ describe('Middleware', () => { }); it('Should be able to use middleware as Container configuration', () => { - - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; function middleware1(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`Middleware1: ${args.serviceIdentifier.toString()}`); return planAndResolve(args); }; } container.applyMiddleware(middleware1); - const _container = container; - expect((_container as unknown as { _middleware: unknown })._middleware).not.to.eql(null); + expect( + (container as unknown as { _middleware: unknown })._middleware, + ).not.to.eql(null); }); it('Should support middleware', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } + class Ninja {} - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; function middleware1(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`Middleware1: ${args.serviceIdentifier.toString()}`); return planAndResolve(args); }; @@ -56,6 +55,7 @@ describe('Middleware', () => { function middleware2(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`Middleware2: ${args.serviceIdentifier.toString()}`); return planAndResolve(args); }; @@ -66,28 +66,25 @@ describe('Middleware', () => { container.bind('Ninja').to(Ninja); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja instanceof Ninja).eql(true); expect(log.length).eql(2); expect(log[0]).eql('Middleware2: Ninja'); expect(log[1]).eql('Middleware1: Ninja'); - }); it('Should allow applyMiddleware at multiple points in time', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } + class Ninja {} - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; function middleware1(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`Middleware1: ${args.serviceIdentifier.toString()}`); return planAndResolve(args); }; @@ -95,37 +92,35 @@ describe('Middleware', () => { function middleware2(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`Middleware2: ${args.serviceIdentifier.toString()}`); return planAndResolve(args); }; } container.applyMiddleware(middleware1); // one point in time - container.applyMiddleware(middleware2); // another point in time + container.applyMiddleware(middleware2); // another point in time container.bind('Ninja').to(Ninja); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja instanceof Ninja).eql(true); expect(log.length).eql(2); expect(log[0]).eql('Middleware2: Ninja'); expect(log[1]).eql('Middleware1: Ninja'); - }); it('Should use middleware', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } + class Ninja {} - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; function middleware1(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`Middleware1: ${args.serviceIdentifier.toString()}`); return planAndResolve(args); }; @@ -133,6 +128,7 @@ describe('Middleware', () => { function middleware2(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`Middleware2: ${args.serviceIdentifier.toString()}`); return planAndResolve(args); }; @@ -141,23 +137,19 @@ describe('Middleware', () => { container.applyMiddleware(middleware1, middleware2); container.bind('Ninja').to(Ninja); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja instanceof Ninja).eql(true); expect(log.length).eql(2); expect(log[0]).eql('Middleware2: Ninja'); expect(log[1]).eql('Middleware1: Ninja'); - }); it('Should be able to use middleware to catch errors during pre-planning phase', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } + class Ninja implements Ninja {} - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; @@ -177,20 +169,16 @@ describe('Middleware', () => { container.get('SOME_NOT_REGISTERED_ID'); expect(log.length).eql(1); expect(log[0]).eql(`${ERROR_MSGS.NOT_REGISTERED} SOME_NOT_REGISTERED_ID`); - }); it('Should be able to use middleware to catch errors during planning phase', () => { - - interface Warrior { } - @injectable() - class Ninja implements Warrior { } + class Ninja {} @injectable() - class Samurai implements Warrior { } + class Samurai {} - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; @@ -206,20 +194,16 @@ describe('Middleware', () => { } container.applyMiddleware(middleware); - container.bind('Warrior').to(Ninja); - container.bind('Warrior').to(Samurai); + container.bind('Warrior').to(Ninja); + container.bind('Warrior').to(Samurai); container.get('Warrior'); expect(log.length).eql(1); expect(log[0]).to.contain(`${ERROR_MSGS.AMBIGUOUS_MATCH} Warrior`); - }); it('Should be able to use middleware to catch errors during resolution phase', () => { - - interface Warrior { } - - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; @@ -235,51 +219,54 @@ describe('Middleware', () => { } container.applyMiddleware(middleware); - container.bind('Warrior'); // Invalid binding missing BindingToSyntax + container.bind('Warrior'); // Invalid binding missing BindingToSyntax container.get('Warrior'); expect(log.length).eql(1); expect(log[0]).eql(`${ERROR_MSGS.INVALID_BINDING_TYPE} Warrior`); - }); it('Should help users to identify problems with middleware', () => { - - const container = new Container(); + const container: Container = new Container(); function middleware(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { try { return planAndResolve(args); - } catch (e) { - return undefined as unknown as (_: interfaces.NextArgs) => undefined + } catch (_e: unknown) { + return undefined as unknown as (_: interfaces.NextArgs) => undefined; } }; } container.applyMiddleware(middleware); - const throws = () => { container.get('SOME_NOT_REGISTERED_ID'); }; + const throws: () => void = () => { + container.get('SOME_NOT_REGISTERED_ID'); + }; expect(throws).to.throw(ERROR_MSGS.INVALID_MIDDLEWARE_RETURN); - }); it('Should allow users to intercept a resolution context', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } + class Ninja {} - const container = new Container(); + const container: Container = new Container(); const log: string[] = []; function middleware1(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { - const nextContextInterceptor = args.contextInterceptor; + const nextContextInterceptor: ( + contexts: interfaces.Context, + ) => interfaces.Context = args.contextInterceptor; args.contextInterceptor = (context: interfaces.Context) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`contextInterceptor1: ${args.serviceIdentifier.toString()}`); - return nextContextInterceptor !== null ? nextContextInterceptor(context) : context; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return nextContextInterceptor !== null + ? nextContextInterceptor(context) + : context; }; return planAndResolve(args); }; @@ -287,10 +274,16 @@ describe('Middleware', () => { function middleware2(planAndResolve: interfaces.Next): interfaces.Next { return (args: interfaces.NextArgs) => { - const nextContextInterceptor = args.contextInterceptor; + const nextContextInterceptor: ( + contexts: interfaces.Context, + ) => interfaces.Context = args.contextInterceptor; args.contextInterceptor = (context: interfaces.Context) => { + // eslint-disable-next-line @typescript-eslint/no-base-to-string log.push(`contextInterceptor2: ${args.serviceIdentifier.toString()}`); - return nextContextInterceptor !== null ? nextContextInterceptor(context) : context; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return nextContextInterceptor !== null + ? nextContextInterceptor(context) + : context; }; return planAndResolve(args); }; @@ -299,13 +292,11 @@ describe('Middleware', () => { container.applyMiddleware(middleware1, middleware2); container.bind('Ninja').to(Ninja); - const ninja = container.get('Ninja'); + const ninja: Ninja = container.get('Ninja'); expect(ninja instanceof Ninja).eql(true); expect(log.length).eql(2); expect(log[0]).eql('contextInterceptor1: Ninja'); expect(log[1]).eql('contextInterceptor2: Ninja'); - }); - -}); \ No newline at end of file +}); diff --git a/test/node/error_messages.test.ts b/test/node/error_messages.test.ts index 39e354277..331638e74 100644 --- a/test/node/error_messages.test.ts +++ b/test/node/error_messages.test.ts @@ -1,83 +1,107 @@ import { expect } from 'chai'; + import * as ERROR_MSGS from '../../src/constants/error_msgs'; import { Container, injectable } from '../../src/inversify'; describe('Error message when resolving fails', () => { - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} @injectable() - class Bokken implements Weapon { } + class Bokken {} it('Should contain correct message and the serviceIdentifier in error message', () => { - const container = new Container(); + const container: Container = new Container(); - container.bind('Weapon').to(Katana); + container.bind('Weapon').to(Katana); - const tryWeapon = () => { container.get('Ninja'); }; + const tryWeapon: () => void = () => { + container.get('Ninja'); + }; expect(tryWeapon).to.throw(`${ERROR_MSGS.NOT_REGISTERED} Ninja`); - }); it('Should contain the provided name in error message when target is named', () => { - - const container = new Container(); - const tryGetNamedWeapon = (name: string | number | symbol) => { container.getNamed('Weapon', name); }; - - expect(() => tryGetNamedWeapon('superior')).to.throw(/.*\bWeapon\b.*\bsuperior\b/g); - expect(() => tryGetNamedWeapon(Symbol.for('Superior'))).to.throw(/.*\bWeapon\b.*Symbol\(Superior\)/g); - expect(() => tryGetNamedWeapon(0)).to.throw(/.*\bWeapon\b.*\b0\b/g); - + const container: Container = new Container(); + const tryGetNamedWeapon: (name: string | number | symbol) => void = ( + name: string | number | symbol, + ) => { + container.getNamed('Weapon', name); + }; + + expect(() => { + tryGetNamedWeapon('superior'); + }).to.throw(/.*\bWeapon\b.*\bsuperior\b/g); + expect(() => { + tryGetNamedWeapon(Symbol.for('Superior')); + }).to.throw(/.*\bWeapon\b.*Symbol\(Superior\)/g); + expect(() => { + tryGetNamedWeapon(0); + }).to.throw(/.*\bWeapon\b.*\b0\b/g); }); it('Should contain the provided tag in error message when target is tagged', () => { - - const container = new Container(); - const tryGetTaggedWeapon = (tag: string | number | symbol) => { container.getTagged('Weapon', tag, true); }; - - expect(() => tryGetTaggedWeapon('canShoot')).to.throw(/.*\bWeapon\b.*\bcanShoot\b.*\btrue\b/g); - expect(() => tryGetTaggedWeapon(Symbol.for('Can shoot'))).to.throw(/.*\bWeapon\b.*Symbol\(Can shoot\).*\btrue\b/g); - expect(() => tryGetTaggedWeapon(0)).to.throw(/.*\bWeapon\b.*\b0\b.*\btrue\b/g); - + const container: Container = new Container(); + const tryGetTaggedWeapon: (tag: string | number | symbol) => void = ( + tag: string | number | symbol, + ) => { + container.getTagged('Weapon', tag, true); + }; + + expect(() => { + tryGetTaggedWeapon('canShoot'); + }).to.throw(/.*\bWeapon\b.*\bcanShoot\b.*\btrue\b/g); + expect(() => { + tryGetTaggedWeapon(Symbol.for('Can shoot')); + }).to.throw(/.*\bWeapon\b.*Symbol\(Can shoot\).*\btrue\b/g); + expect(() => { + tryGetTaggedWeapon(0); + }).to.throw(/.*\bWeapon\b.*\b0\b.*\btrue\b/g); }); it('Should list all possible bindings in error message if no matching binding found', () => { - - const container = new Container(); - container.bind('Weapon').to(Katana).whenTargetNamed('strong'); - container.bind('Weapon').to(Shuriken).whenTargetTagged('canThrow', true); - container.bind('Weapon').to(Bokken).whenTargetNamed('weak'); + const container: Container = new Container(); + container.bind('Weapon').to(Katana).whenTargetNamed('strong'); + container.bind('Weapon').to(Shuriken).whenTargetTagged('canThrow', true); + container.bind('Weapon').to(Bokken).whenTargetNamed('weak'); try { container.getNamed('Weapon', 'superior'); } catch (error) { - expect((error as Error).message).to.match(/.*\bKatana\b.*\bnamed\b.*\bstrong\b/); - expect((error as Error).message).to.match(/.*\bBokken\b.*\bnamed\b.*\bweak\b/); - expect((error as Error).message).to.match(/.*\bShuriken\b.*\btagged\b.*\bcanThrow\b.*\btrue\b/); + expect((error as Error).message).to.match( + /.*\bKatana\b.*\bnamed\b.*\bstrong\b/, + ); + expect((error as Error).message).to.match( + /.*\bBokken\b.*\bnamed\b.*\bweak\b/, + ); + expect((error as Error).message).to.match( + /.*\bShuriken\b.*\btagged\b.*\bcanThrow\b.*\btrue\b/, + ); } }); it('Should list all possible bindings in error message if ambiguous matching binding found', () => { - - const container = new Container(); - container.bind('Weapon').to(Katana).whenTargetNamed('strong'); - container.bind('Weapon').to(Shuriken).whenTargetTagged('canThrow', true); - container.bind('Weapon').to(Bokken).whenTargetNamed('weak'); + const container: Container = new Container(); + container.bind('Weapon').to(Katana).whenTargetNamed('strong'); + container.bind('Weapon').to(Shuriken).whenTargetTagged('canThrow', true); + container.bind('Weapon').to(Bokken).whenTargetNamed('weak'); try { container.get('Weapon'); } catch (error) { - expect((error as Error).message).to.match(/.*\bKatana\b.*\bnamed\b.*\bstrong\b/); - expect((error as Error).message).to.match(/.*\bBokken\b.*\bnamed\b.*\bweak\b/); - expect((error as Error).message).to.match(/.*\bShuriken\b.*\btagged\b.*\bcanThrow\b.*\btrue\b/); + expect((error as Error).message).to.match( + /.*\bKatana\b.*\bnamed\b.*\bstrong\b/, + ); + expect((error as Error).message).to.match( + /.*\bBokken\b.*\bnamed\b.*\bweak\b/, + ); + expect((error as Error).message).to.match( + /.*\bShuriken\b.*\btagged\b.*\bcanThrow\b.*\btrue\b/, + ); } - }); - -}); \ No newline at end of file +}); diff --git a/test/node/exceptions.test.ts b/test/node/exceptions.test.ts index 688393e1f..387a1d242 100644 --- a/test/node/exceptions.test.ts +++ b/test/node/exceptions.test.ts @@ -1,63 +1,52 @@ import { expect } from 'chai'; + import * as ERROR_MSGS from '../../src/constants/error_msgs'; import { Container, inject, injectable } from '../../src/inversify'; describe('Node', () => { - it('Should throw if circular dependencies found', () => { - - interface IA { } - interface IB { } - interface IC { } - interface ID { } - @injectable() - class A implements IA { - public b: IB; - public c: IC; - public constructor( - @inject('B') b: IB, - @inject('C') c: IC, - ) { + class A { + public b: unknown; + public c: unknown; + constructor(@inject('B') b: unknown, @inject('C') c: unknown) { this.b = b; this.c = c; } } @injectable() - class B implements IB { } + class B {} @injectable() - class C implements IC { - public d: ID; - public constructor(@inject('D') d: ID) { + class C { + public d: unknown; + constructor(@inject('D') d: unknown) { this.d = d; } } @injectable() - class D implements ID { - public a: IA; - public constructor(@inject('A') a: IA) { + class D { + public a: unknown; + constructor(@inject('A') a: unknown) { this.a = a; } } - const container = new Container(); + const container: Container = new Container(); container.bind('A').to(A); container.bind('B').to(B); container.bind('C').to(C); container.bind('D').to(D); function willThrow() { - const a = container.get('A'); + const a: A = container.get('A'); return a; } expect(willThrow).to.throw( - `${ERROR_MSGS.CIRCULAR_DEPENDENCY} A --> C --> D --> A` + `${ERROR_MSGS.CIRCULAR_DEPENDENCY} A --> C --> D --> A`, ); - }); - -}); \ No newline at end of file +}); diff --git a/test/node/performance.test.ts b/test/node/performance.test.ts index 04d2dc860..75441a947 100644 --- a/test/node/performance.test.ts +++ b/test/node/performance.test.ts @@ -1,22 +1,24 @@ import { expect } from 'chai'; -import { Container } from '../../src/inversify'; import { performance } from 'perf_hooks'; -describe('Performance', () => { +import { Container } from '../../src/inversify'; +describe('Performance', () => { function registerN(times: number) { - - const result = { + const result: { + container: Container; + register: number; + } = { container: new Container(), - register: -1 + register: -1, }; - let i = 0; - - for (i = 0; i < times; i++) { - const start = performance.now(); - result.container.bind(`SOME_ID_${i}`).toConstantValue({ test: i }); - const end = performance.now(); + for (let i: number = 0; i < times; i++) { + const start: number = performance.now(); + result.container + .bind(`SOME_ID_${i.toString()}`) + .toConstantValue({ test: i }); + const end: number = performance.now(); result.register = end - start; } @@ -24,22 +26,23 @@ describe('Performance', () => { } function resolveN(container: Container, times: number) { - - const result = { + const result: { + avg: number; + max: number; + min: number; + } = { avg: -1, max: -1, - min: 9999999999999999 + min: Number.MAX_SAFE_INTEGER, }; const items: number[] = []; - let i = 0; - for (i = 0; i < times; i++) { - - const start = performance.now(); - container.get(`SOME_ID_${times}`); - const end = performance.now(); - const total = end - start; + for (let i: number = 0; i < times; i++) { + const start: number = performance.now(); + container.get(`SOME_ID_${times.toString()}`); + const end: number = performance.now(); + const total: number = end - start; if (total < result.min) { result.min = total; @@ -51,60 +54,92 @@ describe('Performance', () => { items.push(total); } - result.avg = items.reduce((p, c) => p + c, 0) / items.length; + result.avg = + items.reduce((p: number, c: number) => p + c, 0) / items.length; return result; } it('Should be able to register 1 binding in less than 1 ms', () => { - const result1 = registerN(1); + const result1: { + container: Container; + register: number; + } = registerN(1); expect(result1.register).to.below(1); expect(result1.register).to.below(1); }); it('Should be able to register 5 bindings in less than 1 ms', () => { - const result5 = registerN(5); + const result5: { + container: Container; + register: number; + } = registerN(5); expect(result5.register).to.below(1); }); it('Should be able to register 1K bindings in less than 1 ms', () => { - const result1K = registerN(1000); + const result1K: { + container: Container; + register: number; + } = registerN(1000); expect(result1K.register).to.below(1); }); it('Should be able to register 5K bindings in less than 1 ms', () => { - const result5K = registerN(5000); + const result5K: { + container: Container; + register: number; + } = registerN(5000); expect(result5K.register).to.below(1); }); it('Should be able to register 1 bindings in less than 1 ms', () => { - const container1 = registerN(1000).container; - const result1 = resolveN(container1, 5); + const container1: Container = registerN(1000).container; + const result1: { + avg: number; + max: number; + min: number; + } = resolveN(container1, 5); expect(result1.avg).to.below(1); }); it('Should be able to register 5 bindings in less than 1 ms', () => { - const container5 = registerN(1000).container; - const result5 = resolveN(container5, 5); + const container5: Container = registerN(1000).container; + const result5: { + avg: number; + max: number; + min: number; + } = resolveN(container5, 5); expect(result5.avg).to.below(1); }); it('Should be able to register 1K bindings in less than 1 ms', () => { - const container1K = registerN(1000).container; - const result1K = resolveN(container1K, 5); + const container1K: Container = registerN(1000).container; + const result1K: { + avg: number; + max: number; + min: number; + } = resolveN(container1K, 5); expect(result1K.avg).to.below(1); }); it('Should be able to register 5K bindings in less than 1 ms', () => { - const container5K = registerN(5000).container; - const result5K = resolveN(container5K, 5); + const container5K: Container = registerN(5000).container; + const result5K: { + avg: number; + max: number; + min: number; + } = resolveN(container5K, 5); expect(result5K.avg).to.below(1); }); it('Should be able to register 10K bindings in less than 1 ms', () => { - const container10K = registerN(10000).container; - const result10K = resolveN(container10K, 5); + const container10K: Container = registerN(10000).container; + const result10K: { + avg: number; + max: number; + min: number; + } = resolveN(container10K, 5); expect(result10K.avg).to.below(1); }); - -}); \ No newline at end of file +}); diff --git a/test/node/proxies.test.ts b/test/node/proxies.test.ts index 23ed0e821..e13770394 100644 --- a/test/node/proxies.test.ts +++ b/test/node/proxies.test.ts @@ -1,13 +1,12 @@ import { expect } from 'chai'; + import { interfaces } from '../../src/interfaces/interfaces'; import { Container, inject, injectable } from '../../src/inversify'; describe('InversifyJS', () => { - it('Should support the injection of proxied objects', () => { - - const weaponId = 'Weapon'; - const warriorId = 'Warrior'; + const weaponId: string = 'Weapon'; + const warriorId: string = 'Warrior'; interface Weapon { use(): void; @@ -27,35 +26,39 @@ describe('InversifyJS', () => { @injectable() class Ninja implements Warrior { public weapon: Weapon; - public constructor(@inject(weaponId) weapon: Weapon) { + constructor(@inject(weaponId) weapon: Weapon) { this.weapon = weapon; } } - const container = new Container(); + const container: Container = new Container(); container.bind(warriorId).to(Ninja); const log: string[] = []; - container.bind(weaponId).to(Katana).onActivation((context: interfaces.Context, weapon: Weapon) => { - const handler = { - apply(target: any, thisArgument: any, argumentsList: any[]) { - log.push(`Starting: ${new Date().getTime()}`); - const result = target.apply(thisArgument, argumentsList); - log.push(`Finished: ${new Date().getTime()}`); - return result; - } - }; - weapon.use = new Proxy(weapon.use, handler); - return weapon; - }); - - const ninja = container.get(warriorId); + container + .bind(weaponId) + .to(Katana) + .onActivation((_context: interfaces.Context, weapon: Weapon) => { + const handler: ProxyHandler<() => void> = { + apply( + target: () => void, + thisArgument: unknown, + argumentsList: [], + ): void { + log.push(`Starting: ${new Date().getTime().toString()}`); + target.apply(thisArgument, argumentsList); + log.push(`Finished: ${new Date().getTime().toString()}`); + }, + }; + weapon.use = new Proxy(weapon.use, handler); + return weapon; + }); + + const ninja: Warrior = container.get(warriorId); ninja.weapon.use(); expect(log.length).eql(2); expect(log[0]?.indexOf('Starting: ')).not.to.eql(-1); expect(log[1]?.indexOf('Finished: ')).not.to.eql(-1); - }); - -}); \ No newline at end of file +}); diff --git a/test/planning/context.test.ts b/test/planning/context.test.ts index fbf66cce3..c19492023 100644 --- a/test/planning/context.test.ts +++ b/test/planning/context.test.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; + import { TargetTypeEnum } from '../../src/constants/literal_types'; import { Container } from '../../src/container/container'; import { Context } from '../../src/planning/context'; @@ -7,40 +8,35 @@ import { Request } from '../../src/planning/request'; import { Target } from '../../src/planning/target'; describe('Context', () => { - it('Should set its own properties correctly', () => { - - const container = new Container(); - const context1 = new Context(container); - const invalid = null; - const context2: Context = new (Context)(invalid as unknown as Container); + const container: Container = new Container(); + const context1: Context = new Context(container); + const invalid: null = null; + const context2: Context = new Context(invalid as unknown as Container); expect(context1.container).not.to.eql(null); expect(context2.container).eql(null); expect(context1.id).to.be.a('number'); expect(context2.id).to.be.a('number'); expect(context1.id).not.eql(context2.id); - }); it('Should be linkable to a Plan', () => { + const container: Container = new Container(); + const context: Context = new Context(container); + const target: Target = new Target(TargetTypeEnum.Variable, '', 'Ninja'); - const container = new Container(); - const context = new Context(container); - const target = new Target(TargetTypeEnum.Variable, '', 'Ninja'); - - const ninjaRequest = new Request( + const ninjaRequest: Request = new Request( 'Ninja', context, null, [], - target + target, ); - const plan = new Plan(context, ninjaRequest); + const plan: Plan = new Plan(context, ninjaRequest); context.addPlan(plan); expect(context.plan.rootRequest.serviceIdentifier).eql('Ninja'); }); - -}); \ No newline at end of file +}); diff --git a/test/planning/metadata.test.ts b/test/planning/metadata.test.ts index e6837cc1d..3493a12ca 100644 --- a/test/planning/metadata.test.ts +++ b/test/planning/metadata.test.ts @@ -1,12 +1,11 @@ import { expect } from 'chai'; + import { Metadata } from '../../src/planning/metadata'; describe('Metadata', () => { - it('Should set its own properties correctly', () => { - const m = new Metadata('power', 5); + const m: Metadata = new Metadata('power', 5); expect(m.key).to.equals('power'); expect(m.value).to.equals(5); }); - -}); \ No newline at end of file +}); diff --git a/test/planning/plan.test.ts b/test/planning/plan.test.ts index 2b34d02d9..4fba8b083 100644 --- a/test/planning/plan.test.ts +++ b/test/planning/plan.test.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; + import { TargetTypeEnum } from '../../src/constants/literal_types'; import { Container } from '../../src/container/container'; import { Context } from '../../src/planning/context'; @@ -7,22 +8,20 @@ import { Request } from '../../src/planning/request'; import { Target } from '../../src/planning/target'; describe('Plan', () => { - it('Should set its own properties correctly', () => { - - const container = new Container(); - const context = new Context(container); - const runtimeId = 'Something'; + const container: Container = new Container(); + const context: Context = new Context(container); + const runtimeId: string = 'Something'; const request: Request = new Request( runtimeId, context, null, [], - new Target(TargetTypeEnum.Variable, '', runtimeId) + new Target(TargetTypeEnum.Variable, '', runtimeId), ); - const plan = new Plan(context, request); + const plan: Plan = new Plan(context, request); expect(plan.parentContext).eql(context); expect(plan.rootRequest.serviceIdentifier).eql(request.serviceIdentifier); @@ -31,5 +30,4 @@ describe('Plan', () => { expect(plan.rootRequest.childRequests).eql(request.childRequests); expect(plan.rootRequest.target).eql(request.target); }); - -}); \ No newline at end of file +}); diff --git a/test/planning/planner.test.ts b/test/planning/planner.test.ts index f458ba265..945b50342 100644 --- a/test/planning/planner.test.ts +++ b/test/planning/planner.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; + import { inject } from '../../src/annotation/inject'; import { injectable } from '../../src/annotation/injectable'; import { multiInject } from '../../src/annotation/multi_inject'; @@ -11,10 +12,10 @@ import { Container } from '../../src/container/container'; import { interfaces } from '../../src/interfaces/interfaces'; import { named } from '../../src/inversify'; import { MetadataReader } from '../../src/planning/metadata_reader'; +import { Plan } from '../../src/planning/plan'; import { plan } from '../../src/planning/planner'; describe('Planner', () => { - let sandbox: sinon.SinonSandbox; beforeEach(() => { @@ -26,59 +27,48 @@ describe('Planner', () => { }); it('Should be able to create a basic plan', () => { - - interface KatanaBlade { } - @injectable() - class KatanaBlade implements KatanaBlade { } - - interface KatanaHandler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements KatanaHandler { } - - interface Katana { } + class KatanaHandler {} @injectable() - class Katana implements Katana { + class Katana { public handler: KatanaHandler; public blade: KatanaBlade; - public constructor( + constructor( @inject('KatanaHandler') @targetName('handler') handler: KatanaHandler, - @inject('KatanaBlade') @targetName('blade') blade: KatanaBlade + @inject('KatanaBlade') @targetName('blade') blade: KatanaBlade, ) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } - - interface Ninja { } + class Shuriken {} @injectable() - class Ninja implements Ninja { + class Ninja { public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') @targetName('katana') katana: Katana, - @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken + @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken, ) { this.katana = katana; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const katanaId = 'Katana'; - const katanaHandlerId = 'KatanaHandler'; - const katanaBladeId = 'KatanaBlade'; + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const katanaId: string = 'Katana'; + const katanaHandlerId: string = 'KatanaHandler'; + const katanaBladeId: string = 'KatanaBlade'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); container.bind(katanaId).to(Katana); @@ -86,12 +76,22 @@ describe('Planner', () => { container.bind(katanaHandlerId).to(KatanaHandler); // Actual - const actualPlan = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId).plan; - const actualNinjaRequest = actualPlan.rootRequest; - const actualKatanaRequest = actualNinjaRequest.childRequests[0]; - const actualKatanaHandlerRequest = actualKatanaRequest?.childRequests[0]; - const actualKatanaBladeRequest = actualKatanaRequest?.childRequests[1]; - const actualShurikenRequest = actualNinjaRequest.childRequests[1]; + const actualPlan: Plan = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ).plan; + const actualNinjaRequest: interfaces.Request = actualPlan.rootRequest; + const actualKatanaRequest: interfaces.Request | undefined = + actualNinjaRequest.childRequests[0]; + const actualKatanaHandlerRequest: interfaces.Request | undefined = + actualKatanaRequest?.childRequests[0]; + const actualKatanaBladeRequest: interfaces.Request | undefined = + actualKatanaRequest?.childRequests[1]; + const actualShurikenRequest: interfaces.Request | undefined = + actualNinjaRequest.childRequests[1]; expect(actualNinjaRequest.serviceIdentifier).eql(ninjaId); expect(actualNinjaRequest.childRequests.length).eql(2); @@ -105,192 +105,190 @@ describe('Planner', () => { // KatanaHandler expect(actualKatanaHandlerRequest?.serviceIdentifier).eql(katanaHandlerId); expect(actualKatanaHandlerRequest?.bindings.length).eql(1); - expect(actualKatanaHandlerRequest?.target.serviceIdentifier).eql(katanaHandlerId); + expect(actualKatanaHandlerRequest?.target.serviceIdentifier).eql( + katanaHandlerId, + ); // KatanaBlade expect(actualKatanaBladeRequest?.serviceIdentifier).eql(katanaBladeId); expect(actualKatanaBladeRequest?.bindings.length).eql(1); - expect(actualKatanaBladeRequest?.target.serviceIdentifier).eql(katanaBladeId); + expect(actualKatanaBladeRequest?.target.serviceIdentifier).eql( + katanaBladeId, + ); // Shuriken expect(actualShurikenRequest?.serviceIdentifier).eql(shurikenId); expect(actualShurikenRequest?.bindings.length).eql(1); expect(actualShurikenRequest?.target.serviceIdentifier).eql(shurikenId); - }); it('Should throw when circular dependencies found', () => { - - interface IA { } - interface IB { } - interface IC { } - interface ID { } - @injectable() - class D implements ID { - public a: IA; - public constructor( - @inject('A') a: IA - ) { // circular dependency + class D { + public a: unknown; + constructor(@inject('A') a: unknown) { this.a = a; } } @injectable() - class C implements IC { - public d: ID; - public constructor( - @inject('D') d: ID - ) { + class C { + public d: unknown; + constructor(@inject('D') d: unknown) { this.d = d; } } @injectable() - class B implements IB { } + class B {} @injectable() - class A implements IA { - public b: IB; - public c: IC; - public constructor( - @inject('B') b: IB, - @inject('C') c: IC - ) { + class A { + public b: unknown; + public c: unknown; + constructor(@inject('B') b: unknown, @inject('C') c: unknown) { this.b = b; this.c = c; } } - const aId = 'A'; - const bId = 'B'; - const cId = 'C'; - const dId = 'D'; + const aId: string = 'A'; + const bId: string = 'B'; + const cId: string = 'C'; + const dId: string = 'D'; - const container = new Container(); - container.bind(aId).to(A); - container.bind(bId).to(B); - container.bind(cId).to(C); - container.bind(dId).to(D); + const container: Container = new Container(); + container.bind(aId).to(A); + container.bind(bId).to(B); + container.bind(cId).to(C); + container.bind(dId).to(D); - const throwErrorFunction = () => { + const throwErrorFunction: () => void = () => { container.get(aId); }; expect(throwErrorFunction).to.throw( - `${ERROR_MSGS.CIRCULAR_DEPENDENCY} A --> C --> D --> A` + `${ERROR_MSGS.CIRCULAR_DEPENDENCY} A --> C --> D --> A`, ); - }); it('Should only plan sub-dependencies when binding type is BindingType.Instance', () => { - - interface KatanaBlade { } - @injectable() - class KatanaBlade implements KatanaBlade { } - - interface KatanaHandler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements KatanaHandler { } - - interface Katana { } + class KatanaHandler {} @injectable() - class Katana implements Katana { + class Katana { public handler: KatanaHandler; public blade: KatanaBlade; - public constructor( + constructor( @inject('KatanaHandler') @targetName('handler') handler: KatanaHandler, - @inject('KatanaBlade') @targetName('blade') blade: KatanaBlade + @inject('KatanaBlade') @targetName('blade') blade: KatanaBlade, ) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } - - interface Ninja { } + class Shuriken {} @injectable() - class Ninja implements Ninja { + class Ninja { public katanaFactory: interfaces.Factory; public shuriken: Shuriken; - public constructor( - @inject('Factory') @targetName('katanaFactory') katanaFactory: interfaces.Factory, - @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken + constructor( + @inject('Factory') + @targetName('katanaFactory') + katanaFactory: interfaces.Factory, + @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken, ) { this.katanaFactory = katanaFactory; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const katanaId = 'Katana'; - const katanaHandlerId = 'KatanaHandler'; - const katanaBladeId = 'KatanaBlade'; - const katanaFactoryId = 'Factory'; + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const katanaId: string = 'Katana'; + const katanaHandlerId: string = 'KatanaHandler'; + const katanaBladeId: string = 'KatanaBlade'; + const katanaFactoryId: string = 'Factory'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); container.bind(katanaBladeId).to(Katana); container.bind(katanaBladeId).to(KatanaBlade); container.bind(katanaHandlerId).to(KatanaHandler); - container.bind>(katanaFactoryId).toFactory((context: interfaces.Context) => - () => - context.container.get(katanaId)); - - const actualPlan = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId).plan; + container + .bind>(katanaFactoryId) + .toFactory( + (context: interfaces.Context) => () => + context.container.get(katanaId), + ); + + const actualPlan: Plan = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ).plan; expect(actualPlan.rootRequest.serviceIdentifier).eql(ninjaId); - expect(actualPlan.rootRequest.childRequests[0]?.serviceIdentifier).eql(katanaFactoryId); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests.length).eql(0); // IMPORTANT! - expect(actualPlan.rootRequest.childRequests[1]?.serviceIdentifier).eql(shurikenId); - expect(actualPlan.rootRequest.childRequests[1]?.childRequests.length).eql(0); + expect(actualPlan.rootRequest.childRequests[0]?.serviceIdentifier).eql( + katanaFactoryId, + ); + expect(actualPlan.rootRequest.childRequests[0]?.childRequests.length).eql( + 0, + ); // IMPORTANT! + expect(actualPlan.rootRequest.childRequests[1]?.serviceIdentifier).eql( + shurikenId, + ); + expect(actualPlan.rootRequest.childRequests[1]?.childRequests.length).eql( + 0, + ); expect(actualPlan.rootRequest.childRequests[2]).eql(undefined); - }); it('Should generate plans with multi-injections', () => { - - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } - - interface Ninja { } + class Shuriken {} @injectable() class Ninja implements Ninja { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @multiInject('Weapon') @targetName('weapons') weapons: Weapon[] + public katana: unknown; + public shuriken: unknown; + constructor( + @multiInject('Weapon') + @targetName('weapons') + weapons: [unknown, unknown], ) { - this.katana = weapons[0] as Weapon; - this.shuriken = weapons[1] as Weapon; + [this.katana, this.shuriken] = weapons; } } - const ninjaId = 'Ninja'; - const weaponId = 'Weapon'; + const ninjaId: string = 'Ninja'; + const weaponId: string = 'Weapon'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind(weaponId).to(Shuriken); - container.bind(weaponId).to(Katana); + container.bind(weaponId).to(Shuriken); + container.bind(weaponId).to(Katana); - const actualPlan = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId).plan; + const actualPlan: Plan = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ).plan; // root request has no target expect(actualPlan.rootRequest.serviceIdentifier).eql(ninjaId); @@ -298,157 +296,207 @@ describe('Planner', () => { expect(actualPlan.rootRequest.target.isArray()).eql(false); // root request should only have one child request with target weapons/Weapon[] - expect(actualPlan.rootRequest.childRequests[0]?.serviceIdentifier).eql('Weapon'); + expect(actualPlan.rootRequest.childRequests[0]?.serviceIdentifier).eql( + 'Weapon', + ); expect(actualPlan.rootRequest.childRequests[1]).eql(undefined); - expect(actualPlan.rootRequest.childRequests[0]?.target.name.value()).eql('weapons'); - expect(actualPlan.rootRequest.childRequests[0]?.target.serviceIdentifier).eql('Weapon'); + expect(actualPlan.rootRequest.childRequests[0]?.target.name.value()).eql( + 'weapons', + ); + expect( + actualPlan.rootRequest.childRequests[0]?.target.serviceIdentifier, + ).eql('Weapon'); expect(actualPlan.rootRequest.childRequests[0]?.target.isArray()).eql(true); // child request should have two child requests with targets weapons/Weapon[] but bindings Katana and Shuriken - expect(actualPlan.rootRequest.childRequests[0]?.childRequests.length).eql(2); - - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.serviceIdentifier).eql(weaponId); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.target.name.value()).eql('weapons'); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.target.serviceIdentifier).eql('Weapon'); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.target.isArray()).eql(true); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.serviceIdentifier).eql('Weapon'); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.bindings[0]?.serviceIdentifier).eql('Weapon'); - const shurikenImplementationType = actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.bindings[0]?.implementationType as - { name: string }; + expect(actualPlan.rootRequest.childRequests[0]?.childRequests.length).eql( + 2, + ); + + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[0] + ?.serviceIdentifier, + ).eql(weaponId); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.target.name.value(), + ).eql('weapons'); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.target + .serviceIdentifier, + ).eql('Weapon'); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.target.isArray(), + ).eql(true); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[0] + ?.serviceIdentifier, + ).eql('Weapon'); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[0]?.bindings[0] + ?.serviceIdentifier, + ).eql('Weapon'); + + const shurikenImplementationType: NewableFunction = actualPlan.rootRequest + .childRequests[0]?.childRequests[0]?.bindings[0] + ?.implementationType as NewableFunction; + expect(shurikenImplementationType.name).eql('Shuriken'); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.serviceIdentifier).eql(weaponId); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.target.name.value()).eql('weapons'); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.target.serviceIdentifier).eql('Weapon'); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.target.isArray()).eql(true); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.serviceIdentifier).eql('Weapon'); - expect(actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.bindings[0]?.serviceIdentifier).eql('Weapon'); - const katanaImplementationType = actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.bindings[0]?.implementationType as - { name: string }; - expect(katanaImplementationType.name).eql('Katana'); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[1] + ?.serviceIdentifier, + ).eql(weaponId); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.target.name.value(), + ).eql('weapons'); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.target + .serviceIdentifier, + ).eql('Weapon'); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.target.isArray(), + ).eql(true); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[1] + ?.serviceIdentifier, + ).eql('Weapon'); + expect( + actualPlan.rootRequest.childRequests[0]?.childRequests[1]?.bindings[0] + ?.serviceIdentifier, + ).eql('Weapon'); + const katanaImplementationType: NewableFunction = actualPlan.rootRequest + .childRequests[0]?.childRequests[1]?.bindings[0] + ?.implementationType as NewableFunction; + expect(katanaImplementationType.name).eql('Katana'); }); it('Should throw when no matching bindings are found', () => { - - interface Katana { } @injectable() - class Katana implements Katana { } + class Katana {} - interface Shuriken { } @injectable() - class Shuriken implements Shuriken { } - - interface Ninja { } + class Shuriken {} @injectable() - class Ninja implements Ninja { + class Ninja { public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') @targetName('katana') katana: Katana, - @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken + @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken, ) { this.katana = katana; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); - const throwFunction = () => { - plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const throwFunction: () => void = () => { + plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); }; expect(throwFunction).to.throw(`${ERROR_MSGS.NOT_REGISTERED} Katana`); - }); it('Should throw when an ambiguous match is found', () => { - - interface Katana { } - @injectable() - class Katana implements Katana { } + class Katana {} @injectable() - class SharpKatana implements Katana { } - - interface Shuriken { } - class Shuriken implements Shuriken { } + class SharpKatana {} - interface Ninja { } + class Shuriken {} @injectable() - class Ninja implements Ninja { + class Ninja { public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') katana: Katana, - @inject('Shuriken') shuriken: Shuriken + @inject('Shuriken') shuriken: Shuriken, ) { this.katana = katana; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const katanaId = 'Katana'; - const shurikenId = 'Shuriken'; + const ninjaId: string = 'Ninja'; + const katanaId: string = 'Katana'; + const shurikenId: string = 'Shuriken'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(katanaId).to(Katana); container.bind(katanaId).to(SharpKatana); container.bind(shurikenId).to(Shuriken); - const throwFunction = () => { - plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const throwFunction: () => void = () => { + plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); }; expect(throwFunction).to.throw(`${ERROR_MSGS.AMBIGUOUS_MATCH} Katana`); - }); it('Should apply constrains when an ambiguous match is found', () => { - - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } - - interface Ninja { } + class Shuriken {} - const ninjaId = 'Ninja'; - const weaponId = 'Weapon'; + const ninjaId: string = 'Ninja'; + const weaponId: string = 'Weapon'; @injectable() class Ninja implements Ninja { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject(weaponId) @targetName('katana') @tagged('canThrow', false) katana: Weapon, - @inject(weaponId) @targetName('shuriken') @tagged('canThrow', true) shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject(weaponId) + @targetName('katana') + @tagged('canThrow', false) + katana: unknown, + @inject(weaponId) + @targetName('shuriken') + @tagged('canThrow', true) + shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind(weaponId).to(Katana).whenTargetTagged('canThrow', false); - container.bind(weaponId).to(Shuriken).whenTargetTagged('canThrow', true); + container.bind(weaponId).to(Katana).whenTargetTagged('canThrow', false); + container.bind(weaponId).to(Shuriken).whenTargetTagged('canThrow', true); - const actualPlan = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId).plan; + const actualPlan: Plan = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ).plan; // root request has no target expect(actualPlan.rootRequest.serviceIdentifier).eql(ninjaId); @@ -456,33 +504,48 @@ describe('Planner', () => { expect(actualPlan.rootRequest.target.isArray()).eql(false); // root request should have 2 child requests - expect(actualPlan.rootRequest.childRequests[0]?.serviceIdentifier).eql(weaponId); - expect(actualPlan.rootRequest.childRequests[0]?.target.name.value()).eql('katana'); - expect(actualPlan.rootRequest.childRequests[0]?.target.serviceIdentifier).eql(weaponId); + expect(actualPlan.rootRequest.childRequests[0]?.serviceIdentifier).eql( + weaponId, + ); + expect(actualPlan.rootRequest.childRequests[0]?.target.name.value()).eql( + 'katana', + ); + expect( + actualPlan.rootRequest.childRequests[0]?.target.serviceIdentifier, + ).eql(weaponId); - expect(actualPlan.rootRequest.childRequests[1]?.serviceIdentifier).eql(weaponId); - expect(actualPlan.rootRequest.childRequests[1]?.target.name.value()).eql('shuriken'); - expect(actualPlan.rootRequest.childRequests[1]?.target.serviceIdentifier).eql(weaponId); + expect(actualPlan.rootRequest.childRequests[1]?.serviceIdentifier).eql( + weaponId, + ); + expect(actualPlan.rootRequest.childRequests[1]?.target.name.value()).eql( + 'shuriken', + ); + expect( + actualPlan.rootRequest.childRequests[1]?.target.serviceIdentifier, + ).eql(weaponId); expect(actualPlan.rootRequest.childRequests[2]).eql(undefined); - }); it('Should throw when a class has a missing @injectable annotation', () => { - - interface Weapon { } - - class Katana implements Weapon { } - - const container = new Container(); - container.bind('Weapon').to(Katana); - - const throwFunction = () => { - plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, 'Weapon'); + class Katana {} + + const container: Container = new Container(); + container.bind('Weapon').to(Katana); + + const throwFunction: () => void = () => { + plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + 'Weapon', + ); }; - expect(throwFunction).to.throw(`${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} Katana.`); - + expect(throwFunction).to.throw( + `${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} Katana.`, + ); }); it('Should throw when apply a metadata decorator without @inject or @multiInject', () => { @@ -490,109 +553,125 @@ describe('Planner', () => { class Ninja { @named('name') // tslint:disable-next-line: no-empty - set weapon(weapon: Weapon) { - - } + public set weapon(weapon: unknown) {} } - interface Weapon { } - class Katana implements Weapon { } + class Katana {} - const container = new Container(); - container.bind('Weapon').to(Katana); + const container: Container = new Container(); + container.bind('Weapon').to(Katana); container.bind(Ninja).toSelf(); - const throwFunction = () => { - plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, Ninja); + const throwFunction: () => void = () => { + plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + Ninja, + ); }; - expect(throwFunction).to.throw(`${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} for property weapon in class Ninja.`); + expect(throwFunction).to.throw( + `${ERROR_MSGS.MISSING_INJECTABLE_ANNOTATION} for property weapon in class Ninja.`, + ); }); it('Should ignore checking base classes for @injectable when skipBaseClassChecks is set on the container', () => { - class Test { } + class Test {} @injectable() - class Test2 extends Test { } + class Test2 extends Test {} - const container = new Container({ skipBaseClassChecks: true }); + const container: Container = new Container({ skipBaseClassChecks: true }); container.bind(Test2).toSelf(); container.get(Test2); }); it('Should ignore checking base classes for @injectable on resolve when skipBaseClassChecks is set', () => { - class Test { } + class Test {} @injectable() - class Test2 extends Test { } + class Test2 extends Test {} - const container = new Container({ skipBaseClassChecks: true }); + const container: Container = new Container({ skipBaseClassChecks: true }); container.resolve(Test2); }); it('Should throw when an class has a missing @inject annotation', () => { - - interface Sword { } + interface Sword { + damage: number; + } @injectable() - class Katana implements Sword { } - - interface Warrior { } + class Katana implements Sword { + public readonly damage: number = 20; + } @injectable() - class Ninja implements Warrior { - + class Ninja { public katana: Katana; - public constructor( - katana: Sword - ) { + constructor(katana: Sword) { this.katana = katana; } } - const container = new Container(); - container.bind('Warrior').to(Ninja); + const container: Container = new Container(); + container.bind('Warrior').to(Ninja); container.bind('Sword').to(Katana); - const throwFunction = () => { - plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, 'Warrior'); + const throwFunction: () => void = () => { + plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + 'Warrior', + ); }; - expect(throwFunction).to.throw(`${ERROR_MSGS.MISSING_INJECT_ANNOTATION} argument 0 in class Ninja.`); - + expect(throwFunction).to.throw( + `${ERROR_MSGS.MISSING_INJECT_ANNOTATION} argument 0 in class Ninja.`, + ); }); it('Should throw when a function has a missing @injectable annotation', () => { - - interface Katana { } + interface Sword { + damage: number; + } @injectable() - class Katana implements Katana { } - - interface Ninja { } + class Katana implements Sword { + public readonly damage: number = 20; + } @injectable() - class Ninja implements Ninja { - + class Ninja { public katana: Katana; - public constructor( - katanaFactory: () => Katana - ) { + constructor(katanaFactory: () => Katana) { this.katana = katanaFactory(); } } - const container = new Container(); + const container: Container = new Container(); container.bind('Ninja').to(Ninja); container.bind('Katana').to(Katana); container.bind('Factory').to(Katana); - const throwFunction = () => { - plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, 'Ninja'); + const throwFunction: () => void = () => { + plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + 'Ninja', + ); }; - expect(throwFunction).to.throw(`${ERROR_MSGS.MISSING_INJECT_ANNOTATION} argument 0 in class Ninja.`); + expect(throwFunction).to.throw( + `${ERROR_MSGS.MISSING_INJECT_ANNOTATION} argument 0 in class Ninja.`, + ); }); -}); \ No newline at end of file +}); diff --git a/test/planning/queryable_string.test.ts b/test/planning/queryable_string.test.ts index b06c29aea..1099730cd 100644 --- a/test/planning/queryable_string.test.ts +++ b/test/planning/queryable_string.test.ts @@ -1,21 +1,21 @@ import { expect } from 'chai'; + import { QueryableString } from '../../src/planning/queryable_string'; describe('QueryableString', () => { - it('Should be able to set its own properties', () => { - const queryableString = new QueryableString('some_text'); + const queryableString: QueryableString = new QueryableString('some_text'); expect(queryableString.value()).to.eql('some_text'); }); it('Should be able to return its value', () => { - const queryableString = new QueryableString('some_text'); + const queryableString: QueryableString = new QueryableString('some_text'); expect(queryableString.value()).to.eql('some_text'); expect(queryableString.value() === 'some_other_text').to.eql(false); }); it('Should be able to identify if it"s value starts with certain text', () => { - const queryableString = new QueryableString('some_text'); + const queryableString: QueryableString = new QueryableString('some_text'); expect(queryableString.startsWith('some')).to.eql(true); expect(queryableString.startsWith('s')).to.eql(true); expect(queryableString.startsWith('me')).to.eql(false); @@ -23,7 +23,7 @@ describe('QueryableString', () => { }); it('Should be able to identify if it"s value ends with certain text', () => { - const queryableString = new QueryableString('some_text'); + const queryableString: QueryableString = new QueryableString('some_text'); expect(queryableString.endsWith('_text')).to.eql(true); expect(queryableString.endsWith('ext')).to.eql(true); expect(queryableString.endsWith('_tex')).to.eql(false); @@ -31,10 +31,9 @@ describe('QueryableString', () => { }); it('Should be able to identify if it"s value is equals to certain text', () => { - const queryableString = new QueryableString('some_text'); + const queryableString: QueryableString = new QueryableString('some_text'); expect(queryableString.equals('some_text')).to.eql(true); expect(queryableString.contains('some_text ')).to.eql(false); expect(queryableString.contains('som_text')).to.eql(false); }); - -}); \ No newline at end of file +}); diff --git a/test/planning/request.test.ts b/test/planning/request.test.ts index d57f72675..28edc768c 100644 --- a/test/planning/request.test.ts +++ b/test/planning/request.test.ts @@ -1,4 +1,5 @@ import { expect } from 'chai'; + import { TargetTypeEnum } from '../../src/constants/literal_types'; import { Container } from '../../src/container/container'; import { interfaces } from '../../src/interfaces/interfaces'; @@ -7,7 +8,7 @@ import { Request } from '../../src/planning/request'; import { Target } from '../../src/planning/target'; describe('Request', () => { - + // eslint-disable-next-line @typescript-eslint/typedef const identifiers = { Katana: 'Katana', KatanaBlade: 'KatanaBlade', @@ -17,24 +18,23 @@ describe('Request', () => { }; it('Should set its own properties correctly', () => { - - const container = new Container(); - const context = new Context(container); + const container: Container = new Container(); + const context: Context = new Context(container); const request1: Request = new Request( identifiers.Ninja, context, null, [], - new Target(TargetTypeEnum.Variable, '', identifiers.Ninja) + new Target(TargetTypeEnum.Variable, '', identifiers.Ninja), ); - const request2 = new Request( + const request2: Request = new Request( identifiers.Ninja, context, null, [], - new Target(TargetTypeEnum.Variable, '', identifiers.Ninja) + new Target(TargetTypeEnum.Variable, '', identifiers.Ninja), ); expect(request1.serviceIdentifier).eql(identifiers.Ninja); @@ -43,39 +43,40 @@ describe('Request', () => { expect(request1.id).to.be.a('number'); expect(request2.id).to.be.a('number'); expect(request1.id).not.eql(request2.id); - }); it('Should be able to add a child request', () => { - - const container = new Container(); - const context = new Context(container); + const container: Container = new Container(); + const context: Context = new Context(container); const ninjaRequest: Request = new Request( identifiers.Ninja, context, null, [], - new Target(TargetTypeEnum.Variable, 'Ninja', identifiers.Ninja) + new Target(TargetTypeEnum.Variable, 'Ninja', identifiers.Ninja), ); ninjaRequest.addChildRequest( identifiers.Katana, [], - new Target(TargetTypeEnum.ConstructorArgument, 'Katana', identifiers.Katana) + new Target( + TargetTypeEnum.ConstructorArgument, + 'Katana', + identifiers.Katana, + ), ); - const katanaRequest = ninjaRequest.childRequests[0]; + const katanaRequest: Request | undefined = ninjaRequest.childRequests[0]; expect(katanaRequest?.serviceIdentifier).eql(identifiers.Katana); expect(katanaRequest?.target.name.value()).eql('Katana'); expect(katanaRequest?.childRequests.length).eql(0); - const katanaParentRequest: interfaces.Request = katanaRequest?.parentRequest as Request; + const katanaParentRequest: interfaces.Request = + katanaRequest?.parentRequest as Request; expect(katanaParentRequest.serviceIdentifier).eql(identifiers.Ninja); expect(katanaParentRequest.target.name.value()).eql('Ninja'); expect(katanaParentRequest.target.serviceIdentifier).eql(identifiers.Ninja); - }); - -}); \ No newline at end of file +}); diff --git a/test/planning/target.test.ts b/test/planning/target.test.ts index 5519a8202..723e2d524 100644 --- a/test/planning/target.test.ts +++ b/test/planning/target.test.ts @@ -1,13 +1,17 @@ import { expect } from 'chai'; + import { TargetTypeEnum } from '../../src/constants/literal_types'; import * as METADATA_KEY from '../../src/constants/metadata_keys'; import { Metadata } from '../../src/planning/metadata'; import { Target } from '../../src/planning/target'; describe('Target', () => { - it('Should be able to create instances of untagged targets', () => { - const target = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana'); + const target: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + ); expect(target.serviceIdentifier).to.be.eql('Katana'); expect(target.name.value()).to.be.eql('katana'); expect(Array.isArray(target.metadata)).to.be.eql(true); @@ -15,7 +19,12 @@ describe('Target', () => { }); it('Should be able to create instances of named targets', () => { - const target = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', 'primary'); + const target: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + 'primary', + ); expect(target.serviceIdentifier).to.be.eql('Katana'); expect(target.name.value()).to.be.eql('katana'); expect(Array.isArray(target.metadata)).to.be.eql(true); @@ -25,7 +34,12 @@ describe('Target', () => { }); it('Should be able to create instances of tagged targets', () => { - const target = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', new Metadata('power', 5)); + const target: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + new Metadata('power', 5), + ); expect(target.serviceIdentifier).to.be.eql('Katana'); expect(target.name.value()).to.be.eql('katana'); expect(Array.isArray(target.metadata)).to.be.eql(true); @@ -35,86 +49,158 @@ describe('Target', () => { }); it('Should be able to identify named metadata', () => { - const target1 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', 'primary'); + const target1: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + 'primary', + ); expect(target1.isNamed()).to.be.eql(true); - const target2 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', new Metadata('power', 5)); + const target2: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + new Metadata('power', 5), + ); expect(target2.isNamed()).to.be.eql(false); }); it('Should be able to identify multi-injections', () => { - const target1 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana'); - target1.metadata.push(new Metadata(METADATA_KEY.MULTI_INJECT_TAG, 'Katana')); + const target1: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + ); + target1.metadata.push( + new Metadata(METADATA_KEY.MULTI_INJECT_TAG, 'Katana'), + ); expect(target1.isArray()).to.be.eql(true); - const target2 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana'); + const target2: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + ); expect(target2.isArray()).to.be.eql(false); }); it('Should be able to match multi-inject for a specified service metadata', () => { - const target1 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana'); - target1.metadata.push(new Metadata(METADATA_KEY.MULTI_INJECT_TAG, 'Katana')); + const target1: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + ); + target1.metadata.push( + new Metadata(METADATA_KEY.MULTI_INJECT_TAG, 'Katana'), + ); target1.metadata.push(new Metadata(METADATA_KEY.INJECT_TAG, 'Shuriken')); expect(target1.matchesArray('Katana')).to.be.eql(true); expect(target1.matchesArray('Shuriken')).to.be.eql(false); }); it('Should be able to match named metadata', () => { - const target1 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', 'primary'); + const target1: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + 'primary', + ); expect(target1.matchesNamedTag('primary')).to.be.eql(true); expect(target1.matchesNamedTag('secondary')).to.be.eql(false); }); it('Should be able to identify tagged metadata', () => { - - const target = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana'); + const target: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + ); expect(target.isTagged()).to.be.eql(false); - const target1 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', new Metadata('power', 5)); + const target1: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + new Metadata('power', 5), + ); expect(target1.isTagged()).to.be.eql(true); - const target2 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', 'primary'); + const target2: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + 'primary', + ); expect(target2.isTagged()).to.be.eql(false); - const target3 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana'); + const target3: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + ); target3.metadata.push(new Metadata('power', 5), new Metadata('speed', 5)); expect(target3.isTagged()).to.be.eql(true); - const target4 = new Target(TargetTypeEnum.Variable, '', 'Katana'); - target4.metadata.push(new Metadata(METADATA_KEY.INJECT_TAG, 'Katana')) + const target4: Target = new Target(TargetTypeEnum.Variable, '', 'Katana'); + target4.metadata.push(new Metadata(METADATA_KEY.INJECT_TAG, 'Katana')); expect(target4.isTagged()).to.be.eql(false); - const target5 = new Target(TargetTypeEnum.Variable, '', 'Katana'); - target5.metadata.push(new Metadata(METADATA_KEY.MULTI_INJECT_TAG, 'Katana')) + const target5: Target = new Target(TargetTypeEnum.Variable, '', 'Katana'); + target5.metadata.push( + new Metadata(METADATA_KEY.MULTI_INJECT_TAG, 'Katana'), + ); expect(target5.isTagged()).to.be.eql(false); - const target6 = new Target(TargetTypeEnum.Variable, 'katanaName', 'Katana'); - target6.metadata.push(new Metadata(METADATA_KEY.NAME_TAG, 'katanaName')) + const target6: Target = new Target( + TargetTypeEnum.Variable, + 'katanaName', + 'Katana', + ); + target6.metadata.push(new Metadata(METADATA_KEY.NAME_TAG, 'katanaName')); expect(target6.isTagged()).to.be.eql(false); - const target7 = new Target(TargetTypeEnum.Variable, '', 'Katana'); - target7.metadata.push(new Metadata(METADATA_KEY.UNMANAGED_TAG, true)) + const target7: Target = new Target(TargetTypeEnum.Variable, '', 'Katana'); + target7.metadata.push(new Metadata(METADATA_KEY.UNMANAGED_TAG, true)); expect(target7.isTagged()).to.be.eql(false); - const target8 = new Target(TargetTypeEnum.Variable, 'katanaName', 'Katana'); - target8.metadata.push(new Metadata(METADATA_KEY.NAMED_TAG, 'katanaName')) + const target8: Target = new Target( + TargetTypeEnum.Variable, + 'katanaName', + 'Katana', + ); + target8.metadata.push(new Metadata(METADATA_KEY.NAMED_TAG, 'katanaName')); expect(target8.isTagged()).to.be.eql(false); - const target9 = new Target(TargetTypeEnum.Variable, '', 'Katana'); - target9.metadata.push(new Metadata(METADATA_KEY.OPTIONAL_TAG, true)) + const target9: Target = new Target(TargetTypeEnum.Variable, '', 'Katana'); + target9.metadata.push(new Metadata(METADATA_KEY.OPTIONAL_TAG, true)); expect(target9.isTagged()).to.be.eql(false); }); it('Should be able to match tagged metadata', () => { - const target1 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', new Metadata('power', 5)); + const target1: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + new Metadata('power', 5), + ); expect(target1.matchesTag('power')(5)).to.be.eql(true); expect(target1.matchesTag('power')(2)).to.be.eql(false); }); it('Should contain an unique identifier', () => { - const target1 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', new Metadata('power', 5)); - const target2 = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Katana', new Metadata('power', 5)); + const target1: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + new Metadata('power', 5), + ); + const target2: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Katana', + new Metadata('power', 5), + ); expect(target1.id).to.be.a('number'); expect(target2.id).to.be.a('number'); expect(target1.id).not.eql(target2.id); }); - -}); \ No newline at end of file +}); diff --git a/test/resolution/resolver.test.ts b/test/resolution/resolver.test.ts index 423621b35..7a2d54a0e 100644 --- a/test/resolution/resolver.test.ts +++ b/test/resolution/resolver.test.ts @@ -10,7 +10,10 @@ import { preDestroy } from '../../src/annotation/pre_destroy'; import { tagged } from '../../src/annotation/tagged'; import { targetName } from '../../src/annotation/target_name'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; -import { BindingTypeEnum, TargetTypeEnum } from '../../src/constants/literal_types'; +import { + BindingTypeEnum, + TargetTypeEnum, +} from '../../src/constants/literal_types'; import { Container } from '../../src/container/container'; import { interfaces } from '../../src/interfaces/interfaces'; import { MetadataReader } from '../../src/planning/metadata_reader'; @@ -18,12 +21,12 @@ import { getBindingDictionary, plan } from '../../src/planning/planner'; import { resolveInstance } from '../../src/resolution/instantiation'; import { resolve } from '../../src/resolution/resolver'; -function resolveTyped(context: interfaces.Context) { - return resolve(context) as T +// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters +function resolveTyped(context: interfaces.Context): T { + return resolve(context) as T; } describe('Resolve', () => { - let sandbox: sinon.SinonSandbox; beforeEach(() => { @@ -35,22 +38,17 @@ describe('Resolve', () => { }); it('Should be able to resolve BindingType.Instance bindings', () => { - - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const katanaId = 'Katana'; - const katanaHandlerId = 'KatanaHandler'; - const katanaBladeId = 'KatanaBlade'; - - interface Blade { } + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const katanaId: string = 'Katana'; + const katanaHandlerId: string = 'KatanaHandler'; + const katanaBladeId: string = 'KatanaBlade'; @injectable() - class KatanaBlade implements Blade { } - - interface Handler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements Handler { } + class KatanaHandler {} interface Sword { handler: KatanaHandler; @@ -59,21 +57,19 @@ describe('Resolve', () => { @injectable() class Katana implements Sword { - public handler: Handler; - public blade: Blade; - public constructor( - @inject(katanaHandlerId) @targetName('handler') handler: Handler, - @inject(katanaBladeId) @targetName('blade') blade: Blade + public handler: KatanaHandler; + public blade: KatanaBlade; + constructor( + @inject(katanaHandlerId) @targetName('handler') handler: KatanaHandler, + @inject(katanaBladeId) @targetName('blade') blade: KatanaBlade, ) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katana: Katana; @@ -84,50 +80,50 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject(katanaId) @targetName('katana') katana: Katana, - @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken + @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken, ) { this.katana = katana; this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); container.bind(katanaId).to(Katana); container.bind(katanaBladeId).to(KatanaBlade); container.bind(katanaHandlerId).to(KatanaHandler); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); - const ninja = resolveTyped(context); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.katana.handler instanceof KatanaHandler).eql(true); expect(ninja.katana.blade instanceof KatanaBlade).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should store singleton type bindings in cache', () => { - - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const katanaId = 'Katana'; - const katanaHandlerId = 'KatanaHandler'; - const katanaBladeId = 'KatanaBlade'; - - interface Blade { } + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const katanaId: string = 'Katana'; + const katanaHandlerId: string = 'KatanaHandler'; + const katanaBladeId: string = 'KatanaBlade'; @injectable() - class KatanaBlade implements Blade { } - - interface Handler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements Handler { } + class KatanaHandler {} interface Sword { handler: KatanaHandler; @@ -136,21 +132,19 @@ describe('Resolve', () => { @injectable() class Katana implements Sword { - public handler: Handler; - public blade: Blade; - public constructor( - @inject(katanaHandlerId) @targetName('handler') handler: Handler, - @inject(katanaBladeId) @targetName('blade') blade: Blade + public handler: KatanaHandler; + public blade: KatanaBlade; + constructor( + @inject(katanaHandlerId) @targetName('handler') handler: KatanaHandler, + @inject(katanaBladeId) @targetName('blade') blade: KatanaBlade, ) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katana: Katana; @@ -161,51 +155,56 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject(katanaId) @targetName('katana') katana: Katana, - @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken + @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken, ) { this.katana = katana; this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); - container.bind(katanaId).to(Katana).inSingletonScope(); // SINGLETON! + container.bind(katanaId).to(Katana).inSingletonScope(); container.bind(katanaBladeId).to(KatanaBlade); - container.bind(katanaHandlerId).to(KatanaHandler).inSingletonScope(); // SINGLETON! - - const bindingDictionary = getBindingDictionary(container); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + container + .bind(katanaHandlerId) + .to(KatanaHandler) + .inSingletonScope(); - const katanaBinding = bindingDictionary.get(katanaId)[0]; + const bindingDictionary: interfaces.Lookup = + getBindingDictionary(container); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + + const katanaBinding: interfaces.Binding | undefined = + bindingDictionary.get(katanaId)[0]; expect(katanaBinding?.cache === null).eql(true); expect(katanaBinding?.activated).eql(false); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); - const ninja2 = resolveTyped(context); + const ninja2: Ninja = resolveTyped(context); expect(ninja2 instanceof Ninja).eql(true); expect(katanaBinding?.cache instanceof Katana).eql(true); expect(katanaBinding?.activated).eql(true); - }); it('Should throw when an invalid BindingType is detected', () => { - - interface Katana { } - @injectable() - class Katana implements Katana { } - - interface Shuriken { } + class Katana {} @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katana: Katana; @@ -216,9 +215,9 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') @targetName('katana') katana: Katana, - @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken + @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken, ) { this.katana = katana; this.shuriken = shuriken; @@ -226,33 +225,37 @@ describe('Resolve', () => { } // container and bindings - const ninjaId = 'Ninja'; - const container = new Container(); + const ninjaId: string = 'Ninja'; + const container: Container = new Container(); container.bind(ninjaId); // IMPORTANT! (Invalid binding) // context and plan - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); - - const throwFunction = () => { + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + + const throwFunction: () => void = () => { resolveTyped(context); }; - expect(context.plan.rootRequest.bindings[0]?.type).eql(BindingTypeEnum.Invalid); - expect(throwFunction).to.throw(`${ERROR_MSGS.INVALID_BINDING_TYPE} ${ninjaId}`); - + expect(context.plan.rootRequest.bindings[0]?.type).eql( + BindingTypeEnum.Invalid, + ); + expect(throwFunction).to.throw( + `${ERROR_MSGS.INVALID_BINDING_TYPE} ${ninjaId}`, + ); }); it('Should be able to resolve BindingType.ConstantValue bindings', () => { - - interface KatanaBlade { } - @injectable() - class KatanaBlade implements KatanaBlade { } - - interface KatanaHandler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements KatanaHandler { } + class KatanaHandler {} interface Sword { handler: KatanaHandler; @@ -263,16 +266,14 @@ describe('Resolve', () => { class Katana implements Sword { public handler: KatanaHandler; public blade: KatanaBlade; - public constructor(handler: KatanaHandler, blade: KatanaBlade) { + constructor(handler: KatanaHandler, blade: KatanaBlade) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katana: Katana; @@ -283,30 +284,39 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject('Katana') @targetName('katana') katana: Katana, - @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken + @inject('Shuriken') @targetName('shuriken') shuriken: Shuriken, ) { this.katana = katana; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const katanaId = 'Katana'; + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const katanaId: string = 'Katana'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); - container.bind(katanaId).toConstantValue(new Katana(new KatanaHandler(), new KatanaBlade())); // IMPORTANT! - - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); - - const katanaBinding = getBindingDictionary(container).get(katanaId)[0]; + container + .bind(katanaId) + .toConstantValue(new Katana(new KatanaHandler(), new KatanaBlade())); + + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + + const katanaBinding: interfaces.Binding | undefined = + getBindingDictionary(container).get(katanaId)[0]; expect(katanaBinding?.activated).eql(false); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(katanaBinding?.activated).eql(true); @@ -315,19 +325,13 @@ describe('Resolve', () => { expect(ninja.katana.handler instanceof KatanaHandler).eql(true); expect(ninja.katana.blade instanceof KatanaBlade).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should be able to resolve BindingType.DynamicValue bindings', () => { - - interface UseDate { - doSomething(): Date; - } - @injectable() - class UseDate implements UseDate { + class UseDate { public currentDate: Date; - public constructor(@inject('Date') currentDate: Date) { + constructor(@inject('Date') currentDate: Date) { this.currentDate = currentDate; } public doSomething() { @@ -335,95 +339,94 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('UseDate').to(UseDate); - container.bind('Date').toDynamicValue((context: interfaces.Context) => new Date()); + container + .bind('Date') + .toDynamicValue((_context: interfaces.Context) => new Date()); - const subject1 = container.get('UseDate'); - const subject2 = container.get('UseDate'); + const subject1: UseDate = container.get('UseDate'); + const subject2: UseDate = container.get('UseDate'); expect(subject1.doSomething() === subject2.doSomething()).eql(false); container.unbind('Date'); container.bind('Date').toConstantValue(new Date()); - const subject3 = container.get('UseDate'); - const subject4 = container.get('UseDate'); + const subject3: UseDate = container.get('UseDate'); + const subject4: UseDate = container.get('UseDate'); expect(subject3.doSomething() === subject4.doSomething()).eql(true); - }); it('Should be able to resolve BindingType.Constructor bindings', () => { + const ninjaId: string = 'Ninja'; + const newableKatanaId: string = 'Newable'; - const ninjaId = 'Ninja'; - const newableKatanaId = 'Newable'; @injectable() - class Katana { } + class Katana {} @injectable() class Ninja { public katana: Katana; - public constructor( - @inject(newableKatanaId) katana: interfaces.Newable - ) { - this.katana = new katana(); // IMPORTANT! + constructor(@inject(newableKatanaId) katana: interfaces.Newable) { + this.katana = new katana(); } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind>(newableKatanaId).toConstructor(Katana); // IMPORTANT! - - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); - const ninja = resolveTyped(context); + container + .bind>(newableKatanaId) + .toConstructor(Katana); + + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); - }); it('Should be able to resolve BindingType.Factory bindings', () => { - - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const swordFactoryId = 'Factory'; - const katanaId = 'Katana'; - const handlerId = 'Handler'; - const bladeId = 'Blade'; - - interface Blade { } + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const swordFactoryId: string = 'Factory'; + const katanaId: string = 'Katana'; + const handlerId: string = 'Handler'; + const bladeId: string = 'Blade'; @injectable() - class KatanaBlade implements Blade { } - - interface Handler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements Handler { } + class KatanaHandler {} interface Sword { - handler: Handler; - blade: Blade; + handler: KatanaHandler; + blade: KatanaBlade; } type SwordFactory = () => Sword; @injectable() class Katana implements Sword { - public handler: Handler; - public blade: Blade; - public constructor( - @inject(handlerId) @targetName('handler') handler: Handler, - @inject(bladeId) @targetName('blade') blade: Blade + public handler: KatanaHandler; + public blade: KatanaBlade; + constructor( + @inject(handlerId) @targetName('handler') handler: KatanaHandler, + @inject(bladeId) @targetName('blade') blade: KatanaBlade, ) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katana: Katana; @@ -434,56 +437,61 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Katana; public shuriken: Shuriken; - public constructor( - @inject(swordFactoryId) @targetName('makeKatana') makeKatana: SwordFactory, - @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken + constructor( + @inject(swordFactoryId) + @targetName('makeKatana') + makeKatana: SwordFactory, + @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken, ) { - this.katana = makeKatana(); // IMPORTANT! + this.katana = makeKatana(); this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); container.bind(katanaId).to(Katana); container.bind(bladeId).to(KatanaBlade); container.bind(handlerId).to(KatanaHandler); - container.bind>(swordFactoryId).toFactory((theContext: interfaces.Context) => - () => - theContext.container.get(katanaId)); - - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); - - const ninja = resolveTyped(context); + container + .bind>(swordFactoryId) + .toFactory( + (theContext: interfaces.Context) => () => + theContext.container.get(katanaId), + ); + + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.katana.handler instanceof KatanaHandler).eql(true); expect(ninja.katana.blade instanceof KatanaBlade).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should be able to resolve bindings with auto factory', () => { - - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const katanaFactoryId = 'Factory'; - const katanaId = 'Katana'; - const katanaHandlerId = 'KatanaHandler'; - const katanaBladeId = 'KatanaBlade'; - - interface KatanaBlade { } + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const katanaFactoryId: string = 'Factory'; + const katanaId: string = 'Katana'; + const katanaHandlerId: string = 'KatanaHandler'; + const katanaBladeId: string = 'KatanaBlade'; @injectable() - class KatanaBlade implements KatanaBlade { } - - interface KatanaHandler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements KatanaHandler { } + class KatanaHandler {} interface Sword { handler: KatanaHandler; @@ -496,19 +504,17 @@ describe('Resolve', () => { class Katana implements Sword { public handler: KatanaHandler; public blade: KatanaBlade; - public constructor( + constructor( @inject(katanaHandlerId) @targetName('handler') handler: KatanaHandler, - @inject(katanaBladeId) @targetName('blade') blade: KatanaBlade + @inject(katanaBladeId) @targetName('blade') blade: KatanaBlade, ) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katana: Katana; @@ -519,77 +525,79 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Katana; public shuriken: Shuriken; - public constructor( - @inject(katanaFactoryId) @targetName('makeKatana') makeKatana: SwordFactory, - @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken + constructor( + @inject(katanaFactoryId) + @targetName('makeKatana') + makeKatana: SwordFactory, + @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken, ) { this.katana = makeKatana(); // IMPORTANT! this.shuriken = shuriken; } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); container.bind(katanaId).to(Katana); container.bind(katanaBladeId).to(KatanaBlade); container.bind(katanaHandlerId).to(KatanaHandler); - container.bind>(katanaFactoryId).toAutoFactory(katanaId); - - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); - const ninja = resolveTyped(context); + container + .bind>(katanaFactoryId) + .toAutoFactory(katanaId); + + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.katana.handler instanceof KatanaHandler).eql(true); expect(ninja.katana.blade instanceof KatanaBlade).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); - it('Should be able to resolve BindingType.Provider bindings', (done) => { - + it('Should be able to resolve BindingType.Provider bindings', (done: Mocha.Done) => { type SwordProvider = () => Promise; - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const swordProviderId = 'Provider'; - const swordId = 'Sword'; - const handlerId = 'Handler'; - const bladeId = 'Blade'; - - interface Blade { } + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const swordProviderId: string = 'Provider'; + const swordId: string = 'Sword'; + const handlerId: string = 'Handler'; + const bladeId: string = 'Blade'; @injectable() - class KatanaBlade implements Blade { } - - interface Handler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements Handler { } + class KatanaHandler {} interface Sword { - handler: Handler; - blade: Blade; + handler: KatanaHandler; + blade: KatanaBlade; } @injectable() class Katana implements Sword { - public handler: Handler; - public blade: Blade; - public constructor( - @inject(handlerId) @targetName('handler') handler: Handler, - @inject(bladeId) @targetName('handler') blade: Blade + public handler: KatanaHandler; + public blade: KatanaBlade; + constructor( + @inject(handlerId) @targetName('handler') handler: KatanaHandler, + @inject(bladeId) @targetName('handler') blade: KatanaBlade, ) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katana: Katana | null; @@ -602,9 +610,11 @@ describe('Resolve', () => { public katana: Katana | null; public katanaProvider: SwordProvider; public shuriken: Shuriken; - public constructor( - @inject(swordProviderId) @targetName('katanaProvider') katanaProvider: SwordProvider, - @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken + constructor( + @inject(swordProviderId) + @targetName('katanaProvider') + katanaProvider: SwordProvider, + @inject(shurikenId) @targetName('shuriken') shuriken: Shuriken, ) { this.katana = null; this.katanaProvider = katanaProvider; @@ -612,171 +622,206 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); container.bind(swordId).to(Katana); - container.bind(bladeId).to(KatanaBlade); - container.bind(handlerId).to(KatanaHandler); + container.bind(bladeId).to(KatanaBlade); + container.bind(handlerId).to(KatanaHandler); - container.bind(swordProviderId).toProvider((theContext: interfaces.Context) => - () => - new Promise((resolveFunc) => { + container.bind(swordProviderId).toProvider( + (theContext: interfaces.Context) => async () => + new Promise((resolveFunc: (value: Sword) => void) => { // Using setTimeout to simulate complex initialization - setTimeout(() => { resolveFunc(theContext.container.get(swordId)); }, 100); - })); - - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); - - const ninja = resolveTyped(context); + setTimeout(() => { + resolveFunc(theContext.container.get(swordId)); + }, 100); + }), + ); + + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - ninja.katanaProvider().then((katana) => { + void ninja.katanaProvider().then((katana: Sword) => { ninja.katana = katana; expect(ninja.katana instanceof Katana).eql(true); expect(ninja.katana.handler instanceof KatanaHandler).eql(true); expect(ninja.katana.blade instanceof KatanaBlade).eql(true); done(); }); - }); it('Should be able to resolve plans with constraints on tagged targets', () => { - - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} interface Warrior { - katana: Weapon; - shuriken: Weapon; + katana: unknown; + shuriken: unknown; } @injectable() class Ninja implements Warrior { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject('Weapon') @targetName('katana') @tagged('canThrow', false) katana: Weapon, - @inject('Weapon') @targetName('shuriken') @tagged('canThrow', true) shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject('Weapon') + @targetName('katana') + @tagged('canThrow', false) + katana: unknown, + @inject('Weapon') + @targetName('shuriken') + @tagged('canThrow', true) + shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const weaponId = 'Weapon'; + const ninjaId: string = 'Ninja'; + const weaponId: string = 'Weapon'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind(weaponId).to(Katana).whenTargetTagged('canThrow', false); - container.bind(weaponId).to(Shuriken).whenTargetTagged('canThrow', true); + container.bind(weaponId).to(Katana).whenTargetTagged('canThrow', false); + container.bind(weaponId).to(Shuriken).whenTargetTagged('canThrow', true); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should be able to resolve plans with constraints on named targets', () => { - - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} interface Warrior { - katana: Weapon; - shuriken: Weapon; + katana: unknown; + shuriken: unknown; } @injectable() class Ninja implements Warrior { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject('Weapon') @targetName('katana') @named('strong') katana: Weapon, - @inject('Weapon') @targetName('shuriken') @named('weak') shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject('Weapon') + @targetName('katana') + @named('strong') + katana: unknown, + @inject('Weapon') + @targetName('shuriken') + @named('weak') + shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const weaponId = 'Weapon'; + const ninjaId: string = 'Ninja'; + const weaponId: string = 'Weapon'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind(weaponId).to(Katana).whenTargetNamed('strong'); - container.bind(weaponId).to(Shuriken).whenTargetNamed('weak'); + container.bind(weaponId).to(Katana).whenTargetNamed('strong'); + container.bind(weaponId).to(Shuriken).whenTargetNamed('weak'); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); - }); it('Should be able to resolve plans with custom contextual constraints', () => { - - interface Weapon { } - @injectable() - class Katana implements Weapon { } + class Katana {} @injectable() - class Shuriken implements Weapon { } + class Shuriken {} interface Warrior { - katana: Weapon; - shuriken: Weapon; + katana: unknown; + shuriken: unknown; } @injectable() class Ninja implements Warrior { - public katana: Weapon; - public shuriken: Weapon; - public constructor( - @inject('Weapon') @targetName('katana') katana: Weapon, - @inject('Weapon') @targetName('shuriken') shuriken: Weapon + public katana: unknown; + public shuriken: unknown; + constructor( + @inject('Weapon') @targetName('katana') katana: unknown, + @inject('Weapon') @targetName('shuriken') shuriken: unknown, ) { this.katana = katana; this.shuriken = shuriken; } } - const ninjaId = 'Ninja'; - const weaponId = 'Weapon'; + const ninjaId: string = 'Ninja'; + const weaponId: string = 'Weapon'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind(weaponId).to(Katana).when((request: interfaces.Request) => - request.target.name.equals('katana')); - - container.bind(weaponId).to(Shuriken).when((request: interfaces.Request) => - request.target.name.equals('shuriken')); - - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + container + .bind(weaponId) + .to(Katana) + .when((request: interfaces.Request) => + request.target.name.equals('katana'), + ); - const ninja = resolveTyped(context); + container + .bind(weaponId) + .to(Shuriken) + .when((request: interfaces.Request) => + request.target.name.equals('shuriken'), + ); + + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); + + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); @@ -784,19 +829,18 @@ describe('Resolve', () => { }); it('Should be able to resolve plans with multi-injections', () => { - interface Weapon { name: string; } @injectable() class Katana implements Weapon { - public name = 'Katana'; + public name: string = 'Katana'; } @injectable() class Shuriken implements Weapon { - public name = 'Shuriken'; + public name: string = 'Shuriken'; } interface Warrior { @@ -808,58 +852,68 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Weapon; public shuriken: Weapon; - public constructor( - @multiInject('Weapon') @targetName('weapons') weapons: Weapon[] + constructor( + @multiInject('Weapon') @targetName('weapons') weapons: Weapon[], ) { this.katana = weapons[0] as Weapon; this.shuriken = weapons[1] as Weapon; } } - const ninjaId = 'Ninja'; - const weaponId = 'Weapon'; + const ninjaId: string = 'Ninja'; + const weaponId: string = 'Weapon'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(weaponId).to(Katana); container.bind(weaponId).to(Shuriken); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); // if only one value is bound to weaponId - const container2 = new Container(); + const container2: Container = new Container(); container2.bind(ninjaId).to(Ninja); container2.bind(weaponId).to(Katana); - const context2 = plan(new MetadataReader(), container2, false, TargetTypeEnum.Variable, ninjaId); + const context2: interfaces.Context = plan( + new MetadataReader(), + container2, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja2 = resolveTyped(context2); + const ninja2: Ninja = resolveTyped(context2); expect(ninja2 instanceof Ninja).eql(true); expect(ninja2.katana instanceof Katana).eql(true); - }); it('Should be able to resolve plans with async multi-injections', async () => { - interface Weapon { name: string; } @injectable() class Katana implements Weapon { - public name = 'Katana'; + public name: string = 'Katana'; } @injectable() class Shuriken implements Weapon { - public name = 'Shuriken'; + public name: string = 'Shuriken'; } interface Warrior { @@ -871,78 +925,108 @@ describe('Resolve', () => { class Ninja implements Warrior { public katana: Weapon; public shuriken: Weapon; - public constructor( - @multiInject('Weapon') weapons: Weapon[] - ) { + constructor(@multiInject('Weapon') weapons: Weapon[]) { this.katana = weapons[0] as Weapon; this.shuriken = weapons[1] as Weapon; } } - const ninjaId = 'Ninja'; - const weaponId = 'Weapon'; + const ninjaId: string = 'Ninja'; + const weaponId: string = 'Weapon'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); - container.bind(weaponId).toDynamicValue(_ => Promise.resolve(new Katana())); + container + .bind(weaponId) + .toDynamicValue(async (_: interfaces.Context) => + Promise.resolve(new Katana()), + ); container.bind(weaponId).to(Shuriken); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = await resolveTyped>(context); + const ninja: Ninja = await resolveTyped>(context); expect(ninja instanceof Ninja).eql(true); expect(ninja.katana instanceof Katana).eql(true); expect(ninja.shuriken instanceof Shuriken).eql(true); // if only one value is bound to weaponId - const container2 = new Container(); + const container2: Container = new Container(); container2.bind(ninjaId).to(Ninja); - container2.bind(weaponId).toDynamicValue(_ => new Katana()); + container2 + .bind(weaponId) + .toDynamicValue((_: interfaces.Context) => new Katana()); - const context2 = plan(new MetadataReader(), container2, false, TargetTypeEnum.Variable, ninjaId); + const context2: interfaces.Context = plan( + new MetadataReader(), + container2, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja2 = await resolveTyped>(context2); + const ninja2: Ninja = await resolveTyped>(context2); expect(ninja2 instanceof Ninja).eql(true); expect(ninja2.katana instanceof Katana).eql(true); - expect(ninja2.shuriken === undefined).eql(true) - + expect(ninja2.shuriken).to.eq(undefined); }); it('Should be able to resolve plans with async and non async injections', async () => { - const syncPropertyId = 'syncProperty' - const asyncPropertyId = 'asyncProperty' - const syncCtorId = 'syncCtor' - const asyncCtorId = 'asyncCtor' + const syncPropertyId: string = 'syncProperty'; + const asyncPropertyId: string = 'asyncProperty'; + const syncCtorId: string = 'syncCtor'; + const asyncCtorId: string = 'asyncCtor'; @injectable() class CrazyInjectable { - public constructor( - @inject(syncCtorId) readonly syncCtor: string, - @inject(asyncCtorId) readonly asyncCtor: string) { } @inject(syncPropertyId) - public syncProperty!: string + public syncProperty!: string; @inject(asyncPropertyId) - public asyncProperty!: string + public asyncProperty!: string; + + constructor( + @inject(syncCtorId) public readonly syncCtor: string, + @inject(asyncCtorId) public readonly asyncCtor: string, + ) {} } - const crazyInjectableId = 'crazy' - const container = new Container(); + const crazyInjectableId: string = 'crazy'; + const container: Container = new Container(); container.bind(crazyInjectableId).to(CrazyInjectable); - container.bind(syncCtorId).toConstantValue('syncCtor') - container.bind(asyncCtorId).toDynamicValue(_ => Promise.resolve('asyncCtor')) - container.bind(syncPropertyId).toConstantValue('syncProperty') - container.bind(asyncPropertyId).toDynamicValue(_ => Promise.resolve('asyncProperty')) - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, crazyInjectableId); - const crazyInjectable = await resolveTyped>(context); - expect(crazyInjectable.syncCtor).eql('syncCtor') - expect(crazyInjectable.asyncCtor).eql('asyncCtor') - expect(crazyInjectable.syncProperty).eql('syncProperty') - expect(crazyInjectable.asyncProperty).eql('asyncProperty') - + container.bind(syncCtorId).toConstantValue('syncCtor'); + container + .bind(asyncCtorId) + .toDynamicValue(async (_: interfaces.Context) => + Promise.resolve('asyncCtor'), + ); + container.bind(syncPropertyId).toConstantValue('syncProperty'); + container + .bind(asyncPropertyId) + .toDynamicValue(async (_: interfaces.Context) => + Promise.resolve('asyncProperty'), + ); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + crazyInjectableId, + ); + const crazyInjectable: CrazyInjectable = + await resolveTyped>(context); + expect(crazyInjectable.syncCtor).eql('syncCtor'); + expect(crazyInjectable.asyncCtor).eql('asyncCtor'); + expect(crazyInjectable.syncProperty).eql('syncProperty'); + expect(crazyInjectable.asyncProperty).eql('asyncProperty'); }); it('Should be able to resolve plans with activation handlers', () => { - interface Sword { use(): void; } @@ -961,64 +1045,69 @@ describe('Resolve', () => { @injectable() class Ninja implements Warrior { public katana: Katana; - public constructor( - @inject('Katana') katana: Katana - ) { + constructor(@inject('Katana') katana: Katana) { this.katana = katana; } } - const ninjaId = 'Ninja'; - const katanaId = 'Katana'; + const ninjaId: string = 'Ninja'; + const katanaId: string = 'Katana'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); // This is a global for unit testing but remember // that it is not a good idea to use globals const timeTracker: string[] = []; - container.bind(katanaId).to(Katana).onActivation((theContext: interfaces.Context, katana: Katana) => { - const handler = { - apply(target: any, thisArgument: any, argumentsList: any[]) { - timeTracker.push(`Starting ${target.name} ${new Date().getTime()}`); - const result = target.apply(thisArgument, argumentsList); - timeTracker.push(`Finished ${target.name} ${new Date().getTime()}`); - return result; - } - }; - /// create a proxy for method use() own by katana instance about to be injected - katana.use = new Proxy(katana.use, handler); - return katana; - }); + container + .bind(katanaId) + .to(Katana) + .onActivation((_theContext: interfaces.Context, katana: Katana) => { + const handler: ProxyHandler<() => string> = { + apply(target: () => string, thisArgument: Katana, argumentsList: []) { + timeTracker.push( + `Starting ${target.name} ${new Date().getTime().toString()}`, + ); + const result: string = target.apply(thisArgument, argumentsList); + timeTracker.push( + `Finished ${target.name} ${new Date().getTime().toString()}`, + ); + return result; + }, + }; + + katana.use = new Proxy(katana.use, handler); + return katana; + }); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja.katana.use()).eql('Used Katana!'); expect(Array.isArray(timeTracker)).eql(true); expect(timeTracker.length).eql(2); - }); it('Should be able to resolve BindingType.Function bindings', () => { - - const ninjaId = 'Ninja'; - const shurikenId = 'Shuriken'; - const katanaFactoryId = 'KatanaFactory'; + const ninjaId: string = 'Ninja'; + const shurikenId: string = 'Shuriken'; + const katanaFactoryId: string = 'KatanaFactory'; type KatanaFactory = () => Katana; - interface KatanaBlade { } - @injectable() - class KatanaBlade implements KatanaBlade { } - - interface KatanaHandler { } + class KatanaBlade {} @injectable() - class KatanaHandler implements KatanaHandler { } + class KatanaHandler {} interface Sword { handler: KatanaHandler; @@ -1029,16 +1118,14 @@ describe('Resolve', () => { class Katana implements Sword { public handler: KatanaHandler; public blade: KatanaBlade; - public constructor(handler: KatanaHandler, blade: KatanaBlade) { + constructor(handler: KatanaHandler, blade: KatanaBlade) { this.handler = handler; this.blade = blade; } } - interface Shuriken { } - @injectable() - class Shuriken implements Shuriken { } + class Shuriken {} interface Warrior { katanaFactory: KatanaFactory; @@ -1047,29 +1134,39 @@ describe('Resolve', () => { @injectable() class Ninja implements Warrior { - public constructor( - @inject(katanaFactoryId) @targetName('katana') public katanaFactory: KatanaFactory, - @inject(shurikenId) @targetName('shuriken') public shuriken: Shuriken - ) { - } + constructor( + @inject(katanaFactoryId) + @targetName('katana') + public katanaFactory: KatanaFactory, + @inject(shurikenId) @targetName('shuriken') public shuriken: Shuriken, + ) {} } - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(shurikenId).to(Shuriken); - const katanaFactoryInstance = function () { + const katanaFactoryInstance: () => Katana = function () { return new Katana(new KatanaHandler(), new KatanaBlade()); }; - container.bind(katanaFactoryId).toFunction(katanaFactoryInstance); + container + .bind(katanaFactoryId) + .toFunction(katanaFactoryInstance); - const katanaFactoryBinding = getBindingDictionary(container).get(katanaFactoryId)[0]; + const katanaFactoryBinding: interfaces.Binding | undefined = + getBindingDictionary(container).get(katanaFactoryId)[0]; expect(katanaFactoryBinding?.activated).eql(false); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja instanceof Ninja).eql(true); expect(typeof ninja.katanaFactory === 'function').eql(true); @@ -1080,11 +1177,9 @@ describe('Resolve', () => { expect(katanaFactoryBinding?.activated).eql(true); expect(katanaFactoryBinding?.activated).eql(true); - }); it('Should run the @PostConstruct method', () => { - interface Sword { use(): string; } @@ -1093,14 +1188,14 @@ describe('Resolve', () => { class Katana implements Sword { private useMessage!: string; - public use() { - return this.useMessage; - } - @postConstruct() public postConstruct() { this.useMessage = 'Used Katana!'; } + + public use() { + return this.useMessage; + } } interface Warrior { @@ -1110,43 +1205,51 @@ describe('Resolve', () => { @injectable() class Ninja implements Warrior { public katana: Katana; - public constructor(@inject('Katana') katana: Katana) { + constructor(@inject('Katana') katana: Katana) { this.katana = katana; } } - const ninjaId = 'Ninja'; - const katanaId = 'Katana'; + const ninjaId: string = 'Ninja'; + const katanaId: string = 'Katana'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(katanaId).to(Katana); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja.katana.use()).eql('Used Katana!'); - }); it('Should throw an error if the @postConstruct method throws an error', () => { - @injectable() class Katana { - @postConstruct() public postConstruct() { throw new Error('Original Message'); } } - expect(() => resolveInstance({} as interfaces.Binding, Katana, [], () => null)) - .to.throw('@postConstruct error in class Katana: Original Message'); + expect(() => + resolveInstance( + {} as interfaces.Binding, + Katana, + [], + () => null, + ), + ).to.throw('@postConstruct error in class Katana: Original Message'); }); it('Should run the @PostConstruct method of parent class', () => { - interface Weapon { use(): string; } @@ -1177,28 +1280,33 @@ describe('Resolve', () => { @injectable() class Ninja implements Warrior { public katana: Katana; - public constructor(@inject('Katana') katana: Katana) { + constructor(@inject('Katana') katana: Katana) { this.katana = katana; } } - const ninjaId = 'Ninja'; - const katanaId = 'Katana'; + const ninjaId: string = 'Ninja'; + const katanaId: string = 'Katana'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(katanaId).to(Katana); - const context = plan(new MetadataReader(), container, false, TargetTypeEnum.Variable, ninjaId); + const context: interfaces.Context = plan( + new MetadataReader(), + container, + false, + TargetTypeEnum.Variable, + ninjaId, + ); - const ninja = resolveTyped(context); + const ninja: Ninja = resolveTyped(context); expect(ninja.katana.use()).eql('Used Weapon!'); - }); it('Should run the @PostConstruct method once in the singleton scope', () => { - let timesCalled = 0; + let timesCalled: number = 0; @injectable() class Katana { @postConstruct() @@ -1210,7 +1318,7 @@ describe('Resolve', () => { @injectable() class Ninja { public katana: Katana; - public constructor(@inject('Katana') katana: Katana) { + constructor(@inject('Katana') katana: Katana) { this.katana = katana; } } @@ -1218,60 +1326,64 @@ describe('Resolve', () => { @injectable() class Samurai { public katana: Katana; - public constructor(@inject('Katana') katana: Katana) { + constructor(@inject('Katana') katana: Katana) { this.katana = katana; } } - const ninjaId = 'Ninja'; - const samuraiId = 'Samurai'; - const katanaId = 'Katana'; + const ninjaId: string = 'Ninja'; + const samuraiId: string = 'Samurai'; + const katanaId: string = 'Katana'; - const container = new Container(); + const container: Container = new Container(); container.bind(ninjaId).to(Ninja); container.bind(samuraiId).to(Samurai); container.bind(katanaId).to(Katana).inSingletonScope(); container.get(ninjaId); container.get(samuraiId); expect(timesCalled).to.be.equal(1); - }); it('Should not cache bindings if a dependency in the async chain fails', async () => { - let level2Attempts = 0; + let level2Attempts: number = 0; @injectable() class Level2 { public value: string; - public constructor(@inject('level1') value: string) { + constructor(@inject('level1') value: string) { level2Attempts += 1; this.value = value; } } - let level1Attempts = 0; + let level1Attempts: number = 0; - const container = new Container({ defaultScope: 'Singleton', autoBindInjectable: true }); - container.bind('level1').toDynamicValue(async (context) => { - level1Attempts += 1; + const container: Container = new Container({ + autoBindInjectable: true, + defaultScope: 'Singleton', + }); + container + .bind('level1') + .toDynamicValue(async (_context: interfaces.Context) => { + level1Attempts += 1; - if (level1Attempts === 1) { - throw new Error('first try failed.'); - } + if (level1Attempts === 1) { + throw new Error('first try failed.'); + } - return 'foobar'; - }); + return 'foobar'; + }); container.bind('a').to(Level2); try { await container.getAsync('a'); throw new Error('should have failed on first invocation.'); - } catch (ex) { - // ignore + } catch (_e: unknown) { + /* empty */ } - const level2 = await container.getAsync('a'); + const level2: Level2 = await container.getAsync('a'); expect(level2.value).equals('foobar'); expect(level1Attempts).equals(2); @@ -1279,56 +1391,69 @@ describe('Resolve', () => { }); it('Should support async when default scope is singleton', async () => { - const container = new Container({ defaultScope: 'Singleton' }); + const container: Container = new Container({ defaultScope: 'Singleton' }); container.bind('a').toDynamicValue(async () => Math.random()); - const object1 = await container.getAsync('a'); - const object2 = await container.getAsync('a'); + const object1: unknown = await container.getAsync('a'); + const object2: unknown = await container.getAsync('a'); expect(object1).equals(object2); }); it('Should return different values if default singleton scope is overriden by bind', async () => { - const container = new Container({ defaultScope: 'Singleton' }); - container.bind('a').toDynamicValue(async () => Math.random()).inTransientScope(); + const container: Container = new Container({ defaultScope: 'Singleton' }); + container + .bind('a') + .toDynamicValue(async () => Math.random()) + .inTransientScope(); - const object1 = await container.getAsync('a'); - const object2 = await container.getAsync('a'); + const object1: unknown = await container.getAsync('a'); + const object2: unknown = await container.getAsync('a'); expect(object1).not.equals(object2); }); it('Should only call parent async singleton once within child containers', async () => { - const parent = new Container(); - parent.bind('Parent').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); - const child = parent.createChild(); - const [subject1, subject2] = await Promise.all([ + const parent: Container = new Container(); + parent + .bind('Parent') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); + const child: Container = parent.createChild(); + const [subject1, subject2]: [Date, Date] = await Promise.all([ + child.getAsync('Parent'), child.getAsync('Parent'), - child.getAsync('Parent') ]); expect(subject1 === subject2).eql(true); }); it('should not deactivate a non activated constant value', () => { - const container = new Container(); - container.bind('ConstantValue').toConstantValue('Constant').onDeactivation(sinon.mock().never()); + const container: Container = new Container(); + container + .bind('ConstantValue') + .toConstantValue('Constant') + .onDeactivation(sinon.mock().never()); container.unbind('ConstantValue'); }); - it('Should return resolved instance to onDeactivation when binding is async', async () => { @injectable() - class Destroyable { - } + class Destroyable {} - const container = new Container(); - let deactivatedDestroyable: Destroyable | null = null - container.bind('Destroyable').toDynamicValue(() => Promise.resolve(new Destroyable())).inSingletonScope() - .onDeactivation((instance) => new Promise((r) => { - deactivatedDestroyable = instance - r(); - })); + const container: Container = new Container(); + let deactivatedDestroyable: Destroyable | null = null; + container + .bind('Destroyable') + .toDynamicValue(async () => Promise.resolve(new Destroyable())) + .inSingletonScope() + .onDeactivation( + async (instance: Destroyable) => + new Promise((resolve: () => void) => { + deactivatedDestroyable = instance; + resolve(); + }), + ); await container.getAsync('Destroyable'); @@ -1337,13 +1462,18 @@ describe('Resolve', () => { expect(deactivatedDestroyable).instanceof(Destroyable); // with BindingInWhenOnSyntax - const container2 = new Container({ defaultScope: 'Singleton' }); - let deactivatedDestroyable2: Destroyable | null = null - container2.bind('Destroyable').toDynamicValue(() => Promise.resolve(new Destroyable())) - .onDeactivation((instance) => new Promise((r) => { - deactivatedDestroyable2 = instance - r(); - })); + const container2: Container = new Container({ defaultScope: 'Singleton' }); + let deactivatedDestroyable2: Destroyable | null = null; + container2 + .bind('Destroyable') + .toDynamicValue(async () => Promise.resolve(new Destroyable())) + .onDeactivation( + async (instance: Destroyable) => + new Promise((resolve: () => void) => { + deactivatedDestroyable2 = instance; + resolve(); + }), + ); await container2.getAsync('Destroyable'); @@ -1353,19 +1483,24 @@ describe('Resolve', () => { }); it('Should wait on deactivation promise before returning unbindAsync()', async () => { - let resolved = false; + let resolved: boolean = false; @injectable() - class Destroyable { - } + class Destroyable {} - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope() - .onDeactivation(() => new Promise((r) => { - r(); - - resolved = true; - })); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation( + async () => + new Promise((resolve: () => void) => { + resolve(); + + resolved = true; + }), + ); container.get('Destroyable'); @@ -1375,22 +1510,25 @@ describe('Resolve', () => { }); it('Should wait on predestroy promise before returning unbindAsync()', async () => { - let resolved = false; + let resolved: boolean = false; @injectable() class Destroyable { @preDestroy() - public myPreDestroyMethod() { - return new Promise((r) => { - r({}); + public async myPreDestroyMethod() { + return new Promise((resolve: (value: unknown) => void) => { + resolve({}); resolved = true; }); } } - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope(); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope(); container.get('Destroyable'); @@ -1400,19 +1538,24 @@ describe('Resolve', () => { }); it('Should wait on deactivation promise before returning unbindAllAsync()', async () => { - let resolved = false; + let resolved: boolean = false; @injectable() - class Destroyable { - } + class Destroyable {} - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope() - .onDeactivation(() => new Promise((r) => { - r(); - - resolved = true; - })); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation( + async () => + new Promise((resolve: () => void) => { + resolve(); + + resolved = true; + }), + ); container.get('Destroyable'); @@ -1422,22 +1565,25 @@ describe('Resolve', () => { }); it('Should wait on predestroy promise before returning unbindAllAsync()', async () => { - let resolved = false; + let resolved: boolean = false; @injectable() class Destroyable { @preDestroy() - public myPreDestroyMethod() { - return new Promise((r) => { - r({}); + public async myPreDestroyMethod() { + return new Promise((resolve: (value: unknown) => void) => { + resolve({}); resolved = true; }); } } - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope(); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope(); container.get('Destroyable'); @@ -1447,31 +1593,32 @@ describe('Resolve', () => { }); it('Should call bind.cache.then on unbind w/ PromiseLike binding', async () => { - - const bindStub = sinon.stub().callsFake(() => { + const bindStub: sinon.SinonStub = sinon.stub().callsFake(() => { return { - serviceIdentifier: 'PromiseLike' + serviceIdentifier: 'PromiseLike', }; }); - const stub = sinon.stub().callsFake((bindResolve) => { - bindResolve(bindStub()); - }); + const stub: sinon.SinonStub = sinon + .stub() + .callsFake((bindResolve: (value: unknown) => void) => { + bindResolve(bindStub()); + }); @injectable() class PromiseLike { public then() { return { - then: stub + then: stub, }; } } - const container = new Container(); + const container: Container = new Container(); container.bind('PromiseLike').toConstantValue(new PromiseLike()); - container.getAsync('PromiseLike'); + void container.getAsync('PromiseLike'); container.unbindAll(); @@ -1483,29 +1630,36 @@ describe('Resolve', () => { @injectable() class Destroyable { @preDestroy() - public myPreDestroyMethod() { + public async myPreDestroyMethod() { return Promise.resolve(); } } - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inTransientScope(); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inTransientScope(); - expect(() => container.get('Destroyable')).to - .throw('@preDestroy error in class Destroyable: Class cannot be instantiated in transient scope.'); + expect(() => container.get('Destroyable')).to.throw( + '@preDestroy error in class Destroyable: Class cannot be instantiated in transient scope.', + ); }); it('Should not allow transient construction with async deactivation', async () => { @injectable() - class Destroyable { - } + class Destroyable {} - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inTransientScope() - .onDeactivation(() => Promise.resolve()); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inTransientScope() + .onDeactivation(async () => Promise.resolve()); - expect(() => container.get('Destroyable')).to - .throw('onDeactivation() error in class Destroyable: Class cannot be instantiated in transient scope.'); + expect(() => container.get('Destroyable')).to.throw( + 'onDeactivation() error in class Destroyable: Class cannot be instantiated in transient scope.', + ); }); it('Should not allow request construction with preDestroy', async () => { @@ -1517,154 +1671,188 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('Destroyable').to(Destroyable).inRequestScope(); - expect(() => container.get('Destroyable')).to - .throw('@preDestroy error in class Destroyable: Class cannot be instantiated in request scope.'); + expect(() => container.get('Destroyable')).to.throw( + '@preDestroy error in class Destroyable: Class cannot be instantiated in request scope.', + ); }); it('Should not allow request construction with deactivation', async () => { @injectable() - class Destroyable { - } + class Destroyable {} - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inRequestScope() + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inRequestScope() .onDeactivation(() => { // }); - expect(() => container.get('Destroyable')).to - .throw('onDeactivation() error in class Destroyable: Class cannot be instantiated in request scope.'); + expect(() => container.get('Destroyable')).to.throw( + 'onDeactivation() error in class Destroyable: Class cannot be instantiated in request scope.', + ); }); it('Should force a class with an async deactivation to use the async unbindAll api', async () => { @injectable() - class Destroyable { - } + class Destroyable {} - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope() - .onDeactivation(() => Promise.resolve()); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation(async () => Promise.resolve()); container.get('Destroyable'); - expect(() => container.unbindAll()).to - .throw('Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)'); + expect(() => container.unbindAll()).to.throw( + 'Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)', + ); }); it('Should force a class with an async pre destroy to use the async unbindAll api', async () => { @injectable() class Destroyable { @preDestroy() - public myPreDestroyMethod() { + public async myPreDestroyMethod() { return Promise.resolve(); } } - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope(); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope(); container.get('Destroyable'); - expect(() => container.unbindAll()).to - .throw('Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)'); + expect(() => container.unbindAll()).to.throw( + 'Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)', + ); }); it('Should force a class with an async deactivation to use the async unbind api', async () => { @injectable() - class Destroyable { - } + class Destroyable {} - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope() - .onDeactivation(() => Promise.resolve()); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation(async () => Promise.resolve()); container.get('Destroyable'); - expect(() => container.unbind('Destroyable')).to - .throw('Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)'); + expect(() => container.unbind('Destroyable')).to.throw( + 'Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)', + ); }); it('Should throw deactivation error when errors in deactivation ( sync )', () => { @injectable() - class Destroyable { - } - const errorMessage = 'the error message' - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope() - .onDeactivation(() => { throw new Error(errorMessage) }); + class Destroyable {} + const errorMessage: string = 'the error message'; + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation(() => { + throw new Error(errorMessage); + }); container.get('Destroyable'); - const expectedErrorMessage = ERROR_MSGS.ON_DEACTIVATION_ERROR('Destroyable', errorMessage) + const expectedErrorMessage: string = ERROR_MSGS.ON_DEACTIVATION_ERROR( + 'Destroyable', + errorMessage, + ); - expect(() => container.unbind('Destroyable')).to - .throw(expectedErrorMessage); - }) + expect(() => container.unbind('Destroyable')).to.throw( + expectedErrorMessage, + ); + }); it('Should throw deactivation error when errors in deactivation ( async )', async () => { @injectable() - class Destroyable { - } - const errorMessage = 'the error message' - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope() - .onDeactivation(() => Promise.reject(new Error(errorMessage))); + class Destroyable {} + const errorMessage: string = 'the error message'; + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation(async () => Promise.reject(new Error(errorMessage))); container.get('Destroyable'); - const expectedErrorMessage = ERROR_MSGS.ON_DEACTIVATION_ERROR('Destroyable', errorMessage) + const expectedErrorMessage: string = ERROR_MSGS.ON_DEACTIVATION_ERROR( + 'Destroyable', + errorMessage, + ); - let error: Error | unknown; + let error: unknown; try { - await container.unbindAsync('Destroyable') - } catch (e) { - error = e + await container.unbindAsync('Destroyable'); + } catch (e: unknown) { + error = e; } - expect((error as Error).message).to.eql(expectedErrorMessage) - }) + expect((error as Error).message).to.eql(expectedErrorMessage); + }); it('Should invoke destroy in order (all async): child container, parent container, binding, class', async () => { - let roll = 1; - let binding = null; - let klass = null; - let parent = null; - let child = null; + let roll: number = 1; + let binding: number | null = null; + let klass: number | null = null; + let parent: number | null = null; + let child: number | null = null; @injectable() class Destroyable { @preDestroy() - public myPreDestroyMethod() { - return new Promise((presolve) => { + public async myPreDestroyMethod() { + return new Promise((resolve: (value: unknown) => void) => { klass = roll; roll += 1; - presolve({}); + resolve({}); }); } } - const container = new Container(); - container.onDeactivation('Destroyable', () => { - return new Promise((presolve) => { + const container: Container = new Container(); + container.onDeactivation('Destroyable', async () => { + return new Promise((resolve: () => void) => { parent = roll; roll += 1; - presolve(); + resolve(); }); }); - const childContainer = container.createChild(); - childContainer.bind('Destroyable').to(Destroyable).inSingletonScope().onDeactivation(() => new Promise((presolve) => { - binding = roll; - roll += 1; - presolve(); - })); - childContainer.onDeactivation('Destroyable', () => { - return new Promise((presolve) => { + const childContainer: Container = container.createChild(); + childContainer + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation( + async () => + new Promise((resolve: () => void) => { + binding = roll; + roll += 1; + resolve(); + }), + ); + childContainer.onDeactivation('Destroyable', async () => { + return new Promise((resolve: () => void) => { child = roll; roll += 1; - presolve(); + resolve(); }); }); @@ -1679,40 +1867,44 @@ describe('Resolve', () => { }); it('Should invoke destroy in order (sync + async): child container, parent container, binding, class', async () => { - let roll = 1; - let binding = null; - let klass = null; - let parent = null; - let child = null; + let roll: number = 1; + let binding: number | null = null; + let klass: number | null = null; + let parent: number | null = null; + let child: number | null = null; @injectable() class Destroyable { @preDestroy() - public myPreDestroyMethod() { - return new Promise((presolve) => { + public async myPreDestroyMethod() { + return new Promise((resolve: (value: unknown) => void) => { klass = roll; roll += 1; - presolve({}); + resolve({}); }); } } - const container = new Container(); + const container: Container = new Container(); container.onDeactivation('Destroyable', () => { parent = roll; roll += 1; }); - const childContainer = container.createChild(); - childContainer.bind('Destroyable').to(Destroyable).inSingletonScope().onDeactivation(() => { - binding = roll; - roll += 1; - }); - childContainer.onDeactivation('Destroyable', () => { - return new Promise((presolve) => { + const childContainer: Container = container.createChild(); + childContainer + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation(() => { + binding = roll; + roll += 1; + }); + childContainer.onDeactivation('Destroyable', async () => { + return new Promise((resolve: () => void) => { child = roll; roll += 1; - presolve(); + resolve(); }); }); @@ -1727,11 +1919,11 @@ describe('Resolve', () => { }); it('Should invoke destroy in order (all sync): child container, parent container, binding, class', () => { - let roll = 1; - let binding = null; - let klass = null; - let parent = null; - let child = null; + let roll: number = 1; + let binding: number | null = null; + let klass: number | null = null; + let parent: number | null = null; + let child: number | null = null; @injectable() class Destroyable { @@ -1742,17 +1934,21 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.onDeactivation('Destroyable', () => { parent = roll; roll += 1; }); - const childContainer = container.createChild(); - childContainer.bind('Destroyable').to(Destroyable).inSingletonScope().onDeactivation(() => { - binding = roll; - roll += 1; - }); + const childContainer: Container = container.createChild(); + childContainer + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation(() => { + binding = roll; + roll += 1; + }); childContainer.onDeactivation('Destroyable', () => { child = roll; roll += 1; @@ -1769,11 +1965,11 @@ describe('Resolve', () => { }); it('Should invoke destroy in order (async): child container, parent container, binding, class', async () => { - let roll = 1; - let binding = null; - let klass = null; - let parent = null; - let child = null; + let roll: number = 1; + let binding: number | null = null; + let klass: number | null = null; + let parent: number | null = null; + let child: number | null = null; @injectable() class Destroyable { @@ -1784,17 +1980,21 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.onDeactivation('Destroyable', async () => { parent = roll; roll += 1; }); - const childContainer = container.createChild(); - childContainer.bind('Destroyable').to(Destroyable).inSingletonScope().onDeactivation(() => { - binding = roll; - roll += 1; - }); + const childContainer: Container = container.createChild(); + childContainer + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope() + .onDeactivation(() => { + binding = roll; + roll += 1; + }); childContainer.onDeactivation('Destroyable', () => { child = roll; roll += 1; @@ -1814,114 +2014,132 @@ describe('Resolve', () => { @injectable() class Destroyable { @preDestroy() - public myPreDestroyMethod() { + public async myPreDestroyMethod() { return Promise.resolve(); } } - const container = new Container(); - container.bind('Destroyable').to(Destroyable).inSingletonScope(); + const container: Container = new Container(); + container + .bind('Destroyable') + .to(Destroyable) + .inSingletonScope(); container.get('Destroyable'); - expect(() => container.unbind('Destroyable')).to - .throw('Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)'); + expect(() => container.unbind('Destroyable')).to.throw( + 'Attempting to unbind dependency with asynchronous destruction (@preDestroy or onDeactivation)', + ); }); it('Should force a class with an async onActivation to use the async api', async () => { @injectable() - class Constructable { - } + class Constructable {} - const container = new Container(); - container.bind('Constructable').to(Constructable).inSingletonScope() - .onActivation(() => Promise.resolve()); + const container: Container = new Container(); + container + .bind('Constructable') + .to(Constructable) + .inSingletonScope() + .onActivation(async () => Promise.resolve()); - expect(() => container.get('Constructable')).to.throw(`You are attempting to construct 'Constructable' in a synchronous way but it has asynchronous dependencies.`); + expect(() => container.get('Constructable')).to.throw( + `You are attempting to construct 'Constructable' in a synchronous way but it has asynchronous dependencies.`, + ); }); it('Should force a class with an async post construct to use the async api', async () => { @injectable() class Constructable { @postConstruct() - public myPostConstructMethod() { + public async myPostConstructMethod() { return Promise.resolve(); } } - const container = new Container(); + const container: Container = new Container(); container.bind('Constructable').to(Constructable); - expect(() => container.get('Constructable')).to.throw(`You are attempting to construct 'Constructable' in a synchronous way but it has asynchronous dependencies.`); + expect(() => container.get('Constructable')).to.throw( + `You are attempting to construct 'Constructable' in a synchronous way but it has asynchronous dependencies.`, + ); }); it('Should retry promise if first time failed', async () => { @injectable() - class Constructable { - } + class Constructable {} - let attemped = false; + let attemped: boolean = false; - const container = new Container(); - container.bind('Constructable').toDynamicValue(() => { - if (attemped) { - return Promise.resolve(new Constructable()); - } + const container: Container = new Container(); + container + .bind('Constructable') + .toDynamicValue(async () => { + if (attemped) { + return Promise.resolve(new Constructable()); + } - attemped = true; + attemped = true; - return Promise.reject('break'); - }).inSingletonScope(); + // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors + return Promise.reject('break'); + }) + .inSingletonScope(); try { await container.getAsync('Constructable'); throw new Error('should have thrown exception.'); - } catch (ex) { + } catch (_e: unknown) { await container.getAsync('Constructable'); } }); it('Should return resolved instance to onActivation when binding is async', async () => { @injectable() - class Constructable { - } - let activated: Constructable | null = null - const container = new Container(); - container.bind('Constructable').toDynamicValue(() => Promise.resolve(new Constructable())).inSingletonScope() - .onActivation((context, c) => new Promise((r) => { - activated = c - r(c); - })); + class Constructable {} + let activated: Constructable | null = null; + const container: Container = new Container(); + container + .bind('Constructable') + .toDynamicValue(async () => Promise.resolve(new Constructable())) + .inSingletonScope() + .onActivation( + async (_context: interfaces.Context, c: Constructable) => + new Promise((resolve: (value: Constructable) => void) => { + activated = c; + resolve(c); + }), + ); await container.getAsync('Constructable'); expect(activated).instanceof(Constructable); }); it('Should not allow sync get if an async activation was added to container', async () => { - const container = new Container(); + const container: Container = new Container(); container.bind('foo').toConstantValue('bar'); - container.onActivation('foo', () => Promise.resolve('baz')); + container.onActivation('foo', async () => Promise.resolve('baz')); - expect(() => container.get('foo')).to.throw(`You are attempting to construct 'foo' in a synchronous way but it has asynchronous dependencies.`); + expect(() => container.get('foo')).to.throw( + `You are attempting to construct 'foo' in a synchronous way but it has asynchronous dependencies.`, + ); }); it('Should allow onActivation (sync) of a previously binded sync object (without activation)', async () => { - const container = new Container(); + const container: Container = new Container(); container.bind('foo').toConstantValue('bar'); container.onActivation('foo', () => 'baz'); - const result = container.get('foo'); + const result: unknown = container.get('foo'); expect(result).eql('baz'); }); it('Should allow onActivation to replace objects in async autoBindInjectable chain', async () => { - class Level1 { - - } + class Level1 {} @injectable() class Level2 { @@ -1941,384 +2159,473 @@ describe('Resolve', () => { } } - const constructedLevel2 = new Level2(new Level1()); + const constructedLevel2: Level2 = new Level2(new Level1()); - const container = new Container({ autoBindInjectable: true, defaultScope: 'Singleton' }); - container.bind(Level1).toDynamicValue(() => Promise.resolve(new Level1())); - container.onActivation(Level2, () => { - return Promise.resolve(constructedLevel2); + const container: Container = new Container({ + autoBindInjectable: true, + defaultScope: 'Singleton', }); + container + .bind(Level1) + .toDynamicValue(async () => Promise.resolve(new Level1())); + container.onActivation(Level2, async () => + Promise.resolve(constructedLevel2), + ); - const level2 = await container.getAsync(Level2); + const level2: Level2 = await container.getAsync(Level2); expect(level2).equals(constructedLevel2); - const level3 = await container.getAsync(Level3); + const level3: Level3 = await container.getAsync(Level3); expect(level3.level2).equals(constructedLevel2); }); it('Should allow onActivation (async) of a previously binded sync object (without activation)', async () => { - const container = new Container(); + const container: Container = new Container(); container.bind('foo').toConstantValue('bar'); - container.onActivation('foo', () => Promise.resolve('baz')); + container.onActivation('foo', async () => Promise.resolve('baz')); - const result = await container.getAsync('foo'); + const result: unknown = await container.getAsync('foo'); expect(result).eql('baz'); }); it('Should allow onActivation (sync) of a previously binded async object (without activation)', async () => { - const container = new Container(); - container.bind('foo').toDynamicValue(() => Promise.resolve('bar')); + const container: Container = new Container(); + container.bind('foo').toDynamicValue(async () => Promise.resolve('bar')); container.onActivation('foo', () => 'baz'); - const result = await container.getAsync('foo'); + const result: unknown = await container.getAsync('foo'); expect(result).eql('baz'); }); it('Should allow onActivation (async) of a previously binded async object (without activation)', async () => { - const container = new Container(); - container.bind('foo').toDynamicValue(() => Promise.resolve('bar')); + const container: Container = new Container(); + container.bind('foo').toDynamicValue(async () => Promise.resolve('bar')); - container.onActivation('foo', () => Promise.resolve('baz')); + container.onActivation('foo', async () => Promise.resolve('baz')); - const result = await container.getAsync('foo'); + const result: unknown = await container.getAsync('foo'); expect(result).eql('baz'); }); it('Should allow onActivation (sync) of a previously binded sync object (with activation)', async () => { - const container = new Container(); - container.bind('foo').toConstantValue('bar').onActivation(() => 'bum'); + const container: Container = new Container(); + container + .bind('foo') + .toConstantValue('bar') + .onActivation(() => 'bum'); - container.onActivation('foo', (context, previous) => `${previous}baz`); + container.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (_context: interfaces.Context, previous: unknown) => `${previous}baz`, + ); - const result = container.get('foo'); + const result: unknown = container.get('foo'); expect(result).eql('bumbaz'); }); it('Should allow onActivation (async) of a previously binded sync object (with activation)', async () => { - const container = new Container(); - container.bind('foo').toConstantValue('bar').onActivation(() => 'bum'); + const container: Container = new Container(); + container + .bind('foo') + .toConstantValue('bar') + .onActivation(() => 'bum'); - container.onActivation('foo', (context, previous) => Promise.resolve(`${previous}baz`)); + container.onActivation( + 'foo', + async (_context: interfaces.Context, previous: unknown) => + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + Promise.resolve(`${previous}baz`), + ); - const result = await container.getAsync('foo'); + const result: unknown = await container.getAsync('foo'); expect(result).eql('bumbaz'); }); it('Should allow onActivation (sync) of a previously binded async object (with activation)', async () => { - const container = new Container(); - container.bind('foo').toDynamicValue(() => Promise.resolve('bar')).onActivation(() => 'bum'); + const container: Container = new Container(); + container + .bind('foo') + .toDynamicValue(async () => Promise.resolve('bar')) + .onActivation(() => 'bum'); - container.onActivation('foo', (context, previous) => `${previous}baz`); + container.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (_context: interfaces.Context, previous: unknown) => `${previous}baz`, + ); - const result = await container.getAsync('foo'); + const result: unknown = await container.getAsync('foo'); expect(result).eql('bumbaz'); }); it('Should allow onActivation (async) of a previously binded async object (with activation)', async () => { - const container = new Container(); - container.bind('foo').toDynamicValue(() => Promise.resolve('bar')).onActivation(() => 'bum'); + const container: Container = new Container(); + container + .bind('foo') + .toDynamicValue(async () => Promise.resolve('bar')) + .onActivation(() => 'bum'); - container.onActivation('foo', (context, previous) => Promise.resolve(`${previous}baz`)); + container.onActivation( + 'foo', + async (_context: interfaces.Context, previous: unknown) => + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + Promise.resolve(`${previous}baz`), + ); - const result = await container.getAsync('foo'); + const result: unknown = await container.getAsync('foo'); expect(result).eql('bumbaz'); }); it('Should allow onActivation (sync) of parent (async) through autobind tree', async () => { - class Parent { - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())); - const constructed = new Parent(); + const constructed: Parent = new Parent(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore constructed.foo = 'bar'; container.onActivation(Parent, () => constructed); - const result = await container.getAsync(Child); + const result: Child = await container.getAsync(Child); expect(result.parent).equals(constructed); }); it('Should allow onActivation (sync) of child (async) through autobind tree', async () => { - class Parent { - - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())); - const constructed = new Child(new Parent()); + const constructed: Child = new Child(new Parent()); container.onActivation(Child, () => constructed); - const result = await container.getAsync(Child); + const result: Child = await container.getAsync(Child); expect(result).equals(constructed); }); it('Should allow onActivation (async) of parent (async) through autobind tree', async () => { - class Parent { - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())); - const constructed = new Parent(); + const constructed: Parent = new Parent(); - container.onActivation(Parent, () => Promise.resolve(constructed)); + container.onActivation(Parent, async () => Promise.resolve(constructed)); - const result = await container.getAsync(Child); + const result: Child = await container.getAsync(Child); expect(result.parent).equals(constructed); }); it('Should allow onActivation (async) of child (async) through autobind tree', async () => { - class Parent { - - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())); - const constructed = new Child(new Parent()); + const constructed: Child = new Child(new Parent()); - container.onActivation(Child, () => Promise.resolve(constructed)); + container.onActivation(Child, async () => Promise.resolve(constructed)); - const result = await container.getAsync(Child); + const result: Child = await container.getAsync(Child); expect(result).equals(constructed); }); it('Should allow onActivation of child on parent container', async () => { - class Parent { - - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())); - const constructed = new Child(new Parent()); + const constructed: Child = new Child(new Parent()); - container.onActivation(Child, () => Promise.resolve(constructed)); + container.onActivation(Child, async () => Promise.resolve(constructed)); - const child = container.createChild(); + const child: Container = container.createChild(); - const result = await child.getAsync(Child); + const result: Child = await child.getAsync(Child); expect(result).equals(constructed); }); it('Should allow onActivation of parent on parent container', async () => { - class Parent { - - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())); - const constructed = new Parent(); + const constructed: Parent = new Parent(); - container.onActivation(Parent, () => Promise.resolve(constructed)); + container.onActivation(Parent, async () => Promise.resolve(constructed)); - const child = container.createChild(); + const child: Container = container.createChild(); - const result = await child.getAsync(Child); + const result: Child = await child.getAsync(Child); expect(result.parent).equals(constructed); }); it('Should allow onActivation of child from child container', async () => { - class Parent { - - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())); - const constructed = new Child(new Parent()); + const constructed: Child = new Child(new Parent()); - const child = container.createChild(); - child.onActivation(Child, () => Promise.resolve(constructed)); + const child: Container = container.createChild(); + child.onActivation(Child, async () => Promise.resolve(constructed)); - const result = await child.getAsync(Child); + const result: Child = await child.getAsync(Child); expect(result).equals(constructed); }); it('Should priortize onActivation of parent container over child container', () => { - const container = new Container(); - container.onActivation('foo', (context, previous) => `${previous}baz`); - container.onActivation('foo', (context, previous) => `${previous}1`); - - const child = container.createChild(); - - child.bind('foo').toConstantValue('bar').onActivation((c, previous) => `${previous}bah`); - child.onActivation('foo', (context, previous) => `${previous}bum`); - child.onActivation('foo', (context, previous) => `${previous}2`); - - const result = child.get('foo'); + const container: Container = new Container(); + container.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (_context: interfaces.Context, previous: unknown) => `${previous}baz`, + ); + container.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (_context: interfaces.Context, previous: unknown) => `${previous}1`, + ); + + const child: Container = container.createChild(); + + child + .bind('foo') + .toConstantValue('bar') + .onActivation( + (_context: interfaces.Context, previous: string) => `${previous}bah`, + ); + child.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (_context: interfaces.Context, previous: unknown) => `${previous}bum`, + ); + child.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (_context: interfaces.Context, previous: unknown) => `${previous}2`, + ); + + const result: unknown = child.get('foo'); expect(result).equals('barbahbaz1bum2'); }); it('Should priortize async onActivation of parent container over child container (async)', async () => { - const container = new Container(); - container.onActivation('foo', async (context, previous) => `${previous}baz`); - container.onActivation('foo', async (context, previous) => `${previous}1`); - - const child = container.createChild(); - - child.bind('foo').toConstantValue('bar').onActivation((c, previous) => `${previous}bah`); - child.onActivation('foo', async (context, previous) => `${previous}bum`); - child.onActivation('foo', async (context, previous) => `${previous}2`); - - const result = await child.getAsync('foo'); + const container: Container = new Container(); + container.onActivation( + 'foo', + async (_context: interfaces.Context, previous: unknown) => + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `${previous}baz`, + ); + container.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + async (_context: interfaces.Context, previous: unknown) => `${previous}1`, + ); + + const child: Container = container.createChild(); + + child + .bind('foo') + .toConstantValue('bar') + .onActivation( + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + (_context: interfaces.Context, previous: unknown) => `${previous}bah`, + ); + child.onActivation( + 'foo', + async (_context: interfaces.Context, previous: unknown) => + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `${previous}bum`, + ); + child.onActivation( + 'foo', + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + async (_context: interfaces.Context, previous: unknown) => `${previous}2`, + ); + + const result: unknown = await child.getAsync('foo'); expect(result).equals('barbahbaz1bum2'); }); it('Should not allow onActivation of parent on child container', async () => { - class Parent { - - } + class Parent {} @injectable() class Child { public parent: Parent; - public constructor(@inject(Parent) parent: Parent) { + constructor(@inject(Parent) parent: Parent) { this.parent = parent; } } - const container = new Container({ autoBindInjectable: true }); - container.bind(Parent).toDynamicValue(() => Promise.resolve(new Parent())).inSingletonScope(); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind(Parent) + .toDynamicValue(async () => Promise.resolve(new Parent())) + .inSingletonScope(); - const constructed = new Parent(); + const constructed: Parent = new Parent(); - const child = container.createChild(); - child.onActivation(Parent, () => Promise.resolve(constructed)); + const child: Container = container.createChild(); + child.onActivation(Parent, async () => Promise.resolve(constructed)); - const result = await child.getAsync(Child); + const result: Child = await child.getAsync(Child); expect(result.parent).not.equals(constructed); }); it('Should wait until onActivation promise resolves before returning object', async () => { - let resolved = false; + let resolved: boolean = false; @injectable() - class Constructable { - } - - const container = new Container(); - container.bind('Constructable').to(Constructable).inSingletonScope() - .onActivation((context, c) => new Promise((r) => { - resolved = true; - r(c); - })); + class Constructable {} - const result = await container.getAsync('Constructable'); + const container: Container = new Container(); + container + .bind('Constructable') + .to(Constructable) + .inSingletonScope() + .onActivation( + async (_context: interfaces.Context, c: Constructable) => + new Promise((resolve: (value: Constructable) => void) => { + resolved = true; + resolve(c); + }), + ); + + const result: unknown = await container.getAsync('Constructable'); expect(result).instanceof(Constructable); expect(resolved).eql(true); }); it('Should wait until postConstruct promise resolves before returning object', async () => { - let resolved = false; + let resolved: boolean = false; @injectable() class Constructable { @postConstruct() - public myPostConstructMethod() { - return new Promise((r) => { + public async myPostConstructMethod() { + return new Promise((resolve: (value: unknown) => void) => { resolved = true; - r({}); + resolve({}); }); } } - const container = new Container(); + const container: Container = new Container(); container.bind('Constructable').to(Constructable); - const result = await container.getAsync('Constructable'); + const result: unknown = await container.getAsync('Constructable'); expect(result).instanceof(Constructable); expect(resolved).eql(true); @@ -2328,7 +2635,7 @@ describe('Resolve', () => { @injectable() class UseDate implements UseDate { public currentDate: Date; - public constructor(@inject('Date') currentDate: Date) { + constructor(@inject('Date') currentDate: Date) { expect(currentDate).instanceOf(Date); this.currentDate = currentDate; @@ -2338,12 +2645,15 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('UseDate').to(UseDate); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); - const subject1 = await container.getAsync('UseDate'); - const subject2 = await container.getAsync('UseDate'); + const subject1: UseDate = await container.getAsync('UseDate'); + const subject2: UseDate = await container.getAsync('UseDate'); expect(subject1.doSomething() === subject2.doSomething()).eql(true); }); @@ -2351,7 +2661,7 @@ describe('Resolve', () => { @injectable() class AsyncValue { public date: Date; - public constructor(@inject('Date') date: Date) { + constructor(@inject('Date') date: Date) { this.date = date; } } @@ -2360,18 +2670,26 @@ describe('Resolve', () => { class MixedDependency { public asyncValue: AsyncValue; public date!: Date; - public constructor(@inject(AsyncValue) asyncValue: AsyncValue) { + constructor(@inject(AsyncValue) asyncValue: AsyncValue) { expect(asyncValue).instanceOf(AsyncValue); this.asyncValue = asyncValue; } } - const container = new Container({ autoBindInjectable: true, defaultScope: 'Singleton' }); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); + const container: Container = new Container({ + autoBindInjectable: true, + defaultScope: 'Singleton', + }); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); - const object1 = await container.getAsync(MixedDependency); - const object2 = await container.getAsync(MixedDependency); + const object1: MixedDependency = + await container.getAsync(MixedDependency); + const object2: MixedDependency = + await container.getAsync(MixedDependency); expect(object1).equals(object2); }); @@ -2380,7 +2698,7 @@ describe('Resolve', () => { @injectable() class AsyncValue { public date: Date; - public constructor(@inject('Date') date: Date) { + constructor(@inject('Date') date: Date) { this.date = date; } } @@ -2388,19 +2706,26 @@ describe('Resolve', () => { @injectable() class MixedDependency { public asyncValue: AsyncValue; - public constructor(@inject(AsyncValue) asyncValue: AsyncValue) { + constructor(@inject(AsyncValue) asyncValue: AsyncValue) { expect(asyncValue).instanceOf(AsyncValue); this.asyncValue = asyncValue; } } - const container = new Container({ autoBindInjectable: true, defaultScope: 'Singleton' }); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); + const container: Container = new Container({ + autoBindInjectable: true, + defaultScope: 'Singleton', + }); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); - const async = await container.getAsync(AsyncValue); + const async: AsyncValue = await container.getAsync(AsyncValue); - const object1 = await container.getAsync(MixedDependency); + const object1: MixedDependency = + await container.getAsync(MixedDependency); expect(async).equals(object1.asyncValue); }); @@ -2409,9 +2734,7 @@ describe('Resolve', () => { @injectable() class AsyncValue { public date: Date; - public constructor(@inject('Date') date: Date) { - //expect(date).instanceOf(date); - + constructor(@inject('Date') date: Date) { this.date = date; } } @@ -2420,7 +2743,10 @@ describe('Resolve', () => { class MixedDependency { public asyncValue: AsyncValue; public date: Date; - public constructor(@inject(AsyncValue) asyncValue: AsyncValue, @inject('Date') date: Date) { + constructor( + @inject(AsyncValue) asyncValue: AsyncValue, + @inject('Date') date: Date, + ) { expect(asyncValue).instanceOf(AsyncValue); expect(date).instanceOf(Date); @@ -2429,17 +2755,24 @@ describe('Resolve', () => { } } - const container = new Container({ autoBindInjectable: true }); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); - const subject1 = await container.getAsync(MixedDependency); + const subject1: MixedDependency = + await container.getAsync(MixedDependency); expect(subject1.date).instanceOf(Date); expect(subject1.asyncValue).instanceOf(AsyncValue); }); it('Should support async values already in cache', async () => { - const container = new Container({ autoBindInjectable: true }); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); expect(await container.getAsync('Date')).instanceOf(Date); // causes container to cache singleton as Lazy object expect(await container.getAsync('Date')).instanceOf(Date); @@ -2448,13 +2781,16 @@ describe('Resolve', () => { it('Should support async values already in cache when there dependencies', async () => { @injectable() class HasDependencies { - public constructor(@inject('Date') date: Date) { + constructor(@inject('Date') date: Date) { expect(date).instanceOf(Date); } } - const container = new Container({ autoBindInjectable: true }); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); expect(await container.getAsync('Date')).instanceOf(Date); // causes container to cache singleton as Lazy object await container.getAsync(HasDependencies); @@ -2463,24 +2799,24 @@ describe('Resolve', () => { it('Should support async values already in cache when there are transient dependencies', async () => { @injectable() class Parent { - public constructor(@inject('Date') date: Date) { + constructor(@inject('Date') date: Date) { expect(date).instanceOf(Date); } } @injectable() class Child { - public constructor( - @inject(Parent) parent: Parent, - @inject('Date') date: Date - ) { + constructor(@inject(Parent) parent: Parent, @inject('Date') date: Date) { expect(parent).instanceOf(Parent); expect(date).instanceOf(Date); } } - const container = new Container({ autoBindInjectable: true }); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())).inSingletonScope(); + const container: Container = new Container({ autoBindInjectable: true }); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())) + .inSingletonScope(); expect(await container.getAsync('Date')).instanceOf(Date); // causes container to cache singleton as Lazy object await container.getAsync(Child); @@ -2492,7 +2828,10 @@ describe('Resolve', () => { public currentDate: Date; public foobar: string; - public constructor(@inject('Date') currentDate: Date, @inject('Static') foobar: string) { + constructor( + @inject('Date') currentDate: Date, + @inject('Static') foobar: string, + ) { expect(currentDate).instanceOf(Date); this.currentDate = currentDate; @@ -2500,12 +2839,14 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('UseDate').to(UseDate); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())); - container.bind('Static').toConstantValue('foobar'); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())); + container.bind('Static').toConstantValue('foobar'); - const subject1 = await container.getAsync('UseDate'); + const subject1: UseDate = await container.getAsync('UseDate'); expect(subject1.foobar).eql('foobar'); }); @@ -2513,7 +2854,7 @@ describe('Resolve', () => { @injectable() class UseDate implements UseDate { public currentDate: Date; - public constructor(@inject('Date') currentDate: Date) { + constructor(@inject('Date') currentDate: Date) { expect(currentDate).instanceOf(Date); this.currentDate = currentDate; @@ -2523,18 +2864,22 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('UseDate').to(UseDate); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())); - expect(() => container.get('UseDate')).to.throw(`You are attempting to construct 'UseDate' in a synchronous way but it has asynchronous dependencies.`); + expect(() => container.get('UseDate')).to.throw( + `You are attempting to construct 'UseDate' in a synchronous way but it has asynchronous dependencies.`, + ); }); it('Should be able to resolve indirect Promise bindings', async () => { @injectable() class UseDate implements UseDate { public currentDate: Date; - public constructor(@inject('Date') currentDate: Date) { + constructor(@inject('Date') currentDate: Date) { expect(currentDate).instanceOf(Date); this.currentDate = currentDate; @@ -2544,49 +2889,64 @@ describe('Resolve', () => { } } - const container = new Container(); + const container: Container = new Container(); container.bind('UseDate').to(UseDate); - container.bind('Date').toDynamicValue(() => Promise.resolve(new Date())); + container + .bind('Date') + .toDynamicValue(async () => Promise.resolve(new Date())); - const subject1 = await container.getAsync('UseDate'); - const subject2 = await container.getAsync('UseDate'); + const subject1: UseDate = await container.getAsync('UseDate'); + const subject2: UseDate = await container.getAsync('UseDate'); expect(subject1.doSomething() === subject2.doSomething()).eql(false); }); it('Should be able to resolve direct promise bindings', async () => { - const container = new Container(); - container.bind('async').toDynamicValue(() => Promise.resolve('foobar')); + const container: Container = new Container(); + container + .bind('async') + .toDynamicValue(async () => Promise.resolve('foobar')); - const value = await container.getAsync('async'); + const value: string = await container.getAsync('async'); expect(value).eql('foobar'); }); it('Should error if trying to resolve an promise in sync API', () => { - const container = new Container(); - container.bind('async').toDynamicValue(() => Promise.resolve('foobar')); + const container: Container = new Container(); + container + .bind('async') + .toDynamicValue(async () => Promise.resolve('foobar')); - expect(() => container.get('async')).to.throw(`You are attempting to construct 'async' in a synchronous way but it has asynchronous dependencies.`); + expect(() => container.get('async')).to.throw( + `You are attempting to construct 'async' in a synchronous way but it has asynchronous dependencies.`, + ); }); it('Should cache a a resolved value on singleton when possible', async () => { - const container = new Container(); + const container: Container = new Container(); - const asyncServiceIdentifier = 'async'; + const asyncServiceIdentifier: string = 'async'; - const asyncServiceDynamicResolvedValue = 'foobar'; - const asyncServiceDynamicValue = Promise.resolve(asyncServiceDynamicResolvedValue); - const asyncServiceDynamicValueCallback = sinon.spy(() => asyncServiceDynamicValue); + const asyncServiceDynamicResolvedValue: string = 'foobar'; + const asyncServiceDynamicValue: Promise = Promise.resolve( + asyncServiceDynamicResolvedValue, + ); + const asyncServiceDynamicValueCallback: sinon.SinonSpy< + [], + Promise + > = sinon.spy(async () => asyncServiceDynamicValue); container .bind(asyncServiceIdentifier) .toDynamicValue(asyncServiceDynamicValueCallback) .inSingletonScope(); - const serviceFromGetAsync = await container.getAsync(asyncServiceIdentifier); + const serviceFromGetAsync: unknown = await container.getAsync( + asyncServiceIdentifier, + ); await asyncServiceDynamicValue; - const serviceFromGet = container.get(asyncServiceIdentifier); + const serviceFromGet: unknown = container.get(asyncServiceIdentifier); expect(asyncServiceDynamicValueCallback.callCount).to.eq(1); expect(serviceFromGetAsync).eql(asyncServiceDynamicResolvedValue); diff --git a/test/syntax/binding_in_syntax.test.ts b/test/syntax/binding_in_syntax.test.ts index eee29fbf4..9c7e9403b 100644 --- a/test/syntax/binding_in_syntax.test.ts +++ b/test/syntax/binding_in_syntax.test.ts @@ -1,30 +1,42 @@ import { expect } from 'chai'; + import { Binding } from '../../src/bindings/binding'; import { BindingScopeEnum } from '../../src/constants/literal_types'; import { BindingInSyntax } from '../../src/syntax/binding_in_syntax'; describe('BindingInSyntax', () => { - it('Should set its own properties correctly', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingInSyntax = new BindingInSyntax(binding); - const _bindingInSyntax = bindingInSyntax as unknown as { _binding: Binding }; + const bindingInSyntax: BindingInSyntax = new BindingInSyntax( + binding, + ); - expect(_bindingInSyntax._binding.serviceIdentifier).eql(ninjaIdentifier); + // eslint-disable-next-line @typescript-eslint/naming-convention + const _bindingInSyntax: { + _binding: Binding; + } = bindingInSyntax as unknown as { + _binding: Binding; + }; + expect(_bindingInSyntax._binding.serviceIdentifier).eql(ninjaIdentifier); }); it('Should be able to configure the scope of a binding', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingInSyntax = new BindingInSyntax(binding); + const bindingInSyntax: BindingInSyntax = + new BindingInSyntax(binding); // default scope is transient expect(binding.scope).eql(BindingScopeEnum.Transient); @@ -36,7 +48,5 @@ describe('BindingInSyntax', () => { // set transient scope explicitly bindingInSyntax.inTransientScope(); expect(binding.scope).eql(BindingScopeEnum.Transient); - }); - -}); \ No newline at end of file +}); diff --git a/test/syntax/binding_in_when_on_syntax.test.ts b/test/syntax/binding_in_when_on_syntax.test.ts index a6fadc50b..d57c02e47 100644 --- a/test/syntax/binding_in_when_on_syntax.test.ts +++ b/test/syntax/binding_in_when_on_syntax.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; + import { injectable } from '../../src/annotation/injectable'; import { Binding } from '../../src/bindings/binding'; import { BindingScopeEnum } from '../../src/constants/literal_types'; @@ -7,7 +8,6 @@ import { interfaces } from '../../src/interfaces/interfaces'; import { BindingInWhenOnSyntax } from '../../src/syntax/binding_in_when_on_syntax'; describe('BindingInWhenOnSyntax', () => { - let sandbox: sinon.SinonSandbox; beforeEach(() => { @@ -19,30 +19,50 @@ describe('BindingInWhenOnSyntax', () => { }); it('Should set its own properties correctly', () => { - - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingInWhenOnSyntax = new BindingInWhenOnSyntax(binding); - const _bindingInWhenOnSyntax = bindingInWhenOnSyntax as unknown as { _binding: Binding }; - - expect(_bindingInWhenOnSyntax._binding.serviceIdentifier).eql(ninjaIdentifier); - + const ninjaIdentifier: string = 'Ninja'; + + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingInWhenOnSyntax: BindingInWhenOnSyntax = + new BindingInWhenOnSyntax(binding); + + // eslint-disable-next-line @typescript-eslint/naming-convention + const _bindingInWhenOnSyntax: { + _binding: Binding; + } = bindingInWhenOnSyntax as unknown as { + _binding: Binding; + }; + + expect(_bindingInWhenOnSyntax._binding.serviceIdentifier).eql( + ninjaIdentifier, + ); }); it('Should provide access to BindingInSyntax methods', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingInWhenOnSyntax = new BindingInWhenOnSyntax(binding); + const bindingInWhenOnSyntax: BindingInWhenOnSyntax = + new BindingInWhenOnSyntax(binding); + + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any const _bindingInWhenOnSyntax: any = bindingInWhenOnSyntax; // stubs for BindingWhenSyntax methods - const inSingletonScopeStub = sinon.stub(_bindingInWhenOnSyntax._bindingInSyntax, 'inSingletonScope').returns(null); - const inTransientScopeStub = sinon.stub(_bindingInWhenOnSyntax._bindingInSyntax, 'inTransientScope').returns(null); + const inSingletonScopeStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingInSyntax, 'inSingletonScope') + .returns(null); + const inTransientScopeStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingInSyntax, 'inTransientScope') + .returns(null); // invoke BindingWhenOnSyntax methods bindingInWhenOnSyntax.inSingletonScope(); @@ -51,64 +71,95 @@ describe('BindingInWhenOnSyntax', () => { // assert invoked BindingWhenSyntax methods expect(inSingletonScopeStub.callCount).eql(1); expect(inTransientScopeStub.callCount).eql(1); - }); it('Should provide access to BindingWhenSyntax methods', () => { - - interface Army { } - @injectable() - class Army implements Army { } - - interface ZombieArmy { } + class Army {} @injectable() - class ZombieArmy implements ZombieArmy { } + class ZombieArmy {} - interface Ninja { } - const ninjaIdentifier = 'Ninja'; + const ninjaIdentifier: string = 'Ninja'; - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingInWhenOnSyntax = new BindingInWhenOnSyntax(binding); + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingInWhenOnSyntax: BindingInWhenOnSyntax = + new BindingInWhenOnSyntax(binding); - // cast to any to be able to access private props + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any const _bindingInWhenOnSyntax: any = bindingInWhenOnSyntax; // stubs for BindingWhenSyntax methods - const whenStub = sinon.stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'when').returns(null); - const whenTargetNamedStub = sinon.stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenTargetNamed').returns(null); - const whenTargetTaggedStub = sinon.stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenTargetTagged').returns(null); - const whenInjectedIntoStub = sinon.stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenInjectedInto').returns(null); - const whenParentNamedStub = sinon.stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenParentNamed').returns(null); - const whenParentTaggedStub = sinon.stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenParentTagged').returns(null); - - const whenAnyAncestorIsStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorIs').returns(null); - - const whenNoAncestorIsStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorIs').returns(null); - - const whenNoAncestorNamedStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorNamed').returns(null); - - const whenAnyAncestorNamedStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorNamed').returns(null); - - const whenNoAncestorTaggedStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorTagged').returns(null); - - const whenAnyAncestorTaggedStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorTagged').returns(null); - - const whenAnyAncestorMatchesStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorMatches').returns(null); - - const whenNoAncestorMatchesStub = sinon.stub( - _bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorMatches').returns(null); + const whenStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'when') + .returns(null); + const whenTargetNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenTargetNamed') + .returns(null); + const whenTargetTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenTargetTagged') + .returns(null); + const whenInjectedIntoStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenInjectedInto') + .returns(null); + const whenParentNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenParentNamed') + .returns(null); + const whenParentTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenParentTagged') + .returns(null); + + const whenAnyAncestorIsStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorIs') + .returns(null); + + const whenNoAncestorIsStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorIs') + .returns(null); + + const whenNoAncestorNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorNamed') + .returns(null); + + const whenAnyAncestorNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorNamed') + .returns(null); + + const whenNoAncestorTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorTagged') + .returns(null); + + const whenAnyAncestorTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorTagged') + .returns(null); + + const whenAnyAncestorMatchesStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorMatches') + .returns(null); + + const whenNoAncestorMatchesStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorMatches') + .returns(null); // invoke BindingWhenOnSyntax methods - bindingInWhenOnSyntax.when((request: interfaces.Request) => true); + bindingInWhenOnSyntax.when((_request: interfaces.Request) => true); bindingInWhenOnSyntax.whenTargetNamed('test'); bindingInWhenOnSyntax.whenTargetTagged('test', true); bindingInWhenOnSyntax.whenInjectedInto('army'); @@ -121,8 +172,12 @@ describe('BindingInWhenOnSyntax', () => { bindingInWhenOnSyntax.whenAnyAncestorTagged('test', true); bindingInWhenOnSyntax.whenNoAncestorNamed('test'); bindingInWhenOnSyntax.whenNoAncestorTagged('test', true); - bindingInWhenOnSyntax.whenAnyAncestorMatches((request: interfaces.Request) => true); - bindingInWhenOnSyntax.whenNoAncestorMatches((request: interfaces.Request) => true); + bindingInWhenOnSyntax.whenAnyAncestorMatches( + (_request: interfaces.Request) => true, + ); + bindingInWhenOnSyntax.whenNoAncestorMatches( + (_request: interfaces.Request) => true, + ); // assert invoked BindingWhenSyntax methods expect(whenStub.callCount).eql(1); @@ -139,31 +194,33 @@ describe('BindingInWhenOnSyntax', () => { expect(whenNoAncestorTaggedStub.callCount).eql(1); expect(whenAnyAncestorMatchesStub.callCount).eql(1); expect(whenNoAncestorMatchesStub.callCount).eql(1); - }); it('Should provide access to BindingOnSyntax methods', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingInWhenOnSyntax = new BindingInWhenOnSyntax(binding); + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingInWhenOnSyntax: BindingInWhenOnSyntax = + new BindingInWhenOnSyntax(binding); - // cast to any to be able to access private props + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any const _bindingInWhenOnSyntax: any = bindingInWhenOnSyntax; // stubs for BindingWhenSyntax methods - const onActivationStub = sinon.stub(_bindingInWhenOnSyntax._bindingOnSyntax, 'onActivation').returns(null); + const onActivationStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingInWhenOnSyntax._bindingOnSyntax, 'onActivation') + .returns(null); // invoke BindingWhenOnSyntax methods - bindingInWhenOnSyntax.onActivation((context: interfaces.Context, ninja: Ninja) => - // DO NOTHING - ninja); + bindingInWhenOnSyntax.onActivation( + (_context: interfaces.Context, ninja: unknown) => ninja, + ); // assert invoked BindingWhenSyntax methods expect(onActivationStub.callCount).eql(1); - }); - -}); \ No newline at end of file +}); diff --git a/test/syntax/binding_on_syntax.test.ts b/test/syntax/binding_on_syntax.test.ts index a152598e1..ade883fd1 100644 --- a/test/syntax/binding_on_syntax.test.ts +++ b/test/syntax/binding_on_syntax.test.ts @@ -1,39 +1,47 @@ import { expect } from 'chai'; + import { Binding } from '../../src/bindings/binding'; import { BindingScopeEnum } from '../../src/constants/literal_types'; import { interfaces } from '../../src/interfaces/interfaces'; import { BindingOnSyntax } from '../../src/syntax/binding_on_syntax'; describe('BindingOnSyntax', () => { - it('Should set its own properties correctly', () => { - - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingOnSyntax = new BindingOnSyntax(binding); - const _bindingOnSyntax = bindingOnSyntax as unknown as { _binding: Binding }; + const ninjaIdentifier: string = 'Ninja'; + + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingOnSyntax: BindingOnSyntax = new BindingOnSyntax( + binding, + ); + // eslint-disable-next-line @typescript-eslint/naming-convention + const _bindingOnSyntax: { + _binding: Binding; + } = bindingOnSyntax as unknown as { + _binding: Binding; + }; expect(_bindingOnSyntax._binding.serviceIdentifier).eql(ninjaIdentifier); - }); it('Should be able to configure the activation handler of a binding', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingOnSyntax = new BindingOnSyntax(binding); + const bindingOnSyntax: BindingOnSyntax = new BindingOnSyntax( + binding, + ); - bindingOnSyntax.onActivation((context: interfaces.Context, ninja: Ninja) => { - const handler = {}; - return new Proxy(ninja, handler); - }); + bindingOnSyntax.onActivation( + (_context: interfaces.Context, ninja: unknown) => ninja, + ); expect(binding.onActivation).not.to.eql(null); - }); - -}); \ No newline at end of file +}); diff --git a/test/syntax/binding_to_syntax.test.ts b/test/syntax/binding_to_syntax.test.ts index 3eef12e5b..f4a5d1137 100644 --- a/test/syntax/binding_to_syntax.test.ts +++ b/test/syntax/binding_to_syntax.test.ts @@ -1,38 +1,52 @@ import { expect } from 'chai'; +import sinon from 'sinon'; +import Sinon from 'sinon'; + import { injectable } from '../../src/annotation/injectable'; import { Binding } from '../../src/bindings/binding'; import * as ERROR_MSGS from '../../src/constants/error_msgs'; -import { BindingScopeEnum, BindingTypeEnum } from '../../src/constants/literal_types'; +import { + BindingScopeEnum, + BindingTypeEnum, +} from '../../src/constants/literal_types'; import { interfaces } from '../../src/interfaces/interfaces'; import { BindingToSyntax } from '../../src/syntax/binding_to_syntax'; -import sinon from 'sinon'; describe('BindingToSyntax', () => { - it('Should set its own properties correctly', () => { - - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingToSyntax = new BindingToSyntax(binding); - const _bindingToSyntax = bindingToSyntax as unknown as { _binding: interfaces.Binding } + const ninjaIdentifier: string = 'Ninja'; + + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + + const bindingToSyntax: BindingToSyntax = new BindingToSyntax( + binding, + ); + // eslint-disable-next-line @typescript-eslint/naming-convention + const _bindingToSyntax: { + _binding: interfaces.Binding; + } = bindingToSyntax as unknown as { + _binding: interfaces.Binding; + }; expect(_bindingToSyntax._binding.serviceIdentifier).eql(ninjaIdentifier); - }); it('Should be able to configure the type of a binding', () => { - - interface Ninja { } - @injectable() - class Ninja implements Ninja { } - const ninjaIdentifier = 'Ninja'; + class Ninja {} + const ninjaIdentifier: string = 'Ninja'; - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - // let bindingWithClassAsId = new Binding(Ninja, BindingScopeEnum.Transient); - const bindingToSyntax = new BindingToSyntax(binding); + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + + const bindingToSyntax: BindingToSyntax = new BindingToSyntax( + binding, + ); expect(binding.type).eql(BindingTypeEnum.Invalid); @@ -40,30 +54,38 @@ describe('BindingToSyntax', () => { expect(binding.type).eql(BindingTypeEnum.Instance); expect(binding.implementationType).not.to.eql(null); + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access (bindingToSyntax as any)._binding = binding; bindingToSyntax.toConstantValue(new Ninja()); expect(binding.type).eql(BindingTypeEnum.ConstantValue); expect(binding.cache instanceof Ninja).eql(true); - bindingToSyntax.toDynamicValue((context: interfaces.Context) => new Ninja()); + bindingToSyntax.toDynamicValue( + (_context: interfaces.Context) => new Ninja(), + ); expect(binding.type).eql(BindingTypeEnum.DynamicValue); expect(typeof binding.dynamicValue).eql('function'); - const dynamicValueFactory: any = binding.dynamicValue; - expect(dynamicValueFactory(null) instanceof Ninja).eql(true); + const dynamicValueFactory: interfaces.DynamicValue = + binding.dynamicValue as interfaces.DynamicValue; + + expect( + dynamicValueFactory(null as unknown as interfaces.Context) instanceof + Ninja, + ).eql(true); bindingToSyntax.toConstructor(Ninja); expect(binding.type).eql(BindingTypeEnum.Constructor); expect(binding.implementationType).not.to.eql(null); - bindingToSyntax.toFactory((context: interfaces.Context) => - () => - new Ninja()); + bindingToSyntax.toFactory( + (_context: interfaces.Context) => () => new Ninja(), + ); expect(binding.type).eql(BindingTypeEnum.Factory); expect(binding.factory).not.to.eql(null); - const f = () => 'test'; + const f: () => string = () => 'test'; bindingToSyntax.toFunction(f); expect(binding.type).eql(BindingTypeEnum.Function); expect(binding.cache === f).eql(true); @@ -78,45 +100,43 @@ describe('BindingToSyntax', () => { expect(binding.type).eql(BindingTypeEnum.Factory); expect(binding.factory).not.to.eql(null); - const mockContext = { + const mockContext: interfaces.Context = { container: { - getNamed: sinon.stub() - } - }; + getNamed: sinon.stub(), + } as Partial as interfaces.Container, + } as Partial as interfaces.Context; if (binding.factory !== null) { - binding.factory((mockContext as unknown as interfaces.Context))(ninjaIdentifier); - sinon.assert.calledOnce(mockContext.container.getNamed); + binding.factory(mockContext)(ninjaIdentifier); + sinon.assert.calledOnce(mockContext.container.getNamed as Sinon.SinonSpy); } - bindingToSyntax.toProvider((context: interfaces.Context) => - () => - new Promise((resolve) => { + bindingToSyntax.toProvider( + (_context: interfaces.Context) => async () => + new Promise((resolve: (value: Ninja) => void) => { resolve(new Ninja()); - })); + }), + ); expect(binding.type).eql(BindingTypeEnum.Provider); expect(binding.provider).not.to.eql(null); - }); it('Should prevent invalid function bindings', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingToSyntax: BindingToSyntax = new BindingToSyntax( + binding, + ); - @injectable() - class Ninja implements Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingToSyntax = new BindingToSyntax(binding); - - const f = function () { + const f: () => void = function () { bindingToSyntax.toFunction(5); }; expect(f).to.throw(ERROR_MSGS.INVALID_FUNCTION_BINDING); - }); - -}); \ No newline at end of file +}); diff --git a/test/syntax/binding_when_on_syntax.test.ts b/test/syntax/binding_when_on_syntax.test.ts index 7529aaa96..e1b0a0eaa 100644 --- a/test/syntax/binding_when_on_syntax.test.ts +++ b/test/syntax/binding_when_on_syntax.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; -import * as sinon from 'sinon'; +import Sinon, * as sinon from 'sinon'; + import { injectable } from '../../src/annotation/injectable'; import { Binding } from '../../src/bindings/binding'; import { BindingScopeEnum } from '../../src/constants/literal_types'; @@ -7,7 +8,6 @@ import { interfaces } from '../../src/interfaces/interfaces'; import { BindingWhenOnSyntax } from '../../src/syntax/binding_when_on_syntax'; describe('BindingWhenOnSyntax', () => { - let sandbox: sinon.SinonSandbox; beforeEach(() => { @@ -19,73 +19,113 @@ describe('BindingWhenOnSyntax', () => { }); it('Should set its own properties correctly', () => { - - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingWhenOnSyntax = new BindingWhenOnSyntax(binding); - const _bindingWhenOnSyntax = bindingWhenOnSyntax as unknown as { _binding: interfaces.Binding } - - expect(_bindingWhenOnSyntax._binding.serviceIdentifier).eql(ninjaIdentifier); - + const ninjaIdentifier: string = 'Ninja'; + + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingWhenOnSyntax: BindingWhenOnSyntax = + new BindingWhenOnSyntax(binding); + // eslint-disable-next-line @typescript-eslint/naming-convention + const _bindingWhenOnSyntax: { + _binding: interfaces.Binding; + } = bindingWhenOnSyntax as unknown as { + _binding: interfaces.Binding; + }; + + expect(_bindingWhenOnSyntax._binding.serviceIdentifier).eql( + ninjaIdentifier, + ); }); it('Should provide access to BindingWhenSyntax methods', () => { - - interface Army { } - @injectable() - class Army implements Army { } - - interface ZombieArmy { } + class Army {} @injectable() - class ZombieArmy implements ZombieArmy { } + class ZombieArmy {} - interface Ninja { } - const ninjaIdentifier = 'Ninja'; + const ninjaIdentifier: string = 'Ninja'; - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingWhenOnSyntax = new BindingWhenOnSyntax(binding); + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingWhenOnSyntax: BindingWhenOnSyntax = + new BindingWhenOnSyntax(binding); - // cast to any to be able to access private props + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any const _bindingWhenOnSyntax: any = bindingWhenOnSyntax; // stubs for BindingWhenSyntax methods - const whenStub = sinon.stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'when').returns(null); - const whenTargetNamedStub = sinon.stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenTargetNamed').returns(null); - const whenTargetTaggedStub = sinon.stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenTargetTagged').returns(null); - const whenInjectedIntoStub = sinon.stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenInjectedInto').returns(null); - const whenParentNamedStub = sinon.stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenParentNamed').returns(null); - const whenParentTaggedStub = sinon.stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenParentTagged').returns(null); - - const whenAnyAncestorIsStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorIs').returns(null); - - const whenNoAncestorIsStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorIs').returns(null); - - const whenAnyAncestorNamedStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorNamed').returns(null); - - const whenNoAncestorNamedStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorNamed').returns(null); - - const whenNoAncestorTaggedStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorTagged').returns(null); - - const whenAnyAncestorTaggedStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorTagged').returns(null); - - const whenAnyAncestorMatchesStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorMatches').returns(null); - - const whenNoAncestorMatchesStub = sinon.stub( - _bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorMatches').returns(null); + const whenStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'when') + .returns(null); + const whenTargetNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenTargetNamed') + .returns(null); + const whenTargetTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenTargetTagged') + .returns(null); + const whenInjectedIntoStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenInjectedInto') + .returns(null); + const whenParentNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenParentNamed') + .returns(null); + const whenParentTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenParentTagged') + .returns(null); + + const whenAnyAncestorIsStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorIs') + .returns(null); + + const whenNoAncestorIsStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorIs') + .returns(null); + + const whenAnyAncestorNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorNamed') + .returns(null); + + const whenNoAncestorNamedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorNamed') + .returns(null); + + const whenNoAncestorTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorTagged') + .returns(null); + + const whenAnyAncestorTaggedStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorTagged') + .returns(null); + + const whenAnyAncestorMatchesStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenAnyAncestorMatches') + .returns(null); + + const whenNoAncestorMatchesStub: sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingWhenSyntax, 'whenNoAncestorMatches') + .returns(null); // invoke BindingWhenOnSyntax methods - bindingWhenOnSyntax.when((request: interfaces.Request) => true); + bindingWhenOnSyntax.when((_request: interfaces.Request) => true); bindingWhenOnSyntax.whenTargetNamed('test'); bindingWhenOnSyntax.whenTargetTagged('test', true); bindingWhenOnSyntax.whenInjectedInto('army'); @@ -98,8 +138,12 @@ describe('BindingWhenOnSyntax', () => { bindingWhenOnSyntax.whenAnyAncestorTagged('test', true); bindingWhenOnSyntax.whenNoAncestorNamed('test'); bindingWhenOnSyntax.whenNoAncestorTagged('test', true); - bindingWhenOnSyntax.whenAnyAncestorMatches((request: interfaces.Request) => true); - bindingWhenOnSyntax.whenNoAncestorMatches((request: interfaces.Request) => true); + bindingWhenOnSyntax.whenAnyAncestorMatches( + (_request: interfaces.Request) => true, + ); + bindingWhenOnSyntax.whenNoAncestorMatches( + (_request: interfaces.Request) => true, + ); // assert invoked BindingWhenSyntax methods expect(whenStub.callCount).eql(1); @@ -116,31 +160,32 @@ describe('BindingWhenOnSyntax', () => { expect(whenNoAncestorTaggedStub.callCount).eql(1); expect(whenAnyAncestorMatchesStub.callCount).eql(1); expect(whenNoAncestorMatchesStub.callCount).eql(1); - }); it('Should provide access to BindingOnSyntax methods', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingWhenOnSyntax = new BindingWhenOnSyntax(binding); + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingWhenOnSyntax: BindingWhenOnSyntax = + new BindingWhenOnSyntax(binding); - // cast to any to be able to access private props + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any const _bindingWhenOnSyntax: any = bindingWhenOnSyntax; - // stubs for BindingWhenSyntax methods - const onActivationStub = sinon.stub(_bindingWhenOnSyntax._bindingOnSyntax, 'onActivation').returns(null); + const onActivationStub: Sinon.SinonStub = sinon + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + .stub(_bindingWhenOnSyntax._bindingOnSyntax, 'onActivation') + .returns(null); // invoke BindingWhenOnSyntax methods - bindingWhenOnSyntax.onActivation((context: interfaces.Context, ninja: Ninja) => - // DO NOTHING - ninja); + bindingWhenOnSyntax.onActivation( + (_context: interfaces.Context, ninja: unknown) => ninja, + ); // assert invoked BindingWhenSyntax methods expect(onActivationStub.callCount).eql(1); - }); - -}); \ No newline at end of file +}); diff --git a/test/syntax/binding_when_syntax.test.ts b/test/syntax/binding_when_syntax.test.ts index 499986c45..057325c4c 100644 --- a/test/syntax/binding_when_syntax.test.ts +++ b/test/syntax/binding_when_syntax.test.ts @@ -1,6 +1,10 @@ import { expect } from 'chai'; + import { Binding } from '../../src/bindings/binding'; -import { BindingScopeEnum, TargetTypeEnum } from '../../src/constants/literal_types'; +import { + BindingScopeEnum, + TargetTypeEnum, +} from '../../src/constants/literal_types'; import { Container } from '../../src/container/container'; import { interfaces } from '../../src/interfaces/interfaces'; import { Context } from '../../src/planning/context'; @@ -11,102 +15,169 @@ import { BindingWhenSyntax } from '../../src/syntax/binding_when_syntax'; import { typeConstraint } from '../../src/syntax/constraint_helpers'; describe('BindingWhenSyntax', () => { - it('Should set its own properties correctly', () => { - - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingWhenSyntax = new BindingWhenSyntax(binding); - const _bindingWhenSyntax = bindingWhenSyntax as unknown as { _binding: Binding }; + const ninjaIdentifier: string = 'Ninja'; + + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingWhenSyntax: BindingWhenSyntax = new BindingWhenSyntax( + binding, + ); + // eslint-disable-next-line @typescript-eslint/naming-convention + const _bindingWhenSyntax: { + _binding: Binding; + } = bindingWhenSyntax as unknown as { + _binding: Binding; + }; expect(_bindingWhenSyntax._binding.serviceIdentifier).eql(ninjaIdentifier); - }); it('Should be able to configure custom constraint of a binding', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingWhenSyntax = new BindingWhenSyntax(binding); + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingWhenSyntax: BindingWhenSyntax = new BindingWhenSyntax( + binding, + ); bindingWhenSyntax.when((theRequest: interfaces.Request) => - theRequest.target.name.equals('ninja')); - - const target = new Target(TargetTypeEnum.ConstructorArgument, 'ninja', ninjaIdentifier); - const context = new Context(new Container()); - const request = new Request(ninjaIdentifier, context, null, binding, target); + theRequest.target.name.equals('ninja'), + ); + + const target: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'ninja', + ninjaIdentifier, + ); + const context: Context = new Context(new Container()); + const request: Request = new Request( + ninjaIdentifier, + context, + null, + binding, + target, + ); expect(binding.constraint(request)).eql(true); - }); it('Should have false constraint binding null request whenTargetIsDefault', () => { - interface Weapon { name: string; } - const shurikenBinding = new Binding('Weapon', BindingScopeEnum.Transient); - const shurikenBindingWhenSyntax = new BindingWhenSyntax(shurikenBinding); + const shurikenBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); + const shurikenBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(shurikenBinding); shurikenBindingWhenSyntax.whenTargetIsDefault(); expect(shurikenBinding.constraint(null)).eql(false); - }); it('Should be able to constraint a binding to a named target', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingWhenSyntax: BindingWhenSyntax = new BindingWhenSyntax( + binding, + ); - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingWhenSyntax = new BindingWhenSyntax(binding); - - const named = 'primary'; + const named: string = 'primary'; bindingWhenSyntax.whenTargetNamed(named); expect(binding.constraint).not.to.eql(null); - const context = new Context(new Container()); - - const target = new Target(TargetTypeEnum.ConstructorArgument, 'ninja', ninjaIdentifier, named); - const request = new Request(ninjaIdentifier, context, null, binding, target); + const context: Context = new Context(new Container()); + + const target: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'ninja', + ninjaIdentifier, + named, + ); + const request: Request = new Request( + ninjaIdentifier, + context, + null, + binding, + target, + ); expect(binding.constraint(request)).eql(true); - const target2 = new Target(TargetTypeEnum.ConstructorArgument, 'ninja', ninjaIdentifier); - const request2 = new Request(ninjaIdentifier, context, null, binding, target2); + const target2: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'ninja', + ninjaIdentifier, + ); + const request2: Request = new Request( + ninjaIdentifier, + context, + null, + binding, + target2, + ); expect(binding.constraint(request2)).eql(false); - }); it('Should be able to constraint a binding to a tagged target', () => { + const ninjaIdentifier: string = 'Ninja'; - interface Ninja { } - const ninjaIdentifier = 'Ninja'; - - const binding = new Binding(ninjaIdentifier, BindingScopeEnum.Transient); - const bindingWhenSyntax = new BindingWhenSyntax(binding); + const binding: Binding = new Binding( + ninjaIdentifier, + BindingScopeEnum.Transient, + ); + const bindingWhenSyntax: BindingWhenSyntax = new BindingWhenSyntax( + binding, + ); bindingWhenSyntax.whenTargetTagged('canSwim', true); expect(binding.constraint).not.to.eql(null); - const context = new Context(new Container()); - - const target = new Target(TargetTypeEnum.ConstructorArgument, 'ninja', ninjaIdentifier, new Metadata('canSwim', true)); - const request = new Request(ninjaIdentifier, context, null, binding, target); + const context: Context = new Context(new Container()); + + const target: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'ninja', + ninjaIdentifier, + new Metadata('canSwim', true), + ); + const request: Request = new Request( + ninjaIdentifier, + context, + null, + binding, + target, + ); expect(binding.constraint(request)).eql(true); - const target2 = new Target(TargetTypeEnum.ConstructorArgument, 'ninja', ninjaIdentifier, new Metadata('canSwim', false)); - const request2 = new Request(ninjaIdentifier, context, null, binding, target2); + const target2: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'ninja', + ninjaIdentifier, + new Metadata('canSwim', false), + ); + const request2: Request = new Request( + ninjaIdentifier, + context, + null, + binding, + target2, + ); expect(binding.constraint(request2)).eql(false); - }); it('Should be able to constraint a binding to its parent', () => { - interface Weapon { name: string; } @@ -121,39 +192,93 @@ describe('BindingWhenSyntax', () => { class Ninja implements ChineseWarrior { public shuriken: Weapon; - public constructor(shuriken: Weapon) { + constructor(shuriken: Weapon) { this.shuriken = shuriken; } } class Samurai implements JaponeseWarrior { public katana: Weapon; - public constructor(katana: Weapon) { + constructor(katana: Weapon) { this.katana = katana; } } - const context = new Context(new Container()); + const context: Context = new Context(new Container()); - const samuraiBinding = new Binding('Samurai', BindingScopeEnum.Transient); + const samuraiBinding: Binding = new Binding( + 'Samurai', + BindingScopeEnum.Transient, + ); samuraiBinding.implementationType = Samurai; - const samuraiTarget = new Target(TargetTypeEnum.Variable, '', 'Samurai'); - const samuraiRequest = new Request('Samurai', context, null, samuraiBinding, samuraiTarget); - - const ninjaBinding = new Binding('Ninja', BindingScopeEnum.Transient); + const samuraiTarget: Target = new Target( + TargetTypeEnum.Variable, + '', + 'Samurai', + ); + const samuraiRequest: Request = new Request( + 'Samurai', + context, + null, + samuraiBinding, + samuraiTarget, + ); + + const ninjaBinding: Binding = new Binding( + 'Ninja', + BindingScopeEnum.Transient, + ); ninjaBinding.implementationType = Ninja; - const ninjaTarget = new Target(TargetTypeEnum.Variable, '', 'Ninja'); - const ninjaRequest = new Request('Ninja', context, null, ninjaBinding, ninjaTarget); - - const katanaBinding = new Binding('Weapon', BindingScopeEnum.Transient); - const katanaBindingWhenSyntax = new BindingWhenSyntax(katanaBinding); - const katanaTarget = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Weapon'); - const katanaRequest = new Request('Weapon', context, samuraiRequest, katanaBinding, katanaTarget); - - const shurikenBinding = new Binding('Weapon', BindingScopeEnum.Transient); - const shurikenBindingWhenSyntax = new BindingWhenSyntax(shurikenBinding); - const shurikenTarget = new Target(TargetTypeEnum.ConstructorArgument, 'shuriken', 'Weapon'); - const shurikenRequest = new Request('Weapon', context, ninjaRequest, shurikenBinding, shurikenTarget); + const ninjaTarget: Target = new Target( + TargetTypeEnum.Variable, + '', + 'Ninja', + ); + const ninjaRequest: Request = new Request( + 'Ninja', + context, + null, + ninjaBinding, + ninjaTarget, + ); + + const katanaBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); + const katanaBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(katanaBinding); + const katanaTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Weapon', + ); + const katanaRequest: Request = new Request( + 'Weapon', + context, + samuraiRequest, + katanaBinding, + katanaTarget, + ); + + const shurikenBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); + const shurikenBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(shurikenBinding); + const shurikenTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'shuriken', + 'Weapon', + ); + const shurikenRequest: Request = new Request( + 'Weapon', + context, + ninjaRequest, + shurikenBinding, + shurikenTarget, + ); katanaBindingWhenSyntax.whenInjectedInto(Samurai); expect(katanaBinding.constraint(katanaRequest)).eql(true); @@ -186,11 +311,9 @@ describe('BindingWhenSyntax', () => { shurikenBindingWhenSyntax.whenInjectedInto('Ninja'); expect(shurikenBinding.constraint(katanaRequest)).eql(false); expect(shurikenBinding.constraint(shurikenRequest)).eql(true); - }); it('Should be able to constraint a binding to a named parent', () => { - interface Weapon { name: string; } @@ -205,41 +328,97 @@ describe('BindingWhenSyntax', () => { class Ninja implements ChineseWarrior { public shuriken: Weapon; - public constructor(shuriken: Weapon) { + constructor(shuriken: Weapon) { this.shuriken = shuriken; } } class Samurai implements JaponeseWarrior { public katana: Weapon; - public constructor(katana: Weapon) { + constructor(katana: Weapon) { this.katana = katana; } } - const samuraiBinding = new Binding('Samurai', BindingScopeEnum.Transient); + const samuraiBinding: Binding = new Binding( + 'Samurai', + BindingScopeEnum.Transient, + ); samuraiBinding.implementationType = Samurai; - const context = new Context(new Container()); - - const samuraiTarget = new Target(TargetTypeEnum.ConstructorArgument, '', 'Samurai', 'japonese'); - const samuraiRequest = new Request('Samurai', context, null, samuraiBinding, samuraiTarget); - const ninjaBinding = new Binding('Ninja', BindingScopeEnum.Transient); + const context: Context = new Context(new Container()); + + const samuraiTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + '', + 'Samurai', + 'japonese', + ); + const samuraiRequest: Request = new Request( + 'Samurai', + context, + null, + samuraiBinding, + samuraiTarget, + ); + const ninjaBinding: Binding = new Binding( + 'Ninja', + BindingScopeEnum.Transient, + ); ninjaBinding.implementationType = Ninja; - const ninjaTarget = new Target(TargetTypeEnum.ConstructorArgument, '', 'Ninja', 'chinese'); - const ninjaRequest = new Request('Ninja', context, null, ninjaBinding, ninjaTarget); - - const katanaBinding = new Binding('Weapon', BindingScopeEnum.Transient); - const katanaBindingWhenSyntax = new BindingWhenSyntax(katanaBinding); - const katanaTarget = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Weapon'); - const katanaRequest = new Request('Weapon', context, samuraiRequest, katanaBinding, katanaTarget); - - const shurikenBinding = new Binding('Weapon', BindingScopeEnum.Transient); - const shurikenBindingWhenSyntax = new BindingWhenSyntax(shurikenBinding); - const shurikenTarget = new Target(TargetTypeEnum.ConstructorArgument, 'shuriken', 'Weapon'); - const shurikenRequest = new Request('Weapon', context, ninjaRequest, shurikenBinding, shurikenTarget); + const ninjaTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + '', + 'Ninja', + 'chinese', + ); + const ninjaRequest: Request = new Request( + 'Ninja', + context, + null, + ninjaBinding, + ninjaTarget, + ); + + const katanaBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); + const katanaBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(katanaBinding); + const katanaTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Weapon', + ); + const katanaRequest: Request = new Request( + 'Weapon', + context, + samuraiRequest, + katanaBinding, + katanaTarget, + ); + + const shurikenBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); + const shurikenBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(shurikenBinding); + const shurikenTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'shuriken', + 'Weapon', + ); + const shurikenRequest: Request = new Request( + 'Weapon', + context, + ninjaRequest, + shurikenBinding, + shurikenTarget, + ); katanaBindingWhenSyntax.whenParentNamed('chinese'); shurikenBindingWhenSyntax.whenParentNamed('chinese'); @@ -250,11 +429,9 @@ describe('BindingWhenSyntax', () => { shurikenBindingWhenSyntax.whenParentNamed('japonese'); expect(katanaBinding.constraint(katanaRequest)).eql(true); expect(shurikenBinding.constraint(shurikenRequest)).eql(false); - }); it('Should be able to constraint a binding to a tagged parent', () => { - interface Weapon { name: string; } @@ -269,40 +446,96 @@ describe('BindingWhenSyntax', () => { class Ninja implements ChineseWarrior { public shuriken: Weapon; - public constructor(shuriken: Weapon) { + constructor(shuriken: Weapon) { this.shuriken = shuriken; } } class Samurai implements JaponeseWarrior { public katana: Weapon; - public constructor(katana: Weapon) { + constructor(katana: Weapon) { this.katana = katana; } } - const context = new Context(new Container()); + const context: Context = new Context(new Container()); - const samuraiBinding = new Binding('Samurai', BindingScopeEnum.Transient); + const samuraiBinding: Binding = new Binding( + 'Samurai', + BindingScopeEnum.Transient, + ); samuraiBinding.implementationType = Samurai; - const samuraiTarget = new Target(TargetTypeEnum.ConstructorArgument, '', 'Samurai', new Metadata('sneaky', false)); - const samuraiRequest = new Request('Samurai', context, null, samuraiBinding, samuraiTarget); - - const ninjaBinding = new Binding('Ninja', BindingScopeEnum.Transient); + const samuraiTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + '', + 'Samurai', + new Metadata('sneaky', false), + ); + const samuraiRequest: Request = new Request( + 'Samurai', + context, + null, + samuraiBinding, + samuraiTarget, + ); + + const ninjaBinding: Binding = new Binding( + 'Ninja', + BindingScopeEnum.Transient, + ); ninjaBinding.implementationType = Ninja; - const ninjaTarget = new Target(TargetTypeEnum.ConstructorArgument, '', 'Ninja', new Metadata('sneaky', true)); - const ninjaRequest = new Request('Ninja', context, null, ninjaBinding, ninjaTarget); - - const katanaBinding = new Binding('Weapon', BindingScopeEnum.Transient); - const katanaBindingWhenSyntax = new BindingWhenSyntax(katanaBinding); - const katanaTarget = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Weapon'); - const katanaRequest = new Request('Weapon', context, samuraiRequest, katanaBinding, katanaTarget); - - const shurikenBinding = new Binding('Weapon', BindingScopeEnum.Transient); - const shurikenBindingWhenSyntax = new BindingWhenSyntax(shurikenBinding); - const shurikenTarget = new Target(TargetTypeEnum.ConstructorArgument, 'shuriken', 'Weapon'); - const shurikenRequest = new Request('Weapon', context, ninjaRequest, shurikenBinding, shurikenTarget); + const ninjaTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + '', + 'Ninja', + new Metadata('sneaky', true), + ); + const ninjaRequest: Request = new Request( + 'Ninja', + context, + null, + ninjaBinding, + ninjaTarget, + ); + + const katanaBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); + const katanaBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(katanaBinding); + const katanaTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Weapon', + ); + const katanaRequest: Request = new Request( + 'Weapon', + context, + samuraiRequest, + katanaBinding, + katanaTarget, + ); + + const shurikenBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); + const shurikenBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(shurikenBinding); + const shurikenTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'shuriken', + 'Weapon', + ); + const shurikenRequest: Request = new Request( + 'Weapon', + context, + ninjaRequest, + shurikenBinding, + shurikenTarget, + ); katanaBindingWhenSyntax.whenParentTagged('sneaky', true); shurikenBindingWhenSyntax.whenParentTagged('sneaky', true); @@ -313,11 +546,9 @@ describe('BindingWhenSyntax', () => { shurikenBindingWhenSyntax.whenParentTagged('sneaky', false); expect(katanaBinding.constraint(katanaRequest)).eql(true); expect(shurikenBinding.constraint(shurikenRequest)).eql(false); - }); describe('BindingWhenSyntax.when*Ancestor*()', () => { - interface Material { name: string; } @@ -328,17 +559,17 @@ describe('BindingWhenSyntax', () => { } class Katana implements Weapon { - public name = 'Katana'; + public name: string = 'Katana'; public material: Material; - public constructor(material: Material) { + constructor(material: Material) { this.material = material; } } class Shuriken implements Weapon { - public name = 'Shuriken'; + public name: string = 'Shuriken'; public material: Material; - public constructor(material: Material) { + constructor(material: Material) { this.material = material; } } @@ -353,74 +584,159 @@ describe('BindingWhenSyntax', () => { class NinjaMaster implements Ninja { public shuriken: Weapon; - public constructor(shuriken: Weapon) { + constructor(shuriken: Weapon) { this.shuriken = shuriken; } } class SamuraiMaster implements Samurai { public katana: Weapon; - public constructor(katana: Weapon) { + constructor(katana: Weapon) { this.katana = katana; } } class NinjaStudent implements Ninja { public shuriken: Weapon; - public constructor(shuriken: Weapon) { + constructor(shuriken: Weapon) { this.shuriken = shuriken; } } class SamuraiStudent implements Samurai { public katana: Weapon; - public constructor(katana: Weapon) { + constructor(katana: Weapon) { this.katana = katana; } } - const context = new Context(new Container()); + const context: Context = new Context(new Container()); // Samurai - const samuraiMasterBinding = new Binding('Samurai', BindingScopeEnum.Transient); + const samuraiMasterBinding: Binding = new Binding( + 'Samurai', + BindingScopeEnum.Transient, + ); samuraiMasterBinding.implementationType = SamuraiMaster; - const samuraiStudentBinding = new Binding('Samurai', BindingScopeEnum.Transient); + const samuraiStudentBinding: Binding = new Binding( + 'Samurai', + BindingScopeEnum.Transient, + ); samuraiStudentBinding.implementationType = SamuraiStudent; - const samuraiTarget = new Target(TargetTypeEnum.ConstructorArgument, '', 'Samurai', new Metadata('sneaky', false)); - const samuraiMasterRequest = new Request('Samurai', context, null, samuraiMasterBinding, samuraiTarget); - const samuraiStudentRequest = new Request('Samurai', context, null, samuraiStudentBinding, samuraiTarget); + const samuraiTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + '', + 'Samurai', + new Metadata('sneaky', false), + ); + const samuraiMasterRequest: Request = new Request( + 'Samurai', + context, + null, + samuraiMasterBinding, + samuraiTarget, + ); + const samuraiStudentRequest: Request = new Request( + 'Samurai', + context, + null, + samuraiStudentBinding, + samuraiTarget, + ); // Ninja - const ninjaMasterBinding = new Binding('Ninja', BindingScopeEnum.Transient); + const ninjaMasterBinding: Binding = new Binding( + 'Ninja', + BindingScopeEnum.Transient, + ); ninjaMasterBinding.implementationType = NinjaMaster; - const ninjaStudentBinding = new Binding('Ninja', BindingScopeEnum.Transient); + const ninjaStudentBinding: Binding = new Binding( + 'Ninja', + BindingScopeEnum.Transient, + ); ninjaStudentBinding.implementationType = NinjaStudent; - const ninjaTarget = new Target(TargetTypeEnum.ConstructorArgument, '', 'Ninja', new Metadata('sneaky', true)); - const ninjaMasterRequest = new Request('Ninja', context, null, ninjaMasterBinding, ninjaTarget); - const ninjaStudentRequest = new Request('Ninja', context, null, ninjaStudentBinding, ninjaTarget); + const ninjaTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + '', + 'Ninja', + new Metadata('sneaky', true), + ); + const ninjaMasterRequest: Request = new Request( + 'Ninja', + context, + null, + ninjaMasterBinding, + ninjaTarget, + ); + const ninjaStudentRequest: Request = new Request( + 'Ninja', + context, + null, + ninjaStudentBinding, + ninjaTarget, + ); // Katana - const katanaBinding = new Binding('Weapon', BindingScopeEnum.Transient); + const katanaBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); katanaBinding.implementationType = Katana; - const katanaBindingWhenSyntax = new BindingWhenSyntax(katanaBinding); - const katanaTarget = new Target(TargetTypeEnum.ConstructorArgument, 'katana', 'Weapon'); - const ironKatanaRequest = new Request('Weapon', context, samuraiMasterRequest, katanaBinding, katanaTarget); - const woodKatanaRequest = new Request('Weapon', context, samuraiStudentRequest, katanaBinding, katanaTarget); + const katanaBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(katanaBinding); + const katanaTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'katana', + 'Weapon', + ); + const ironKatanaRequest: Request = new Request( + 'Weapon', + context, + samuraiMasterRequest, + katanaBinding, + katanaTarget, + ); + const woodKatanaRequest: Request = new Request( + 'Weapon', + context, + samuraiStudentRequest, + katanaBinding, + katanaTarget, + ); // Shuriken - const shurikenBinding = new Binding('Weapon', BindingScopeEnum.Transient); + const shurikenBinding: Binding = new Binding( + 'Weapon', + BindingScopeEnum.Transient, + ); shurikenBinding.implementationType = Shuriken; - const shurikenBindingWhenSyntax = new BindingWhenSyntax(shurikenBinding); - const shurikenTarget = new Target(TargetTypeEnum.ConstructorArgument, 'shuriken', 'Weapon'); - const ironShurikenRequest = new Request('Weapon', context, ninjaMasterRequest, shurikenBinding, shurikenTarget); - const woodShurikenRequest = new Request('Weapon', context, ninjaStudentRequest, shurikenBinding, shurikenTarget); + const shurikenBindingWhenSyntax: BindingWhenSyntax = + new BindingWhenSyntax(shurikenBinding); + const shurikenTarget: Target = new Target( + TargetTypeEnum.ConstructorArgument, + 'shuriken', + 'Weapon', + ); + const ironShurikenRequest: Request = new Request( + 'Weapon', + context, + ninjaMasterRequest, + shurikenBinding, + shurikenTarget, + ); + const woodShurikenRequest: Request = new Request( + 'Weapon', + context, + ninjaStudentRequest, + shurikenBinding, + shurikenTarget, + ); it('Should be able to apply a type constraint to some of its ancestors', () => { - shurikenBindingWhenSyntax.whenAnyAncestorIs(NinjaMaster); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(false); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(true); @@ -436,11 +752,9 @@ describe('BindingWhenSyntax', () => { katanaBindingWhenSyntax.whenAnyAncestorIs(SamuraiStudent); expect(katanaBinding.constraint(woodKatanaRequest)).eql(true); expect(katanaBinding.constraint(ironKatanaRequest)).eql(false); - }); it('Should be able to apply a type constraint to none of its ancestors', () => { - shurikenBindingWhenSyntax.whenNoAncestorIs(NinjaMaster); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(true); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(false); @@ -456,11 +770,9 @@ describe('BindingWhenSyntax', () => { katanaBindingWhenSyntax.whenNoAncestorIs(SamuraiStudent); expect(katanaBinding.constraint(woodKatanaRequest)).eql(false); expect(katanaBinding.constraint(ironKatanaRequest)).eql(true); - }); it('Should be able to apply a named constraint to some of its ancestors', () => { - shurikenBindingWhenSyntax.whenAnyAncestorNamed('chinese'); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(false); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(false); @@ -476,11 +788,9 @@ describe('BindingWhenSyntax', () => { katanaBindingWhenSyntax.whenAnyAncestorNamed('japonese'); expect(katanaBinding.constraint(woodKatanaRequest)).eql(false); expect(katanaBinding.constraint(ironKatanaRequest)).eql(false); - }); it('Should be able to apply a named constraint to none of its ancestors', () => { - shurikenBindingWhenSyntax.whenNoAncestorNamed('chinese'); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(true); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(true); @@ -496,11 +806,9 @@ describe('BindingWhenSyntax', () => { katanaBindingWhenSyntax.whenNoAncestorNamed('japonese'); expect(katanaBinding.constraint(woodKatanaRequest)).eql(true); expect(katanaBinding.constraint(ironKatanaRequest)).eql(true); - }); it('Should be able to apply a tagged constraint to some of its ancestors', () => { - shurikenBindingWhenSyntax.whenAnyAncestorTagged('sneaky', true); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(true); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(true); @@ -516,11 +824,9 @@ describe('BindingWhenSyntax', () => { katanaBindingWhenSyntax.whenAnyAncestorTagged('sneaky', false); expect(katanaBinding.constraint(woodKatanaRequest)).eql(true); expect(katanaBinding.constraint(ironKatanaRequest)).eql(true); - }); it('Should be able to apply a tagged constraint to none of its ancestors', () => { - shurikenBindingWhenSyntax.whenNoAncestorTagged('sneaky', true); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(false); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(false); @@ -536,60 +842,86 @@ describe('BindingWhenSyntax', () => { katanaBindingWhenSyntax.whenNoAncestorTagged('sneaky', false); expect(katanaBinding.constraint(woodKatanaRequest)).eql(false); expect(katanaBinding.constraint(ironKatanaRequest)).eql(false); - }); it('Should be able to apply a custom constraint to some of its ancestors', () => { - - const anyAncestorIsNinjaMasterConstraint = typeConstraint(NinjaMaster); - const anyAncestorIsNinjaStudentConstraint = typeConstraint(NinjaStudent); - - shurikenBindingWhenSyntax.whenAnyAncestorMatches(anyAncestorIsNinjaMasterConstraint); + const anyAncestorIsNinjaMasterConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(NinjaMaster); + const anyAncestorIsNinjaStudentConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(NinjaStudent); + + shurikenBindingWhenSyntax.whenAnyAncestorMatches( + anyAncestorIsNinjaMasterConstraint, + ); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(false); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(true); - shurikenBindingWhenSyntax.whenAnyAncestorMatches(anyAncestorIsNinjaStudentConstraint); + shurikenBindingWhenSyntax.whenAnyAncestorMatches( + anyAncestorIsNinjaStudentConstraint, + ); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(true); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(false); - const anyAncestorIsSamuraiMasterConstraint = typeConstraint(SamuraiMaster); - const anyAncestorIsSamuraiStudentConstraint = typeConstraint(SamuraiStudent); + const anyAncestorIsSamuraiMasterConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(SamuraiMaster); + const anyAncestorIsSamuraiStudentConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(SamuraiStudent); - katanaBindingWhenSyntax.whenAnyAncestorMatches(anyAncestorIsSamuraiMasterConstraint); + katanaBindingWhenSyntax.whenAnyAncestorMatches( + anyAncestorIsSamuraiMasterConstraint, + ); expect(katanaBinding.constraint(woodKatanaRequest)).eql(false); expect(katanaBinding.constraint(ironKatanaRequest)).eql(true); - katanaBindingWhenSyntax.whenAnyAncestorMatches(anyAncestorIsSamuraiStudentConstraint); + katanaBindingWhenSyntax.whenAnyAncestorMatches( + anyAncestorIsSamuraiStudentConstraint, + ); expect(katanaBinding.constraint(woodKatanaRequest)).eql(true); expect(katanaBinding.constraint(ironKatanaRequest)).eql(false); - }); it('Should be able to apply a custom constraint to none of its ancestors', () => { - - const anyAncestorIsNinjaMasterConstraint = typeConstraint(NinjaMaster); - const anyAncestorIsNinjaStudentConstraint = typeConstraint(NinjaStudent); - - shurikenBindingWhenSyntax.whenNoAncestorMatches(anyAncestorIsNinjaMasterConstraint); + const anyAncestorIsNinjaMasterConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(NinjaMaster); + const anyAncestorIsNinjaStudentConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(NinjaStudent); + + shurikenBindingWhenSyntax.whenNoAncestorMatches( + anyAncestorIsNinjaMasterConstraint, + ); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(true); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(false); - shurikenBindingWhenSyntax.whenNoAncestorMatches(anyAncestorIsNinjaStudentConstraint); + shurikenBindingWhenSyntax.whenNoAncestorMatches( + anyAncestorIsNinjaStudentConstraint, + ); expect(shurikenBinding.constraint(woodShurikenRequest)).eql(false); expect(shurikenBinding.constraint(ironShurikenRequest)).eql(true); - const anyAncestorIsSamuraiMasterConstraint = typeConstraint(SamuraiMaster); - const anyAncestorIsSamuraiStudentConstraint = typeConstraint(SamuraiStudent); + const anyAncestorIsSamuraiMasterConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(SamuraiMaster); + const anyAncestorIsSamuraiStudentConstraint: ( + request: interfaces.Request | null, + ) => boolean = typeConstraint(SamuraiStudent); - katanaBindingWhenSyntax.whenNoAncestorMatches(anyAncestorIsSamuraiMasterConstraint); + katanaBindingWhenSyntax.whenNoAncestorMatches( + anyAncestorIsSamuraiMasterConstraint, + ); expect(katanaBinding.constraint(woodKatanaRequest)).eql(true); expect(katanaBinding.constraint(ironKatanaRequest)).eql(false); - katanaBindingWhenSyntax.whenNoAncestorMatches(anyAncestorIsSamuraiStudentConstraint); + katanaBindingWhenSyntax.whenNoAncestorMatches( + anyAncestorIsSamuraiStudentConstraint, + ); expect(katanaBinding.constraint(woodKatanaRequest)).eql(false); expect(katanaBinding.constraint(ironKatanaRequest)).eql(true); }); - }); - -}); \ No newline at end of file +}); diff --git a/test/syntax/constraint_helpers.test.ts b/test/syntax/constraint_helpers.test.ts index 6f0c2d5cd..1d42e1dcb 100644 --- a/test/syntax/constraint_helpers.test.ts +++ b/test/syntax/constraint_helpers.test.ts @@ -1,13 +1,10 @@ import { expect } from 'chai'; + import { typeConstraint } from '../../src/syntax/constraint_helpers'; describe('BindingInSyntax', () => { - it('Should be return false when a request object is not provided', () => { - - const result = typeConstraint('TYPE')(null); + const result: boolean = typeConstraint('TYPE')(null); expect(result).to.eql(false); - }); - -}); \ No newline at end of file +}); diff --git a/test/utils/binding_utils.test.ts b/test/utils/binding_utils.test.ts index 080152f3e..cca40ee1c 100644 --- a/test/utils/binding_utils.test.ts +++ b/test/utils/binding_utils.test.ts @@ -1,11 +1,14 @@ import { expect } from 'chai'; + import { Binding } from '../../src/bindings/binding'; import { getFactoryDetails } from '../../src/utils/binding_utils'; describe('getFactoryDetails', () => { it('should thrown an exception non factory binding.type', () => { - const binding = new Binding('', 'Singleton'); + const binding: Binding = new Binding('', 'Singleton'); binding.type = 'Instance'; - expect(() => getFactoryDetails(binding)).to.throw('Unexpected factory type Instance'); + expect(() => getFactoryDetails(binding)).to.throw( + 'Unexpected factory type Instance', + ); }); -}) \ No newline at end of file +}); diff --git a/test/utils/id.test.ts b/test/utils/id.test.ts index 66f010a84..f1671b132 100644 --- a/test/utils/id.test.ts +++ b/test/utils/id.test.ts @@ -1,11 +1,10 @@ -import { expect } from 'chai'; -import { id } from '../../src/utils/id'; - -describe('ID', () => { - - it('Should be able to generate an id', () => { - const id1 = id(); - expect(id1).to.be.a('number'); - }); - -}); \ No newline at end of file +import { expect } from 'chai'; + +import { id } from '../../src/utils/id'; + +describe('ID', () => { + it('Should be able to generate an id', () => { + const id1: number = id(); + expect(id1).to.be.a('number'); + }); +}); diff --git a/test/utils/reflection.test.ts b/test/utils/reflection.test.ts index 4dea8a246..87e167c7c 100644 --- a/test/utils/reflection.test.ts +++ b/test/utils/reflection.test.ts @@ -1,59 +1,61 @@ -import { expect } from "chai"; -import { injectable, inject, LazyServiceIdentifier, Container } from '../../src/inversify'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +import { + Container, + inject, + injectable, + interfaces, + LazyServiceIdentifier, +} from '../../src/inversify'; +import { MetadataReader } from '../../src/planning/metadata_reader'; import { getDependencies } from '../../src/planning/reflection_utils'; -import { MetadataReader } from "../../src/planning/metadata_reader"; -import sinon from "sinon"; describe('Reflection Utilities Unit Tests', () => { - - it('Should unwrap LazyServiceIdentifier in getConstructorArgsAsTarget', () => { - - interface Ninja { - fight(): string; - } - - interface Katana { - hit(): string; - } - - @injectable() - class Katana implements Katana { - public hit() { - return "cut!"; - } - } - - const TYPES = { - Katana: Symbol.for("Katana"), - Ninja: Symbol.for("Ninja"), - }; - - @injectable() - class Ninja implements Ninja { - - private _katana: Katana; - - public constructor( - @inject(new LazyServiceIdentifier(() => TYPES.Katana)) katana: Katana, - ) { - this._katana = katana; - } - - public fight() { return this._katana.hit(); } - - } - - const container = new Container(); - container.bind(TYPES.Ninja).to(Ninja); - container.bind(TYPES.Katana).to(Katana); - - const unwrapSpy = sinon.spy(LazyServiceIdentifier.prototype, 'unwrap'); - - const dependencies = getDependencies(new MetadataReader(), Ninja); - - expect(dependencies.length).to.be.eql(1); - sinon.assert.calledOnce(unwrapSpy); - - sinon.restore(); - }); + it('Should unwrap LazyServiceIdentifier in getConstructorArgsAsTarget', () => { + @injectable() + class Katana { + public hit() { + return 'cut!'; + } + } + + // eslint-disable-next-line @typescript-eslint/typedef + const TYPES = { + Katana: Symbol.for('Katana'), + Ninja: Symbol.for('Ninja'), + }; + + @injectable() + class Ninja implements Ninja { + private readonly _katana: Katana; + + constructor( + @inject(new LazyServiceIdentifier(() => TYPES.Katana)) katana: Katana, + ) { + this._katana = katana; + } + + public fight() { + return this._katana.hit(); + } + } + + const container: Container = new Container(); + container.bind(TYPES.Ninja).to(Ninja); + container.bind(TYPES.Katana).to(Katana); + + const unwrapSpy: sinon.SinonSpy<[], interfaces.ServiceIdentifier> = + sinon.spy(LazyServiceIdentifier.prototype, 'unwrap'); + + const dependencies: interfaces.Target[] = getDependencies( + new MetadataReader(), + Ninja, + ); + + expect(dependencies.length).to.be.eql(1); + sinon.assert.calledOnce(unwrapSpy); + + sinon.restore(); + }); }); diff --git a/test/utils/serialization.test.ts b/test/utils/serialization.test.ts index f2786fe15..2a1c914aa 100644 --- a/test/utils/serialization.test.ts +++ b/test/utils/serialization.test.ts @@ -1,43 +1,47 @@ -import { expect } from 'chai'; -import { TargetTypeEnum } from '../../src/constants/literal_types'; -import { Target } from '../../src/planning/target'; -import { getFunctionName, getSymbolDescription, listMetadataForTarget } from '../../src/utils/serialization'; - -describe('Serialization', () => { - - it('Should return a good function name', () => { - - function testFunction() { - return false; - } - - expect(getFunctionName(testFunction)).eql('testFunction'); - - }); - - it('Should return a good function name by using the regex', () => { - - const testFunction = { name: null }; - testFunction.toString = () => - 'function testFunction'; - - expect(getFunctionName(testFunction)).eql('testFunction'); - - }); - - it('Should not fail when target is not named or tagged', () => { - const serviceIdentifier = 'SomeTypeId'; - const target = new Target(TargetTypeEnum.Variable, '', serviceIdentifier); - const list = listMetadataForTarget(serviceIdentifier, target); - expect(list).to.eql(` ${serviceIdentifier}`); - }); - - it('Should extract symbol description', () => { - const symbolWithDescription = Symbol('description'); - expect(getSymbolDescription(symbolWithDescription)).to.equal('description'); - - const symbolWithoutDescription = Symbol(); - expect(getSymbolDescription(symbolWithoutDescription)).to.equal(''); - }); - -}); \ No newline at end of file +import { expect } from 'chai'; + +import { TargetTypeEnum } from '../../src/constants/literal_types'; +import { Target } from '../../src/planning/target'; +import { + getFunctionName, + getSymbolDescription, + listMetadataForTarget, +} from '../../src/utils/serialization'; + +describe('Serialization', () => { + it('Should return a good function name', () => { + function testFunction() { + return false; + } + + expect(getFunctionName(testFunction)).eql('testFunction'); + }); + + it('Should return a good function name by using the regex', () => { + const testFunction: { + name: null; + } = { name: null }; + testFunction.toString = () => 'function testFunction'; + + expect(getFunctionName(testFunction)).eql('testFunction'); + }); + + it('Should not fail when target is not named or tagged', () => { + const serviceIdentifier: string = 'SomeTypeId'; + const target: Target = new Target( + TargetTypeEnum.Variable, + '', + serviceIdentifier, + ); + const list: string = listMetadataForTarget(serviceIdentifier, target); + expect(list).to.eql(` ${serviceIdentifier}`); + }); + + it('Should extract symbol description', () => { + const symbolWithDescription: symbol = Symbol('description'); + expect(getSymbolDescription(symbolWithDescription)).to.equal('description'); + + const symbolWithoutDescription: symbol = Symbol(); + expect(getSymbolDescription(symbolWithoutDescription)).to.equal(''); + }); +}); diff --git a/test/utils/stubs.ts b/test/utils/stubs.ts index bd83562b8..00f5685cf 100644 --- a/test/utils/stubs.ts +++ b/test/utils/stubs.ts @@ -18,7 +18,7 @@ export interface FooBarInterface { export class Foo implements FooInterface { public name: string; - public constructor() { + constructor() { this.name = 'foo'; } public greet(): string { @@ -28,7 +28,7 @@ export class Foo implements FooInterface { export class Bar implements BarInterface { public name: string; - public constructor() { + constructor() { this.name = 'bar'; } public greet(): string { @@ -40,9 +40,9 @@ export class Bar implements BarInterface { export class FooBar implements FooBarInterface { public foo: FooInterface; public bar: BarInterface; - public constructor( + constructor( @inject('FooInterface') foo: FooInterface, - @inject('BarInterface') bar: BarInterface + @inject('BarInterface') bar: BarInterface, ) { this.foo = foo; this.bar = bar; @@ -52,62 +52,45 @@ export class FooBar implements FooBarInterface { } } -// 2.0 +export class Katana {} +export class Shuriken {} -export interface Weapon { } -export interface Katana extends Weapon { } -export interface Shuriken extends Weapon { } - -export class Katana implements Katana { } -export class Shuriken implements Shuriken { } - -export class WarriorWithoutInjections { } +export class WarriorWithoutInjections {} @injectable() -export class DecoratedWarriorWithoutInjections { } +export class DecoratedWarriorWithoutInjections {} @injectable() export class Warrior { - public constructor( - @inject('Katana') primary: Katana, - @inject('Shuriken') secondary: Shuriken - ) { - // ... - } + constructor( + @inject('Katana') _primary: Katana, + @inject('Shuriken') _secondary: Shuriken, + ) {} } export class InvalidDecoratorUsageWarrior { - public constructor( - primary: Weapon, - secondary: Weapon) { - // ... - } + // eslint-disable-next-line @typescript-eslint/no-useless-constructor + constructor(_primary: unknown, _secondary: unknown) {} } export class MissingInjectionWarrior { - public constructor( - primary: Weapon, - secondary: Weapon) { - // ... - } + // eslint-disable-next-line @typescript-eslint/no-useless-constructor + constructor(_primary: unknown, _secondary: unknown) {} } @injectable() export class NamedWarrior { - public constructor( - @inject('Katana') @named('strong') primary: Weapon, - @inject('Shuriken') @named('weak') secondary: Weapon - ) { - // ... - } + constructor( + @inject('Katana') @named('strong') _primary: unknown, + @inject('Shuriken') @named('weak') _secondary: unknown, + ) {} } @injectable() export class TaggedWarrior { - public constructor( - @inject('Katana') @tagged('power', 5) primary: Weapon, - @inject('Shuriken') @tagged('power', 1) secondary: Weapon - ) { - // ... - } -} \ No newline at end of file + constructor( + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + @inject('Katana') @tagged('power', 5) _primary: unknown, + @inject('Shuriken') @tagged('power', 1) _secondary: unknown, + ) {} +} diff --git a/tslint.json b/tslint.json deleted file mode 100644 index b9bc7c1be..000000000 --- a/tslint.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "extends": "tslint:recommended", - "rules": { - "array-type": [ - true, - "array" - ], - "indent": [ - true, - "spaces", - 2 - ], - "ban-types": false, - "comment-format": false, - "completed-docs": false, - "cyclomatic-complexity": false, - "interface-name": false, - "linebreak-style": false, - "max-classes-per-file": false, - "max-file-line-count": false, - "max-line-length": [ - true, - { - "limit": 140, - "ignore-pattern": "^import" - } - ], - "member-ordering": false, - "newline-before-return": false, - "newline-per-chained-call": false, - "no-any": false, - "no-empty-interface": false, - "no-floating-promises": false, - "no-implicit-dependencies": [ - true, - "dev" - ], - "no-import-side-effect": false, - "no-inferred-empty-object-type": false, - "no-magic-numbers": false, - "no-namespace": false, - "no-null-keyword": false, - "no-parameter-properties": false, - "no-submodule-imports": false, - "no-unbound-method": false, - "no-unnecessary-class": false, - "no-unnecessary-qualifier": false, - "no-unsafe-any": false, - "no-reference": false, - "no-void-expression": false, - "only-arrow-functions": false, - "prefer-function-over-method": false, - "prefer-template": false, - "prefer-readonly": false, - "promise-function-async": false, - "space-before-function-paren": false, - "strict-boolean-expressions": false, - "strict-type-predicates": false, - "switch-default": false, - "trailing-comma": false, - "typedef": false, - "variable-name": false - } -} \ No newline at end of file diff --git a/wiki/activation_handler.md b/wiki/activation_handler.md index e43ae9dd0..bf0c49058 100644 --- a/wiki/activation_handler.md +++ b/wiki/activation_handler.md @@ -25,7 +25,7 @@ interface Ninja { @injectable() class Ninja implements Ninja { public katana: Katana; - public constructor(@inject("Katana") katana: Katana) { + constructor(@inject("Katana") katana: Katana) { this.katana = katana; } } diff --git a/wiki/auto_factory.md b/wiki/auto_factory.md index fabf2eee6..09240aae9 100644 --- a/wiki/auto_factory.md +++ b/wiki/auto_factory.md @@ -9,9 +9,9 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( - @inject("Factory") katanaFactory: interfaces.AutoFactory, - @inject("Shuriken") shuriken: Shuriken + constructor( + @inject("Factory") katanaFactory: interfaces.AutoFactory, + @inject("Shuriken") shuriken: Shuriken ) { this._katana = katanaFactory(); this._shuriken = shuriken; @@ -27,5 +27,5 @@ class Ninja implements Ninja { container.bind("Katana").to(Katana); container.bind>("Factory") - .toAutoFactory("Katana"); + .toAutoFactory("Katana"); ``` diff --git a/wiki/auto_named_factory.md b/wiki/auto_named_factory.md index 1b7c7d71c..11d8544e9 100644 --- a/wiki/auto_named_factory.md +++ b/wiki/auto_named_factory.md @@ -7,7 +7,7 @@ Binds an abstraction to an auto-generated Factory that return elements by given class Ninja implements Ninja { private _katana: Weapon; private _shuriken: Weapon; - public constructor( + constructor( @inject("Factory") weaponFactory: (named: string) => Weapon ) { this._katana = weaponFactory("katana"); diff --git a/wiki/classes_as_id.md b/wiki/classes_as_id.md index e7fa0b3e9..f9855e3fb 100644 --- a/wiki/classes_as_id.md +++ b/wiki/classes_as_id.md @@ -29,7 +29,7 @@ class Ninja implements Warrior { private _katana: Katana; private _shuriken: Shuriken; - public constructor(katana: Katana, shuriken: Shuriken) { + constructor(katana: Katana, shuriken: Shuriken) { this._katana = katana; this._shuriken = shuriken; } @@ -108,7 +108,7 @@ import "reflect-metadata"; import { Container, injectable, inject } from "inversify"; import getDecorators from "inversify-inject-decorators"; -const container = new Container(); +const container: Container = new Container(); const { lazyInject } = getDecorators(container); const TYPE = { @@ -132,7 +132,7 @@ class DomUi { class Dom { public name: string; @lazyInject(TYPE.DomUi) public domUi: DomUi; - public constructor() { + constructor() { this.name = "Dom"; } } diff --git a/wiki/constructor_injection.md b/wiki/constructor_injection.md index 75bfd67a7..c21164cf5 100644 --- a/wiki/constructor_injection.md +++ b/wiki/constructor_injection.md @@ -13,10 +13,10 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( - @inject("Newable") Katana: interfaces.Newable, - @inject("Shuriken") shuriken: Shuriken - ) { + constructor( + @inject("Newable") Katana: interfaces.Newable, + @inject("Shuriken") shuriken: Shuriken + ) { this._katana = new Katana(); this._shuriken = shuriken; } @@ -41,7 +41,7 @@ and the container will have no trouble injecting the dependencies : @injectable() class Ninja implements Ninja { - public constructor(private _dagger:Dagger) { + constructor(private _dagger:Dagger) { } diff --git a/wiki/container_api.md b/wiki/container_api.md index 29b7ba3b6..8c2f61af5 100644 --- a/wiki/container_api.md +++ b/wiki/container_api.md @@ -503,13 +503,13 @@ class Katana { @injectable() class Ninja implements Ninja { public katana: Katana; - public constructor(katana: Katana) { + constructor(katana: Katana) { this.katana = katana; } public fight() { return this.katana.hit(); } } -const container = new Container(); +const container: Container = new Container(); container.bind(Katana).toSelf(); const tryGet = () => container.get(Ninja); diff --git a/wiki/contextual_bindings.md b/wiki/contextual_bindings.md index cdfa8a2ca..450a88c23 100644 --- a/wiki/contextual_bindings.md +++ b/wiki/contextual_bindings.md @@ -22,7 +22,7 @@ interface Ninja { class Ninja implements Ninja { public katana: Weapon; public shuriken: Weapon; - public constructor( + constructor( @inject("Weapon") @targetName("katana") katana: Weapon, @inject("Weapon") @targetName("shuriken") shuriken: Weapon ) { @@ -50,11 +50,11 @@ The target fields implement the `IQueryableString` interface to help you to crea ```ts interface QueryableString { - startsWith(searchString: string): boolean; - endsWith(searchString: string): boolean; - contains(searchString: string): boolean; - equals(compareString: string): boolean; - value(): string; + startsWith(searchString: string): boolean; + endsWith(searchString: string): boolean; + contains(searchString: string): boolean; + equals(compareString: string): boolean; + value(): string; } ``` We have included some helpers to facilitate the creation of custom constraints: diff --git a/wiki/custom_tag_decorators.md b/wiki/custom_tag_decorators.md index e3a1e7cb4..a68d228a0 100644 --- a/wiki/custom_tag_decorators.md +++ b/wiki/custom_tag_decorators.md @@ -10,7 +10,7 @@ let notThrowable = tagged("canThrow", false); class Ninja implements Ninja { public katana: Weapon; public shuriken: Weapon; - public constructor( + constructor( @inject("Weapon") @notThrowable katana: Weapon, @inject("Weapon") @throwable shuriken: Weapon ) { @@ -31,7 +31,7 @@ const dontLikeMondays = moodReason("miserable","I don't like Mondays"); @injectable() class MoodyNinja { - public constructor( + constructor( @inject("Response") @happyAndIKnowIt clapHands: Response, @inject("Response") @dontLikeMondays shootWholeDayDown: Response ) { diff --git a/wiki/deactivation_handler.md b/wiki/deactivation_handler.md index d27cd6887..639bf09f9 100644 --- a/wiki/deactivation_handler.md +++ b/wiki/deactivation_handler.md @@ -7,7 +7,7 @@ It is possible to add a deactivation handler for a type binded in singleton scop class Destroyable { } -const container = new Container(); +const container: Container = new Container(); container.bind("Destroyable").toDynamicValue(() => Promise.resolve(new Destroyable())).inSingletonScope() .onDeactivation((destroyable: Destroyable) => { console.log("Destroyable service is about to be unbinded"); @@ -47,7 +47,7 @@ class Destroyable { } } -const container = new Container(); +const container: Container = new Container(); container.onDeactivation("Destroyable", () => { return new Promise((presolve) => { parent = roll; diff --git a/wiki/default_targets.md b/wiki/default_targets.md index 42e648e12..c22eba026 100644 --- a/wiki/default_targets.md +++ b/wiki/default_targets.md @@ -54,7 +54,7 @@ interface Weapon { @injectable() class Katana implements Weapon { public name: string; - public constructor() { + constructor() { this.name = "Katana"; } } @@ -62,7 +62,7 @@ class Katana implements Weapon { @injectable() class Shuriken implements Weapon { public name: string; - public constructor() { + constructor() { this.name = "Shuriken"; } } diff --git a/wiki/factory_injection.md b/wiki/factory_injection.md index 14c5564cc..83355048f 100644 --- a/wiki/factory_injection.md +++ b/wiki/factory_injection.md @@ -8,9 +8,9 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( - @inject("Factory") katanaFactory: () => Katana, - @inject("Shuriken") shuriken: Shuriken + constructor( + @inject("Factory") katanaFactory: () => Katana, + @inject("Shuriken") shuriken: Shuriken ) { this._katana = katanaFactory(); this._shuriken = shuriken; diff --git a/wiki/inheritance.md b/wiki/inheritance.md index eaf40edf6..dbcd7f439 100644 --- a/wiki/inheritance.md +++ b/wiki/inheritance.md @@ -14,14 +14,14 @@ For example, the following code snippet throws a misleading error: @injectable() class Warrior { public rank: string; - public constructor(rank: string) { // args count = 1 + constructor(rank: string) { // args count = 1 this.rank = rank; } } @injectable() class SamuraiMaster extends Warrior { - public constructor() { // args count = 0 + constructor() { // args count = 0 super("master"); } } @@ -56,14 +56,14 @@ const BaseId = "Base"; @injectable() class Base { public prop: string; - public constructor(@unmanaged() arg: string) { + constructor(@unmanaged() arg: string) { this.prop = arg; } } @injectable() class Derived extends Base { - public constructor() { + constructor() { super("unmanaged-injected-value"); } } @@ -84,14 +84,14 @@ property setter to avoid injecting into the base class: @injectable() class Warrior { protected rank: string; - public constructor() { // args count = 0 + constructor() { // args count = 0 this.rank = null; } } @injectable() class SamuraiMaster extends Warrior { - public constructor() { // args count = 0 + constructor() { // args count = 0 super(); this.rank = "master"; } @@ -106,7 +106,7 @@ We can also use property injection to avoid injecting into the base class: @injectable() class Warrior { protected rank: string; - public constructor() {} // args count = 0 + constructor() {} // args count = 0 } let TYPES = { Rank: "Rank" }; @@ -117,7 +117,7 @@ class SamuraiMaster extends Warrior { @named("master") protected rank: string; - public constructor() { // args count = 0 + constructor() { // args count = 0 super(); } } @@ -138,7 +138,7 @@ its constructor (super). @injectable() class Warrior { protected rank: string; - public constructor(rank: string) { // args count = 1 + constructor(rank: string) { // args count = 1 this.rank = rank; } } @@ -147,7 +147,7 @@ let TYPES = { Rank: "Rank" }; @injectable() class SamuraiMaster extends Warrior { - public constructor( + constructor( @inject(TYPES.Rank) @named("master") rank: string // args count = 1 ) { super(rank); @@ -166,7 +166,7 @@ The following should also work: @injectable() class Warrior { protected rank: string; - public constructor(rank: string) { // args count = 1 + constructor(rank: string) { // args count = 1 this.rank = rank; } } @@ -178,7 +178,7 @@ interface Weapon { @injectable() class Katana implements Weapon { public name: string; - public constructor() { + constructor() { this.name = "Katana"; } } @@ -191,7 +191,7 @@ let TYPES = { @injectable() class SamuraiMaster extends Warrior { public weapon: Weapon; - public constructor( + constructor( @inject(TYPES.Rank) @named("master") rank: string, // args count = 2 @inject(TYPES.Weapon) weapon: Weapon ) { @@ -215,18 +215,18 @@ Setting the `skipBaseClassChecks` option to `true` for the container will disabl ```ts // Not injectable class UnmanagedBase { - public constructor(public unmanagedDependency: string) {} + constructor(public unmanagedDependency: string) {} } @injectable() class InjectableDerived extends UnmanagedBase { - public constructor() // Any arguments defined here will be injected like normal + constructor() // Any arguments defined here will be injected like normal { super("Don't forget me..."); } } -const container = new Container({ skipBaseClassChecks: true }); +const container: Container = new Container({ skipBaseClassChecks: true }); container.bind(InjectableDerived).toSelf(); ``` diff --git a/wiki/injecting_npm_modules.md b/wiki/injecting_npm_modules.md index 2878573a6..a2746ff26 100644 --- a/wiki/injecting_npm_modules.md +++ b/wiki/injecting_npm_modules.md @@ -63,7 +63,7 @@ const applicationDependencies = new ContainerModule((bind) => { // .. }); -const container = new Container(); +const container: Container = new Container(); container.load(thirdPartyDependencies, applicationDependencies); @@ -84,7 +84,7 @@ class SomeClass implements SomeClassInterface { private _lodash: Lodash; private _sequelize: Sequelize; - public constructor( + constructor( @inject(TYPES.Lodash) lodash, @inject(TYPES.Sequelize) sequelize, ) { diff --git a/wiki/middleware.md b/wiki/middleware.md index 8d720ebbd..34bdad7f5 100644 --- a/wiki/middleware.md +++ b/wiki/middleware.md @@ -126,7 +126,7 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( + constructor( @inject("Katana") katana: Katana, @inject("Shuriken") shuriken: Shuriken ) { @@ -154,7 +154,7 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( + constructor( katana: Katana, shuriken: Shuriken ) { diff --git a/wiki/multi_injection.md b/wiki/multi_injection.md index 8a1949dc7..b8f19f03e 100644 --- a/wiki/multi_injection.md +++ b/wiki/multi_injection.md @@ -27,8 +27,8 @@ interface Ninja { class Ninja implements Ninja { public katana: Weapon; public shuriken: Weapon; - public constructor( - @multiInject("Weapon") weapons: Weapon[] + constructor( + @multiInject("Weapon") weapons: Weapon[] ) { this.katana = weapons[0]; this.shuriken = weapons[1]; diff --git a/wiki/named_bindings.md b/wiki/named_bindings.md index af9abd1cb..d69ca5f00 100644 --- a/wiki/named_bindings.md +++ b/wiki/named_bindings.md @@ -21,7 +21,7 @@ interface Ninja { class Ninja implements Ninja { public katana: Weapon; public shuriken: Weapon; - public constructor( + constructor( @inject("Weapon") @named("strong") katana: Weapon, @inject("Weapon") @named("weak") shuriken: Weapon ) { diff --git a/wiki/oo_design.md b/wiki/oo_design.md index 6d510b498..9eb79e8c4 100644 --- a/wiki/oo_design.md +++ b/wiki/oo_design.md @@ -22,7 +22,7 @@ between modules. Let's see an example: import BaseModel from "someframework"; class DerivedModel extends BaseModel { - public constructor() { + constructor() { super(); } public saveOrUpdate() { @@ -45,7 +45,7 @@ The following example do something similar but it favors 'object composition' ov @injectable() class DerivedModel { public baseModel: BaseModel; - public constructor(@inject("BaseModel") baseModel: BaseModel) { + constructor(@inject("BaseModel") baseModel: BaseModel) { this.baseModel = baseModel; } public saveOrUpdate() { @@ -72,7 +72,7 @@ class Ninja { private _katana: Katana; - public constructor( + constructor( katana: Katana ) { this._katana = katana; @@ -99,7 +99,7 @@ class Ninja { private _katana: Katana; - public constructor( + constructor( @inject("Katana") katana: Katana ) { this._katana = katana; diff --git a/wiki/optional_dependencies.md b/wiki/optional_dependencies.md index ab202eb5a..59f96fa7b 100644 --- a/wiki/optional_dependencies.md +++ b/wiki/optional_dependencies.md @@ -6,7 +6,7 @@ We can declare an optional dependency using the `@optional()` decorator: @injectable() class Katana { public name: string; - public constructor() { + constructor() { this.name = "Katana"; } } @@ -14,7 +14,7 @@ class Katana { @injectable() class Shuriken { public name: string; - public constructor() { + constructor() { this.name = "Shuriken"; } } @@ -24,7 +24,7 @@ class Ninja { public name: string; public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject("Katana") katana: Katana, @inject("Shuriken") @optional() shuriken: Shuriken // Optional! ) { @@ -74,7 +74,7 @@ class Ninja { public name: string; public katana: Katana; public shuriken: Shuriken; - public constructor( + constructor( @inject("Katana") katana: Katana, @inject("Shuriken") @optional() shuriken: Shuriken = { name: "DefaultShuriken" } // Default value! ) { diff --git a/wiki/pre_destroy.md b/wiki/pre_destroy.md index 34a99c4b8..7f4123c72 100644 --- a/wiki/pre_destroy.md +++ b/wiki/pre_destroy.md @@ -11,7 +11,7 @@ class Destroyable { } } -const container = new Container(); +const container: Container = new Container(); container.bind("Destroyable").to(Destroyable).inSingletonScope(); container.get("Destroyable"); diff --git a/wiki/property_injection.md b/wiki/property_injection.md index 3d7d7400a..f9bee5ed5 100644 --- a/wiki/property_injection.md +++ b/wiki/property_injection.md @@ -45,7 +45,7 @@ class Book { @inject("PrintService") private _printService: PrintService; - public constructor( + constructor( @inject("Author") author: Author, @inject("Summary") summary: Summary ) { @@ -104,7 +104,7 @@ class Book { @lazyInject("PrintService") private _printService: PrintService; - public constructor(author: string, summary: string) { + constructor(author: string, summary: string) { this._author = author; this._summary = summary; } diff --git a/wiki/provider_injection.md b/wiki/provider_injection.md index fde61a24d..472248e3e 100644 --- a/wiki/provider_injection.md +++ b/wiki/provider_injection.md @@ -13,9 +13,9 @@ class Ninja implements Ninja { public shuriken: Shuriken; public katanaProvider: KatanaProvider; - public constructor( - @inject("KatanaProvider") katanaProvider: KatanaProvider, - @inject("Shuriken") shuriken: Shuriken + constructor( + @inject("KatanaProvider") katanaProvider: KatanaProvider, + @inject("Shuriken") shuriken: Shuriken ) { this.katanaProvider = katanaProvider; this.katana = null; @@ -173,7 +173,7 @@ interface Warrior { @injectable() class Ninja implements Warrior { public level: number; - public constructor() { + constructor() { this.level = 0; } } @@ -227,7 +227,7 @@ The following example showcases how to apply the `valueOrDefault` helper: class Ninja { public level: number; public rank: string; - public constructor() { + constructor() { this.level = 0; this.rank = "Ninja"; } @@ -244,7 +244,7 @@ class Ninja { @injectable() class NinjaMaster { public rank: string; - public constructor() { + constructor() { this.rank = "NinjaMaster"; } } diff --git a/wiki/purpose.md b/wiki/purpose.md index 8a1c76892..b2c2b12f6 100644 --- a/wiki/purpose.md +++ b/wiki/purpose.md @@ -25,7 +25,7 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( + constructor( @inject(TYPES.Katana) katana: Katana, @inject(TYPES.Shuriken) shuriken: Shuriken ) { diff --git a/wiki/recipes.md b/wiki/recipes.md index c3fb2e286..cbde85908 100644 --- a/wiki/recipes.md +++ b/wiki/recipes.md @@ -153,7 +153,7 @@ class Weapon implements IWeapon { private readonly _name: string; public parent: IWeaponHolder; - public constructor( + constructor( // We can inject stuff into Weapon @inject(TYPE.WeaponName) name: string ) { @@ -174,7 +174,7 @@ class Weapon implements IWeapon { class Character implements IWeaponHolder { public weapon: IWeapon; public name: string; - public constructor( + constructor( @inject(TYPE.FactoryOfWeapon) factoryOfWeapon: FactoryOfWeapon ) { this.name = "Ninja"; @@ -185,7 +185,7 @@ class Character implements IWeaponHolder { } } -const container = new Container(); +const container: Container = new Container(); // We inject a string just to demostrate that we can inject stuff into Weapon container.bind(TYPE.WeaponName).toConstantValue("Katana"); @@ -240,7 +240,7 @@ class Weapon implements IWeapon { private readonly _name: string; public parent: IWeaponHolder; - public constructor( + constructor( // We can inject stuff into Weapon @inject(TYPE.WeaponName) name: string ) { @@ -261,7 +261,7 @@ class Weapon implements IWeapon { class Character implements IWeaponHolder { public weapon: IWeapon; public name: string; - public constructor( + constructor( @inject(TYPE.Weapon) weapon: IWeapon ) { this.name = "Ninja"; @@ -272,7 +272,7 @@ class Character implements IWeaponHolder { } } -const container = new Container(); +const container: Container = new Container(); // We inject a string just to demostrate that we can inject stuff into Weapon container.bind(TYPE.WeaponName).toConstantValue("Katana"); diff --git a/wiki/symbols_as_id.md b/wiki/symbols_as_id.md index b8a758a98..4f3b1d259 100644 --- a/wiki/symbols_as_id.md +++ b/wiki/symbols_as_id.md @@ -7,9 +7,9 @@ In very large applications using strings as the identifiers of the types to be i import { Container, injectable, inject } from "inversify"; let Symbols = { - Ninja : Symbol.for("Ninja"), - Katana : Symbol.for("Katana"), - Shuriken : Symbol.for("Shuriken") + Ninja : Symbol.for("Ninja"), + Katana : Symbol.for("Katana"), + Shuriken : Symbol.for("Shuriken") }; @injectable() @@ -32,9 +32,9 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( - @inject(Symbols.Katana) katana: Katana, - @inject(Symbols.Shuriken) shuriken: Shuriken + constructor( + @inject(Symbols.Katana) katana: Katana, + @inject(Symbols.Shuriken) shuriken: Shuriken ) { this._katana = katana; this._shuriken = shuriken; diff --git a/wiki/tagged_bindings.md b/wiki/tagged_bindings.md index 6ffabbbf4..d205b4031 100644 --- a/wiki/tagged_bindings.md +++ b/wiki/tagged_bindings.md @@ -22,7 +22,7 @@ interface Ninja { class Ninja implements Ninja { public katana: Weapon; public shuriken: Weapon; - public constructor( + constructor( @inject("Weapon") @tagged("canThrow", false) katana: Weapon, @inject("Weapon") @tagged("canThrow", true) shuriken: Weapon ) { diff --git a/wiki/testing.md b/wiki/testing.md index 5236fde1c..3c8f80423 100644 --- a/wiki/testing.md +++ b/wiki/testing.md @@ -56,7 +56,7 @@ describe('Fetching Users Integration Test', () => { let mockUserApiService: jest.Mocked; beforeAll(() => { - const container = new Container(); + const container: Container = new Container(); container.bind(UserService).toSelf(); container.bind(UserDAL).toSelf(); @@ -97,7 +97,7 @@ describe('User Service Unit Spec', () => { let mockUserApiService: jest.Mocked; beforeAll(() => { - const container = new Container(); + const container: Container = new Container(); container.bind(UserService).toSelf(); diff --git a/wiki/transitive_bindings.md b/wiki/transitive_bindings.md index ca4604d09..8d7248891 100644 --- a/wiki/transitive_bindings.md +++ b/wiki/transitive_bindings.md @@ -9,7 +9,7 @@ A transitive binding can be declared using the `toService` method: class MySqlDatabaseTransactionLog { public time: number; public name: string; - public constructor() { + constructor() { this.time = new Date().getTime(); this.name = "MySqlDatabaseTransactionLog"; } @@ -27,7 +27,7 @@ class TransactionLog { public name: string; } -const container = new Container(); +const container: Container = new Container(); container.bind(MySqlDatabaseTransactionLog).toSelf().inSingletonScope(); container.bind(DatabaseTransactionLog).toService(MySqlDatabaseTransactionLog); container.bind(TransactionLog).toService(DatabaseTransactionLog); diff --git a/wiki/upgrade.md b/wiki/upgrade.md index 261badacf..17abf7b7a 100644 --- a/wiki/upgrade.md +++ b/wiki/upgrade.md @@ -88,7 +88,7 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( + constructor( katana: Katana, shuriken: Shuriken ) { @@ -110,7 +110,7 @@ class Ninja implements Ninja { private _katana: Katana; private _shuriken: Shuriken; - public constructor( + constructor( @inject("Katana") katana: Katana, @inject("Shuriken") shuriken: Shuriken ) {