Compare commits

..

24 Commits

Author SHA1 Message Date
a70869a941 docs: reflect changes to changelog 2025-04-02 22:20:58 +02:00
7a6253a386 docs: Push version to 1.2.0 2025-04-02 22:20:58 +02:00
31de73db00 docs(api): add full JSDoc for all inject() overloads with examples 2025-04-02 22:20:58 +02:00
688a9d4ee4 docs: reflect changes to changelog 2025-04-02 22:01:10 +02:00
39dbd6d816 docs: Push version to 1.1.0 2025-04-02 22:01:10 +02:00
0718ff9d68 feat(api): expose inject() function via index.ts 2025-04-02 22:01:10 +02:00
115b3181e0 docs: reflect changes to changelog 2025-04-02 21:52:36 +02:00
c7fa78270c chore: update package-lock.json 2025-04-02 21:52:36 +02:00
3401656219 test(inject): add test suite to verify behavior of new inject() function 2025-04-02 21:52:36 +02:00
7e7fd996b3 feat(core): add inject() function to programmatically inject dependencies 2025-04-02 21:52:36 +02:00
a6fabc329b refactor(resolve): rename necessary parameter to isNecessary for clarity 2025-04-02 21:52:36 +02:00
81fb7f0071 refactor(inject): rename necessary parameter to isNecessary for clarity 2025-04-02 21:52:36 +02:00
2e89cdd619 test(config): modernize jest setup for ESM 2025-04-02 21:52:36 +02:00
b157f48261 fix: imports in tests 2025-04-02 21:52:36 +02:00
bbf68bff34 docs: reflect changes to changelog 2025-04-02 21:52:36 +02:00
b994a074c0 docs: Push version to 1.0.0 2025-04-02 21:52:36 +02:00
1da8efff94 feat(cli): add --without-extension flag to control import path formatting 2025-04-02 21:52:36 +02:00
c6e9fbd2a3 feat!: switch to native ESM with NodeNext module resolution and .js import paths
BREAKING CHANGE: Consumers must use ESM-compatible environments.
All import paths now include .js extensions.
CommonJS (require) is no longer supported.
2025-04-02 21:52:36 +02:00
9bd899581f docs: reflect new export to changelog 2025-04-02 20:22:07 +02:00
b43e4ee12f docs: push version to 0.4.0 2025-04-02 20:22:07 +02:00
ef4a2295bf feat: Export ImplementsStatic helper function 2025-04-02 20:22:07 +02:00
f4f8c7b78e docs: Fix changelog links 2025-03-14 13:52:53 +01:00
beaad4f65e docs: Fix changelog links 2025-03-14 13:51:17 +01:00
20Max01
95ef003e99 Feature/integrate register instance in register (#2)
* refactor: consolidate registration decorators

- Introduced `Register` decorator to handle class and instance registration in the DI container.
- Deprecated `RegisterInstance` in favor of `Register`, which now internally handles instance registration.
- Added support for marking dependencies as deprecated with a warning logged upon first resolution.
- Updated documentation with examples and notes on deprecation.

* tests: add mode parameter to RegisterInstanceDecorator

- Introduced a `mode` parameter to the `test_RegisterInstanceDecorator` function allowing 'instance' or 'standalone' modes.
- Updated test cases to utilize the new `mode` parameter when registering an instance.
- Disabled specific ESLint rule in `Decorators.test.ts` for deprecation warnings.
- Added an additional test call to `test_RegisterInstanceDecorator` with 'instance' mode.

* docs: Reflect changes to changelog

* refactor: add region tags for overloads in Register.ts

* docs: Reflect changes to changelog

* refactor: consolidate registration decorators

- Introduced `Register` decorator to handle class and instance registration in the DI container.
- Deprecated `RegisterInstance` in favor of `Register`, which now internally handles instance registration.
- Added support for marking dependencies as deprecated with a warning logged upon first resolution.
- Updated documentation with examples and notes on deprecation.

* tests: add mode parameter to RegisterInstanceDecorator

- Introduced a `mode` parameter to the `test_RegisterInstanceDecorator` function allowing 'instance' or 'standalone' modes.
- Updated test cases to utilize the new `mode` parameter when registering an instance.
- Disabled specific ESLint rule in `Decorators.test.ts` for deprecation warnings.
- Added an additional test call to `test_RegisterInstanceDecorator` with 'instance' mode.

* docs: Reflect changes to changelog

* refactor: add region tags for overloads in Register.ts

* docs: Reflect changes to changelog

* docs: Reflect changes to changelog und push version
2025-03-14 13:43:10 +01:00
23 changed files with 824 additions and 163 deletions

View File

@@ -9,71 +9,148 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
### Deprecated ### Deprecated
### Removed ### Removed
### Fixed ### Fixed
### Security ### Security
## [1.2.0]
### Added
- docs: added complete Typedoc-style documentation for all `inject()` overloads
Includes parameter descriptions, usage examples, and detailed error annotations for each variant.
## [1.1.0]
### Added
- feat: exported `inject()` function from the public API via `index.ts`
The function is now available as part of the main module exports.
## [1.0.0]
### Added
- feat: Enable native ESM support using `"type": "module"` and `moduleResolution: "NodeNext"` in the compiler settings.
- feat: All internal imports now explicitly include `.js` extensions for full Node.js ESM compatibility.
- feat: Updated `tsconfig.json` to reflect changes for ESM builds (`module: "NodeNext"`, `target: "ES2020"`, etc.).
- feat(cli): add `--without-extension` (`-x`) flag to optionally omit file extensions in generated import paths
- feat: introduce `inject()` function as a programmatic alternative to the `@Inject` decorator
Supports optional initializers and constructor instantiation for resolved dependencies.
Designed for cases where decorators are not suitable or dynamic resolution is needed.
- test: added comprehensive test suite for `inject()` function, covering resolution, initialization, error cases and instantiation behavior
### Changed
- All source files using relative or internal imports were updated to use `.js` extensions to support Node.js ESM runtime resolution.
- test: update Jest config for ts-jest ESM compatibility and .js import support
- renamed internal parameter `necessary``isNecessary` for naming clarity
### Removed
- Removed implicit support for CommonJS-style imports without file extensions.
### Deprecated
- Support for CommonJS consumers using `require()` is no longer available. Use `import` with an ESM-compatible environment instead.
### Fixed
### Security
### ⚠️ Breaking Changes
- **BREAKING CHANGE**: This version migrates the entire codebase to native ES modules.
- Consumers must use Node.js in ESM mode or compatible bundlers.
- Import paths now include `.js` extensions.
- Using `require()` (CommonJS) to load this library will no longer work.
- All consuming projects must either:
- Use `"type": "module"` in their `package.json`, or
- Use an ESM-aware bundler (e.g. Webpack, Vite, etc.)
## [0.4.0]
### Added
- feat: Export ImplementsStatic helper function
### Deprecated
### Removed
### Fixed
### Security
## [0.3.0]
### Added
- refactor: consolidate registration decorators
Introduced Register decorator to handle class and instance registration in the DI container.
Deprecated RegisterInstance in favor of Register, which now internally handles instance registration.
Added support for marking dependencies as deprecated with a warning logged upon first resolution.
Updated documentation with examples and notes on deprecation.
- tests: add mode parameter to RegisterInstanceDecorator
Introduced a mode parameter to the test_RegisterInstanceDecorator function allowing 'instance' or 'standalone' modes.
Updated test cases to utilize the new mode parameter when registering an instance.
Disabled specific ESLint rule in Decorators.test.ts for deprecation warnings.
Added an additional test call to test_RegisterInstanceDecorator with 'instance' mode.
- refactor: add region tags for overloads in Register.ts
## [0.2.0] ## [0.2.0]
### Added ### Added
- Add pre release building to release workflow on dev/* branches an version changes. - Add pre release building to release workflow on dev/\* branches an version changes.
- feat: Introduced a new CLI command `tsinjex-generate` to automate the generation of import statements for registered dependencies. - feat: Introduced a new CLI command `tsinjex-generate` to automate the generation of import statements for registered dependencies.
The command scans `.ts` files for `@Register` and `@RegisterInstance` decorators and generates an `auto-imports.ts` file. The command scans `.ts` files for `@Register` and `@RegisterInstance` decorators and generates an `auto-imports.ts` file.
This ensures that all registered dependencies are automatically included without requiring manual imports. This ensures that all registered dependencies are automatically included without requiring manual imports.
The CLI can be executed via `npx tsinjex-generate` or added as a script in `package.json` for easier integration. The CLI can be executed via `npx tsinjex-generate` or added as a script in `package.json` for easier integration.
### Deprecated ### Deprecated
### Removed ### Removed
### Fixed ### Fixed
### Security ### Security
## [0.0.14] ## [0.0.14]
### Added ### Added
- Added **ChangeLog** file and format it according to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Added **ChangeLog** file and format it according to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Added reference to **Semantic Versioning** in the changelog file. (History will be updated on time). - Added reference to **Semantic Versioning** in the changelog file. (History will be updated on time).
- Version format is now `v0.0.0` instead of `0.0.0`. Changes to this are also reflected in the workflos. - Version format is now `v0.0.0` instead of `0.0.0`. Changes to this are also reflected in the workflos.
- Add `Identifiers` and `Jest` Sections to the `README.md` file. - Add `Identifiers` and `Jest` Sections to the `README.md` file.
- feat: Add new Error `InitializationError` to reflect errors during initialization of a dependency. - feat: Add new Error `InitializationError` to reflect errors during initialization of a dependency.
- feat: Add initialization error handling and refactor Inject. - feat: Add initialization error handling and refactor Inject.
- feat: After injecting a dependency, the lazzy loading getter will be replaced with the dependency itself. - feat: After injecting a dependency, the lazzy loading getter will be replaced with the dependency itself.
- feat: remove the use of a private property to store the injected dependencies. Now the dependencies are stored in the property itself. - feat: remove the use of a private property to store the injected dependencies. Now the dependencies are stored in the property itself.
- test: Add tests for the new features. - test: Add tests for the new features.
### Deprecated ### Deprecated
- Deprecated the old version format `0.0.0`. - Deprecated the old version format `0.0.0`.
### Removed ### Removed
### Fixed ### Fixed
### Security ### Security
--- ---
[unreleased]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.14...HEAD [unreleased]: https://github.com/20Max01/TSinjex/compare/v1.0.0...HEAD
[0.0.14]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.13...v0.0.14 [1.1.0]: https://github.com/20Max01/TSinjex/compare/v1.1.0...v1.2.0
[0.2.00]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.14...v0.2.0 [1.1.0]: https://github.com/20Max01/TSinjex/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/20Max01/TSinjex/compare/v0.4.0...v1.0.0
[0.4.0]: https://github.com/20Max01/TSinjex/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/20Max01/TSinjex/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/20Max01/TSinjex/compare/v0.0.14...v0.2.0
[0.0.14]: https://github.com/20Max01/TSinjex/compare/v0.0.13...v0.0.14

View File

@@ -24,21 +24,27 @@ const argv = yargs
description: 'File pattern to search for (e.g., .ts, .js)', description: 'File pattern to search for (e.g., .ts, .js)',
default: '.ts', default: '.ts',
}) })
.option('without-extension', {
alias: 'x',
type: 'boolean',
description: 'Omit file extension in import paths',
default: false,
})
.help() .help()
.argv; .argv;
// Fixed RegEx patterns for decorator detection // Fixed RegEx patterns for decorator detection
const SEARCH_PATTERNS = [ const SEARCH_PATTERNS = [
/^@Register(?:<(.+)?>)?\(\s*["']{1}(.+)?["']{1}\s*,?\s*(true|false)?\s*\)/m, // Matches @Register(...) /^@Register(?:<(.+)?>)?\(\s*["']{1}(.+)?["']{1}\s*,?\s*(true|false)?\s*\)/m,
/^@RegisterInstance(?:<(.+)?>)?\(\s*["']{1}(.+)?["']{1}\s*,?\s*(.+)?\s*\)/m, // Matches @RegisterInstance(...) /^@RegisterInstance(?:<(.+)?>)?\(\s*["']{1}(.+)?["']{1}\s*,?\s*(.+)?\s*\)/m,
]; ];
const FILE_PATTERN = argv.pattern.startsWith('.') ? argv.pattern : `.${argv.pattern}`; // Ensure the pattern starts with a dot const FILE_PATTERN = argv.pattern.startsWith('.') ? argv.pattern : `.${argv.pattern}`;
/** /**
* Recursively searches for all files in a directory matching the specified pattern. * Recursively collects all files with a specific extension.
* @param {string} dirPath - The directory to search. * @param {string} dirPath - Root directory
* @returns {string[]} - List of matching files. * @returns {string[]} List of file paths
*/ */
function getAllFiles(dirPath) { function getAllFiles(dirPath) {
let files = fs.readdirSync(dirPath); let files = fs.readdirSync(dirPath);
@@ -57,9 +63,9 @@ function getAllFiles(dirPath) {
} }
/** /**
* Filters files that contain at least one of the specified regex patterns. * Checks files for decorator usage.
* @param {string[]} files - List of files to check. * @param {string[]} files
* @returns {string[]} - Files that contain at least one of the specified patterns. * @returns {string[]} Filtered files
*/ */
function findFilesWithPattern(files) { function findFilesWithPattern(files) {
return files.filter((file) => { return files.filter((file) => {
@@ -69,16 +75,21 @@ function findFilesWithPattern(files) {
} }
/** /**
* Generates an import file containing imports for all found files. * Generates ES-style import statements from file paths.
* @param {string[]} files - List of relevant files. * @param {string[]} files
* @returns {string} - Generated import code. * @returns {string}
*/ */
function generateImports(files) { function generateImports(files) {
return files.map((file) => `import '${file.replace(/\\/g, '/')}';`).join('\n') + '\n'; return files.map((file) => {
const relative = './' + path.relative(argv.src, file).replace(/\\/g, '/');
const noExt = relative.replace(FILE_PATTERN, '');
const finalPath = argv['without-extension'] ? noExt : `${noExt}${FILE_PATTERN}`;
return `import '${finalPath}';`;
}).join('\n') + '\n';
} }
/** /**
* Main function that executes the script. * Script entry point.
*/ */
function main() { function main() {
try { try {

View File

@@ -2,11 +2,17 @@ module.exports = {
setupFilesAfterEnv: ['./scripts/jest.setup.js'], setupFilesAfterEnv: ['./scripts/jest.setup.js'],
preset: 'ts-jest', preset: 'ts-jest',
testEnvironment: 'node', testEnvironment: 'node',
extensionsToTreatAsEsm: ['.ts'],
transform: {
'^.+\\.ts$': ['ts-jest', { useESM: true }],
},
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'], testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'],
testPathIgnorePatterns: ['\\.spec\\.ts$', '\\.performance\\.test\\.ts$'], testPathIgnorePatterns: ['\\.spec\\.ts$', '\\.performance\\.test\\.ts$'],
moduleDirectories: ['node_modules', 'src'], moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: { moduleNameMapper: {
'^src/(.*)\\.js$': '<rootDir>/src/$1',
'^src/(.*)$': '<rootDir>/src/$1', '^src/(.*)$': '<rootDir>/src/$1',
'^(\\.{1,2}/.*)\\.js$': '$1',
}, },
collectCoverage: true, collectCoverage: true,
coverageDirectory: '.locale/coverage', coverageDirectory: '.locale/coverage',
@@ -19,4 +25,4 @@ module.exports = {
statements: 70, statements: 70,
}, },
}, },
}; };

View File

@@ -1,23 +1,32 @@
module.exports = { module.exports = {
setupFilesAfterEnv: ['./scripts/jest.setup.js'], setupFilesAfterEnv: ['./scripts/jest.setup.js'],
preset: 'ts-jest',
testEnvironment: 'node', testEnvironment: 'node',
transform: {
'^.+\\.ts$': ['ts-jest', { useESM: true }],
},
extensionsToTreatAsEsm: ['.ts'],
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'], testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'],
testPathIgnorePatterns: ['\\.spec\\.ts$', '\\.performance\\.test\\.ts$'], testPathIgnorePatterns: ['\\.spec\\.ts$', '\\.performance\\.test\\.ts$'],
moduleDirectories: ['node_modules', 'src'], moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: { moduleNameMapper: {
'^src/(.*)\\.js$': '<rootDir>/src/$1',
'^src/(.*)$': '<rootDir>/src/$1', '^src/(.*)$': '<rootDir>/src/$1',
'^(\\.{1,2}/.*)\\.js$': '$1',
}, },
collectCoverage: true, collectCoverage: true,
coverageDirectory: '.locale/coverage', coverageDirectory: '.locale/coverage',
coverageReporters: ['text', ['lcov', { projectRoot: '..' }], 'json-summary'], coverageReporters: [
'text',
['lcov', { projectRoot: '..' }],
'json-summary',
],
collectCoverageFrom: [ collectCoverageFrom: [
'src/**/*.{ts,tsx}', 'src/**/*.{ts,tsx}',
'!src/**/*.d.ts', '!src/**/*.d.ts',
'!src/**/*.performance.test.ts', '!src/**/*.performance.test.ts',
'!src/**/*.spec.ts', '!src/**/*.spec.ts',
'!src/**/*.test.ts', '!src/**/*.test.ts',
'!src/auto-imports.ts' '!src/auto-imports.ts',
], ],
coverageThreshold: { coverageThreshold: {
global: { global: {

7
package-lock.json generated
View File

@@ -1,17 +1,20 @@
{ {
"name": "ts-injex", "name": "ts-injex",
"version": "0.0.9", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ts-injex", "name": "ts-injex",
"version": "0.0.9", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"eslint-plugin-prettier": "^5.2.1", "eslint-plugin-prettier": "^5.2.1",
"jest-environment-jsdom": "^29.7.0" "jest-environment-jsdom": "^29.7.0"
}, },
"bin": {
"tsinjex-generate": "bin/generate-imports.cjs"
},
"devDependencies": { "devDependencies": {
"@stylistic/eslint-plugin": "^2.6.2", "@stylistic/eslint-plugin": "^2.6.2",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",

View File

@@ -1,6 +1,6 @@
{ {
"name": "ts-injex", "name": "ts-injex",
"version": "0.2.0", "version": "1.2.0",
"description": "Simple boilerplate code free dependency injection system for TypeScript.", "description": "Simple boilerplate code free dependency injection system for TypeScript.",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
@@ -63,4 +63,4 @@
"LICENSE", "LICENSE",
"package.json" "package.json"
] ]
} }

View File

@@ -1,9 +1,9 @@
/* istanbul ignore file */ /* istanbul ignore file */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Inject } from 'src/decorators/Inject'; import { Inject } from '../decorators/Inject.js';
import { DependencyResolutionError } from 'src/interfaces/Exceptions'; import { DependencyResolutionError } from '../interfaces/Exceptions.js';
import { ForceConstructor } from 'src/types/GenericContructor'; import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex.js';
import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex'; import { ForceConstructor } from '../types/GenericContructor.js';
/** /**
* Test the Inject decorator. * Test the Inject decorator.
@@ -282,6 +282,7 @@ export function test_RegisterInstanceDecorator(
Container: ITSinjex_, Container: ITSinjex_,
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
registerInstance: Function, registerInstance: Function,
mode: 'instance' | 'standalone' = 'standalone',
): void { ): void {
describe('RegisterInstance Decorator Tests', () => { describe('RegisterInstance Decorator Tests', () => {
let container: ITSinjex; let container: ITSinjex;
@@ -295,7 +296,10 @@ export function test_RegisterInstanceDecorator(
}); });
it('should register an instance of a dependency', () => { it('should register an instance of a dependency', () => {
@registerInstance('InstanceIdentifier') @registerInstance(
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass { class TestClass {
private readonly _dependency!: any; private readonly _dependency!: any;
@@ -337,7 +341,10 @@ export function test_RegisterInstanceDecorator(
}); });
it('should register an instance of a dependency and get it on set', () => { it('should register an instance of a dependency and get it on set', () => {
@registerInstance('InstanceIdentifier') @registerInstance(
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass { class TestClass {
private readonly _dependency!: any; private readonly _dependency!: any;

View File

@@ -1,15 +1,18 @@
import { TSinjex } from 'src/classes/TSinjex'; /* eslint-disable deprecation/deprecation */
import { Inject } from 'src/decorators/Inject'; import { TSinjex } from 'src/classes/TSinjex.js';
import { Register } from 'src/decorators/Register'; import { Inject } from 'src/decorators/Inject.js';
import { RegisterInstance } from 'src/decorators/RegisterInstance'; import { Register } from 'src/decorators/Register.js';
import { RegisterInstance } from 'src/decorators/RegisterInstance.js';
import { import {
test_InjectDecorator, test_InjectDecorator,
test_RegisterDecorator, test_RegisterDecorator,
test_RegisterInstanceDecorator, test_RegisterInstanceDecorator,
} from './Decorators.spec'; } from './Decorators.spec.js';
test_InjectDecorator(TSinjex, Inject); test_InjectDecorator(TSinjex, Inject);
test_RegisterDecorator(TSinjex, Register); test_RegisterDecorator(TSinjex, Register);
test_RegisterInstanceDecorator(TSinjex, RegisterInstance); test_RegisterInstanceDecorator(TSinjex, RegisterInstance);
test_RegisterInstanceDecorator(TSinjex, Register, 'instance');

View File

@@ -1,6 +1,11 @@
/* istanbul ignore file */ /* istanbul ignore file */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { ITSinjex, ITSinjex_ } from 'src/interfaces/ITSinjex'; import {
DependencyResolutionError,
InitializationError,
NoInstantiationMethodError,
} from '../interfaces/Exceptions.js';
import { ITSinjex, ITSinjex_ } from '../interfaces/ITSinjex.js';
export function test_RegisterFunction( export function test_RegisterFunction(
Container: ITSinjex_, Container: ITSinjex_,
@@ -69,3 +74,111 @@ export function test_ResolveFunction(
}); });
}); });
} }
/**
* Test the inject function.
* @param Container The DI container implementation to test against.
* @param inject The inject function to test.
*/
export function test_injectFunction(
Container: ITSinjex_,
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
inject: Function,
): void {
describe('inject Function Tests', () => {
let container: ITSinjex;
beforeEach(() => {
// Reset singleton
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_instance'] = undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(Container as any)['_dependencies'] = undefined;
container = Container.getInstance();
});
it('should resolve and return the dependency as is', () => {
container.register('SimpleDep', { value: 'test' });
const resolved = inject('SimpleDep');
expect(resolved.value).toBe('test');
});
it('should resolve and run the initializer function', () => {
container.register('DepWithInit', { value: 'before' });
const resolved = inject('DepWithInit', (dep: any) => {
dep.value = 'after';
return dep;
});
expect(resolved.value).toBe('after');
});
it('should resolve and instantiate the dependency if init is true and constructor exists', () => {
class WithConstructor {
value = 'constructed';
}
container.register('Constructable', WithConstructor);
const resolved = inject('Constructable', true);
expect(resolved.value).toBe('constructed');
});
it('should return undefined if dependency is not found and not necessary', () => {
const resolved = inject('NonExistentDep', undefined, false);
expect(resolved).toBeUndefined();
});
it('should throw DependencyResolutionError if dependency is not found and necessary', () => {
expect(() => inject('MissingDep')).toThrow(
DependencyResolutionError,
);
});
it('should throw InitializationError if init function throws', () => {
container.register('InitThrows', {});
expect(() =>
inject('InitThrows', () => {
throw new Error('fail');
}),
).toThrow(InitializationError);
});
it('should throw NoInstantiationMethodError if init = true and no constructor exists', () => {
container.register('NonConstructable', {});
expect(() => inject('NonConstructable', true)).toThrow(
NoInstantiationMethodError,
);
});
it('should not throw if no constructor and necessary = false', () => {
container.register('SafeSkip', {});
expect(() => inject('SafeSkip', true, false)).not.toThrow();
});
it('should return undefined if initializer fails and not necessary', () => {
container.register('InitErrorOptional', {});
const result = inject(
'InitErrorOptional',
() => {
throw new Error('ignored');
},
false,
);
expect(result).toBeUndefined();
});
it('should return undefined if dependency is null and not necessary', () => {
container.register('NullDep', null);
const result = inject('NullDep', true, false);
expect(result).toBeUndefined();
});
});
}

View File

@@ -1,7 +1,13 @@
import { TSinjex } from 'src/classes/TSinjex'; import {
import { register } from 'src/functions/register'; test_injectFunction,
import { resolve } from 'src/functions/resolve'; test_RegisterFunction,
import { test_RegisterFunction, test_ResolveFunction } from './Functions.spec'; test_ResolveFunction,
} from './Functions.spec.js';
import { TSinjex } from '../classes/TSinjex.js';
import { inject } from '../functions/inject.js';
import { register } from '../functions/register.js';
import { resolve } from '../functions/resolve.js';
test_RegisterFunction(TSinjex, register); test_RegisterFunction(TSinjex, register);
test_ResolveFunction(TSinjex, resolve); test_ResolveFunction(TSinjex, resolve);
test_injectFunction(TSinjex, inject);

View File

@@ -1,5 +1,5 @@
/* istanbul ignore file */ /* istanbul ignore file */
import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex'; import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex.js';
/** /**
* Test the implementation of the `ITSinjex` interface. * Test the implementation of the `ITSinjex` interface.

View File

@@ -1,4 +1,4 @@
import { test_ITSinjex } from './ITSinjex.spec'; import { test_ITSinjex } from './ITSinjex.spec.js';
import { TSinjex } from '../classes/TSinjex'; import { TSinjex } from '../classes/TSinjex.js';
test_ITSinjex(TSinjex); test_ITSinjex(TSinjex);

View File

@@ -1,13 +1,13 @@
import type { Inject } from '../decorators/Inject'; import type { Inject } from '../decorators/Inject.js';
import type { Register } from '../decorators/Register'; import type { Register } from '../decorators/Register.js';
import type { RegisterInstance } from '../decorators/RegisterInstance'; import type { RegisterInstance } from '../decorators/RegisterInstance.js';
import type { register } from '../functions/register'; import type { register } from '../functions/register.js';
import type { resolve } from '../functions/resolve'; import type { resolve } from '../functions/resolve.js';
import { ImplementsStatic } from '../helper/ImplementsStatic'; import { ImplementsStatic } from '../helper/ImplementsStatic.js';
import { DependencyResolutionError } from '../interfaces/Exceptions'; import { DependencyResolutionError } from '../interfaces/Exceptions.js';
import { IDependency } from '../interfaces/IDependency'; import { IDependency } from '../interfaces/IDependency.js';
import { ITSinjex, ITSinjex_ } from '../interfaces/ITSinjex'; import { ITSinjex, ITSinjex_ } from '../interfaces/ITSinjex.js';
import { Identifier } from '../types/Identifier'; import { Identifier } from '../types/Identifier.js';
/** /**
* # TSinjex * # TSinjex

View File

@@ -1,12 +1,12 @@
import { TSinjex } from '../classes/TSinjex.js';
import { import {
DependencyResolutionError, DependencyResolutionError,
InitializationError, InitializationError,
InjectorError, InjectorError,
NoInstantiationMethodError, NoInstantiationMethodError,
} from 'src/interfaces/Exceptions'; } from '../interfaces/Exceptions.js';
import { TSinjex } from '../classes/TSinjex'; import { Identifier } from '../types/Identifier.js';
import { Identifier } from '../types/Identifier'; import { InitDelegate } from '../types/InitDelegate.js';
import { InitDelegate } from '../types/InitDelegate';
/** /**
* A decorator to inject a dependency from a DI (Dependency Injection) container into a class property. * A decorator to inject a dependency from a DI (Dependency Injection) container into a class property.
@@ -17,7 +17,7 @@ import { InitDelegate } from '../types/InitDelegate';
* @param init Optional an initializer function to transform the dependency before injection * @param init Optional an initializer function to transform the dependency before injection
* or true to instantiate the dependency if it has a constructor. * or true to instantiate the dependency if it has a constructor.
* @see {@link InitDelegate} for more information on initializer functions. * @see {@link InitDelegate} for more information on initializer functions.
* @param necessary If true, throws an error if the dependency is not found. * @param isNecessary If true, throws an error if the dependency is not found.
* @returns The resolved dependency or undefined if the dependency is not necessary * @returns The resolved dependency or undefined if the dependency is not necessary
* and not found, or throws an error if the dependency is necessary and not found. * and not found, or throws an error if the dependency is necessary and not found.
* @throws **Only throws errors if the dependency is necessary.** * @throws **Only throws errors if the dependency is necessary.**
@@ -43,7 +43,7 @@ import { InitDelegate } from '../types/InitDelegate';
export function Inject<T, U>( export function Inject<T, U>(
identifier: Identifier, identifier: Identifier,
init?: InitDelegate<T, U> | true, init?: InitDelegate<T, U> | true,
necessary = true, isNecessary = true,
) { ) {
return function (target: unknown, propertyKey: string | symbol): void { return function (target: unknown, propertyKey: string | symbol): void {
/** /**
@@ -52,7 +52,7 @@ export function Inject<T, U>(
* @returns The resolved dependency or undefined if the dependency is not found. * @returns The resolved dependency or undefined if the dependency is not found.
*/ */
const resolve = (): T | undefined => { const resolve = (): T | undefined => {
return TSinjex.getInstance().resolve<T>(identifier, necessary); return TSinjex.getInstance().resolve<T>(identifier, isNecessary);
}; };
Object.defineProperty(target, propertyKey, { Object.defineProperty(target, propertyKey, {
@@ -61,7 +61,7 @@ export function Inject<T, U>(
const dependency: T | undefined = tryAndCatch( const dependency: T | undefined = tryAndCatch(
() => resolve(), () => resolve(),
necessary, isNecessary,
identifier, identifier,
DependencyResolutionError, DependencyResolutionError,
); );
@@ -78,13 +78,13 @@ export function Inject<T, U>(
else if (initFunction != null) else if (initFunction != null)
instance = tryAndCatch( instance = tryAndCatch(
initFunction, initFunction,
necessary, isNecessary,
identifier, identifier,
InitializationError, InitializationError,
); );
else if (necessary) else if (isNecessary)
throw new NoInstantiationMethodError(identifier); throw new NoInstantiationMethodError(identifier);
} else if (necessary) } else if (isNecessary)
throw new DependencyResolutionError(identifier); throw new DependencyResolutionError(identifier);
/** /**

View File

@@ -1,5 +1,133 @@
import { TSinjex } from '../classes/TSinjex'; import { InitDelegate } from 'src/types/InitDelegate.js';
import { Identifier } from '../types/Identifier'; import { TSinjex } from '../classes/TSinjex.js';
import { Identifier } from '../types/Identifier.js';
//#region Overloads
/**
* A decorator to register a class in the **TSinjex** DI (Dependency Injection) container.
* @template TargetType The type of the class to be registered.
* @param identifier The identifier used to register the class in the DI container.
* @see {@link Identifier} for more information on identifiers.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@Register('MyClassIdentifier')
* class MyClass {
* // ...
* }
* ```
* @example
* ```ts
* \@Register('MyClassIdentifier', true)
* class MyClass {
* // ...
* }
* ```
*/
export function Register<
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
deprecated?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void;
/**
* A decorator to register an instance of a class in the DI (Dependency Injection) container.
* @template TargetType The type of the class whose instance is to be registered.
* @param identifier The identifier used to register the instance in the DI container.
* @see {@link Identifier} for more information on identifiers.
* @param shouldRegister Set to 'instance' to register the instance in the DI container
* with an empty constructor.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', 'instance')
* class MyClass {
* // ...
* }
* ```
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', 'instance', true)
* class MyClass {
* // ...
* }
* ```
*/
export function Register<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
shouldRegister: 'instance',
deprecated?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void;
/**
* A decorator to register an instance of a class in the DI (Dependency Injection) container.
* @template TargetType The type of the class whose instance is to be registered.
* @param identifier The identifier used to register the instance in the DI container.
* @see {@link Identifier} for more information on identifiers.
* @param init An optional initializer function which get the constructor of the class
* as input and returns an instance of the class.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @see {@link InitDelegate} for more information on initializer functions.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor())
* class MyClass {
* // ...
* }
* ```
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor(), true)
* class MyClass {
* // ...
* }
* ```
*/
export function Register<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
init?: InitDelegate<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType>
>,
deprecated?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void;
//#endregion Overloads
// eslint-disable-next-line jsdoc/require-jsdoc
export function Register<
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
arg1?:
| undefined
| boolean
| InitDelegate<TargetType, InstanceType<TargetType>>
| 'instance',
arg2?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void {
const deprecated = typeof arg1 === 'boolean' ? arg1 : arg2;
const init = typeof arg1 === 'function' ? arg1 : undefined;
const shouldRegisterInstance = arg1 === 'instance';
if (init == undefined && shouldRegisterInstance !== true) {
return _register(identifier, deprecated);
} else {
return _registerInstance(identifier, init, deprecated);
}
}
/** /**
* A decorator to register a class in the **TSinjex** DI (Dependency Injection) container. * A decorator to register a class in the **TSinjex** DI (Dependency Injection) container.
@@ -17,7 +145,7 @@ import { Identifier } from '../types/Identifier';
* } * }
* ``` * ```
*/ */
export function Register< function _register<
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>, TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
>(identifier: Identifier, deprecated?: boolean) { >(identifier: Identifier, deprecated?: boolean) {
return function (constructor: TargetType, ...args: unknown[]): void { return function (constructor: TargetType, ...args: unknown[]): void {
@@ -28,3 +156,111 @@ export function Register<
diContainer.register(identifier, constructor, deprecated); diContainer.register(identifier, constructor, deprecated);
}; };
} }
/**
* A decorator to register an instance of a class in the DI (Dependency Injection) container.
* @template TargetType The type of the class whose instance is to be registered.
* @param identifier The identifier used to register the instance in the DI container.
* @see {@link Identifier} for more information on identifiers.
* @param init An optional initializer function which get the constructor of the class
* as input and returns an instance of the class.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @see {@link InitDelegate} for more information on initializer functions.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor())
* class MyClass {
* // ...
* }
* ```
*/
function _registerInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
init?: InitDelegate<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType>
>,
deprecated?: boolean,
) {
return function (constructor: TargetType, ...args: unknown[]): void {
// Get the instance of the DI container
const diContainer = TSinjex.getInstance();
let instance: InstanceType<TargetType>;
// Create a proxy to instantiate the class when needed (Lazy Initialization)
let lazyProxy: unknown = new Proxy(
{},
{
get(target, prop, receiver) {
({ instance, lazyProxy } = initializeInstance<TargetType>(
instance,
init,
constructor,
args,
lazyProxy,
));
// Return the requested property of the instance
return instance[prop as keyof InstanceType<TargetType>];
},
set(target, prop, value, receiver) {
({ instance, lazyProxy } = initializeInstance<TargetType>(
instance,
init,
constructor,
args,
lazyProxy,
));
// Set the requested property of the instance
return (instance[prop as keyof InstanceType<TargetType>] =
value);
},
},
);
// Register the lazy proxy in the DI container
diContainer.register(identifier, lazyProxy, deprecated);
};
}
/**
* Initializes the instance of the class.
* @template TargetType The type of the class whose instance is to be initialized.
* @param instance The instance of the class to be initialized.
* @param init The optional initializer function to initialize the instance.
* @param constructor The constructor of the class.
* @param args The arguments to be passed to the constructor of the class.
* @param lazyProxy The lazy proxy to instantiate the class when needed.
* @returns The initialized instance and the lazy proxy.
*/
function initializeInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
instance: InstanceType<TargetType>,
init:
| InitDelegate<
TargetType &
(new (..._args: unknown[]) => InstanceType<TargetType>),
InstanceType<TargetType>
>
| undefined,
constructor: TargetType,
args: unknown[],
lazyProxy: unknown,
): { instance: InstanceType<TargetType>; lazyProxy: unknown } {
if (instance == null) {
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
return { instance, lazyProxy };
}

View File

@@ -1,6 +1,6 @@
import { TSinjex } from '../classes/TSinjex'; import { Register } from './Register.js';
import { Identifier } from '../types/Identifier'; import { Identifier } from '../types/Identifier.js';
import { InitDelegate } from '../types/InitDelegate'; import { InitDelegate } from '../types/InitDelegate.js';
/** /**
* A decorator to register an instance of a class in the DI (Dependency Injection) container. * A decorator to register an instance of a class in the DI (Dependency Injection) container.
@@ -9,6 +9,8 @@ import { InitDelegate } from '../types/InitDelegate';
* @see {@link Identifier} for more information on identifiers. * @see {@link Identifier} for more information on identifiers.
* @param init An optional initializer function which get the constructor of the class * @param init An optional initializer function which get the constructor of the class
* as input and returns an instance of the class. * as input and returns an instance of the class.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @see {@link InitDelegate} for more information on initializer functions. * @see {@link InitDelegate} for more information on initializer functions.
* @returns The decorator function to be applied on the class. * @returns The decorator function to be applied on the class.
* @example * @example
@@ -18,6 +20,7 @@ import { InitDelegate } from '../types/InitDelegate';
* // ... * // ...
* } * }
* ``` * ```
* @deprecated Use {@link Register} instead. This decorator already uses the {@link Register} decorator internally.
*/ */
export function RegisterInstance< export function RegisterInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>, TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
@@ -27,47 +30,10 @@ export function RegisterInstance<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> }, TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType> InstanceType<TargetType>
>, >,
) { deprecated?: boolean,
return function (constructor: TargetType, ...args: unknown[]): void { ): (constructor: TargetType, ...args: unknown[]) => void {
// Get the instance of the DI container const initDelegate = typeof init === 'function' ? init : undefined;
const diContainer = TSinjex.getInstance();
let instance: InstanceType<TargetType>;
// Create a proxy to instantiate the class when needed (Lazy Initialization) if (initDelegate) return Register(identifier, initDelegate, deprecated);
let lazyProxy: unknown = new Proxy( else return Register(identifier, 'instance', deprecated);
{},
{
get(target, prop, receiver) {
if (instance == null) {
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
// Return the requested property of the instance
return instance[prop as keyof InstanceType<TargetType>];
},
set(target, prop, value, receiver) {
if (instance == null) {
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
// Set the requested property of the instance
return (instance[prop as keyof InstanceType<TargetType>] =
value);
},
},
);
// Register the lazy proxy in the DI container
diContainer.register(identifier, lazyProxy);
};
} }

220
src/functions/inject.ts Normal file
View File

@@ -0,0 +1,220 @@
import { TSinjex } from '../classes/TSinjex.js';
import {
DependencyResolutionError,
InitializationError,
InjectorError,
NoInstantiationMethodError,
} from '../interfaces/Exceptions.js';
import { Identifier } from '../types/Identifier.js';
import { InitDelegate } from '../types/InitDelegate.js';
/**
* Resolves a dependency by its identifier without initialization or instantiation.
* @template T The expected type of the dependency.
* @param identifier The identifier used to resolve the dependency from the container.
* @returns The resolved dependency.
* @throws A {@link DependencyResolutionError} if the dependency is not found.
* @example
* ```ts
* const logger = inject<Logger>('Logger');
* ```
*/
export function inject<T>(identifier: Identifier): T;
/**
* Resolves and instantiates a dependency using its constructor.
* @template T The expected class type of the dependency.
* @param identifier The identifier used to resolve the dependency from the container.
* @param shouldInit Set to `true` to instantiate the dependency after resolution.
* @returns The resolved and instantiated dependency.
* @throws A {@link DependencyResolutionError} if the dependency is not found.
* @throws A {@link NoInstantiationMethodError} if the dependency has no constructor.
* @throws An {@link InitializationError} if instantiation fails.
* @example
* ```ts
* const instance = inject<Service>('Service', true);
* ```
*/
export function inject<T>(identifier: Identifier, shouldInit: true): T;
/**
* Resolves and instantiates a dependency using its constructor, optionally failing silently.
* @template T The expected class type of the dependency.
* @param identifier The identifier used to resolve the dependency from the container.
* @param shouldInit Set to `true` to instantiate the dependency.
* @param isNecessary If `false`, resolution or instantiation errors return `undefined` instead of throwing.
* @returns The resolved and instantiated dependency, or `undefined` if resolution or instantiation fails.
* @example
* ```ts
* const instance = inject<Service>('OptionalService', true, false);
* if (instance) instance.doSomething();
* ```
*/
export function inject<T>(
identifier: Identifier,
shouldInit: true,
isNecessary: false,
): T | undefined;
/**
* Resolves a dependency without instantiating it, optionally failing silently.
* @template T The expected type of the dependency.
* @param identifier The identifier used to resolve the dependency from the container.
* @param shouldInit Set to `false` to skip instantiation.
* @param isNecessary If `false`, resolution errors return `undefined` instead of throwing.
* @returns The resolved dependency, or `undefined` if not found.
* @example
* ```ts
* const config = inject<Config>('Config', false, false) ?? getDefaultConfig();
* ```
*/
export function inject<T>(
identifier: Identifier,
shouldInit: false,
isNecessary: false,
): T | undefined;
/**
* Resolves a dependency and applies a custom initializer function to transform the result.
* @template T The original dependency type.
* @template U The final return type after initialization.
* @param identifier The identifier used to resolve the dependency.
* @param init A function to transform or initialize the dependency.
* @returns The transformed dependency.
* @throws A {@link DependencyResolutionError} if the dependency is not found.
* @throws An {@link InitializationError} if the initializer throws.
* @example
* ```ts
* const client = inject<Api>('Api', (api) => api.getClient());
* ```
*/
export function inject<T, U>(
identifier: Identifier,
init: InitDelegate<T, U>,
): U;
/**
* Resolves a dependency and applies a custom initializer function, optionally failing silently.
* @template T The original dependency type.
* @template U The final return type after initialization.
* @param identifier The identifier used to resolve the dependency.
* @param init A function to transform or initialize the dependency.
* @param isNecessary If `false`, resolution or initializer errors return `undefined` instead of throwing.
* @returns The transformed dependency, or `undefined` if resolution or initialization fails.
* @example
* ```ts
* const db = inject<Database, Pool>('Database', (d) => d.getPool(), false);
* if (db) db.query('SELECT * FROM users');
* ```
*/
export function inject<T, U>(
identifier: Identifier,
init: InitDelegate<T, U>,
isNecessary: false,
): U | undefined;
/**
* A function to inject a dependency from a DI (Dependency Injection) container into a variable.
* This is the actual implementation that handles all overload variants.
* @template T The original dependency type.
* @template U The final return type after optional initialization or transformation.
* @param identifier The identifier used to resolve the dependency.
* @see {@link Identifier} for more information on identifiers.
* @param init Optional: either `true` to instantiate via constructor, `false` to skip, or a function to transform the dependency.
* @see {@link InitDelegate} for more information on initializer functions.
* @param isNecessary If `true`, throws on failure; if `false`, returns `undefined` on resolution or initialization errors.
* @returns The resolved dependency or result of initialization, or `undefined` if not necessary and resolution fails.
* @throws A {@link DependencyResolutionError} if the dependency is not found (and necessary).
* @throws A {@link NoInstantiationMethodError} if instantiation is requested but no constructor exists.
* @throws An {@link InitializationError} if the initializer throws an error.
* @throws A {@link InjectorError} for unknown errors during resolution or transformation.
* @example
* ```ts
* const service = inject<Service>('Service');
* ```
* @example
* ```ts
* const instance = inject<Service>('Service', true);
* ```
* @example
* ```ts
* const logger = inject<ILogger>('ILogger_', (x) => x.getLogger('Module'), false);
* ```
*/
export function inject<T, U>(
identifier: Identifier,
init?: InitDelegate<T, U> | true | false,
isNecessary = true,
): T | U | undefined {
let instance: T | U | undefined;
const dependency: T | undefined = tryAndCatch(
() => TSinjex.getInstance().resolve<T>(identifier, isNecessary),
isNecessary,
identifier,
DependencyResolutionError,
);
if (dependency != null) {
const initFunction: (() => U) | undefined =
typeof init === 'function' && dependency != null
? (): U => init(dependency)
: init === true && hasConstructor(dependency)
? (): U => new dependency() as U
: undefined;
if (init == null || init === false) instance = dependency;
else if (initFunction != null)
instance = tryAndCatch(
initFunction,
isNecessary,
identifier,
InitializationError,
);
else if (isNecessary) throw new NoInstantiationMethodError(identifier);
} else if (isNecessary) throw new DependencyResolutionError(identifier);
return instance as T | U;
}
/**
* Tries to execute a function and catches any errors that occur.
* If the function is necessary and an error occurs, it throws the error
* with the specified error class and identifier.
* @param fn The function to execute.
* @param necessary If true, throws an error if an error occurs.
* @param identifier The identifier of the dependency.
* @param errorClass The error class to throw if an error occurs.
* @returns The result of the function or undefined if an error occurs and the function is not necessary.
*/
function tryAndCatch<ReturnType, ErrorType>(
fn: () => ReturnType,
necessary: boolean,
identifier?: Identifier,
errorClass?: ErrorType,
): ReturnType | undefined {
try {
return fn();
} catch (error) {
if (necessary)
throw new (errorClass != null ? errorClass : error)(
identifier ?? 'not specified',
error,
);
else return undefined;
}
}
/**
* Checks if an object has a constructor.
* @param obj The object to check.
* @returns True if the object has a constructor, false otherwise.
*/
function hasConstructor<T>(obj: T): obj is T & { new (): unknown } {
const _obj = obj as unknown as { prototype?: { constructor?: unknown } };
return (
_obj?.prototype != null &&
typeof _obj.prototype.constructor === 'function'
);
}

View File

@@ -1,5 +1,5 @@
import { TSinjex } from '../classes/TSinjex'; import { TSinjex } from '../classes/TSinjex.js';
import { Identifier } from '../types/Identifier'; import { Identifier } from '../types/Identifier.js';
/** /**
* Register a dependency. * Register a dependency.

View File

@@ -1,6 +1,6 @@
import { TSinjex } from '../classes/TSinjex'; import { TSinjex } from '../classes/TSinjex.js';
import { DependencyResolutionError } from '../interfaces/Exceptions'; import { DependencyResolutionError } from '../interfaces/Exceptions.js';
import { Identifier } from '../types/Identifier'; import { Identifier } from '../types/Identifier.js';
/** /**
* Resolve a dependency. * Resolve a dependency.
@@ -15,12 +15,12 @@ export function resolve<T>(identifier: Identifier): T;
* Resolve a dependency * Resolve a dependency
* @param identifier The identifier used to register the class in the DI container. * @param identifier The identifier used to register the class in the DI container.
* @see {@link Identifier} for more information on identifiers. * @see {@link Identifier} for more information on identifiers.
* @param necessary The dependency is **not** necessary. * @param isNecessary The dependency is **not** necessary.
* @returns The resolved dependency or undefined if the dependency is not found. * @returns The resolved dependency or undefined if the dependency is not found.
*/ */
export function resolve<T>( export function resolve<T>(
identifier: Identifier, identifier: Identifier,
necessary: false, isNecessary: false,
): T | undefined; ): T | undefined;
/** /**

View File

@@ -1,18 +1,22 @@
/* istanbul ignore file */ /* istanbul ignore file */
// Main // Main
export * from './classes/TSinjex'; export * from './classes/TSinjex.js';
// Decorators // Decorators
export * from './decorators/Inject'; export * from './decorators/Inject.js';
export * from './decorators/Register'; export * from './decorators/Register.js';
export * from './decorators/RegisterInstance'; export * from './decorators/RegisterInstance.js';
// Helper
export * from './helper/ImplementsStatic.js';
// Functions // Functions
export * from './functions/resolve'; export * from './functions/resolve.js';
export * from './functions/register'; export * from './functions/register.js';
export * from './functions/inject.js';
// Interfaces & Types // Interfaces & Types
export type * from './interfaces/ITSinjex'; export type * from './interfaces/ITSinjex.js';
export type * from './types/InitDelegate'; export type * from './types/InitDelegate.js';
export type * from './types/GenericContructor'; export type * from './types/GenericContructor.js';

View File

@@ -1,5 +1,5 @@
import { Identifier } from 'src/types/Identifier'; import { Identifier } from 'src/types/Identifier.js';
import { ITSinjex } from './ITSinjex'; import { ITSinjex } from './ITSinjex.js';
/** /**
* General error class for {@link ITSinjex} interface. * General error class for {@link ITSinjex} interface.

View File

@@ -1,5 +1,5 @@
import { DependencyResolutionError } from './Exceptions'; import { DependencyResolutionError } from './Exceptions.js';
import { Identifier } from '../types/Identifier'; import { Identifier } from '../types/Identifier.js';
/** /**
* Static TSInjex Interface * Static TSInjex Interface

View File

@@ -7,13 +7,13 @@
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"outDir": "./dist", "outDir": "./dist",
"module": "ESNext", "module": "NodeNext",
"target": "ES6", "target": "ES2020",
"allowJs": true, "allowJs": true,
"noUnusedLocals": false, "noUnusedLocals": false,
"noUnusedParameters": false, "noUnusedParameters": false,
"noImplicitAny": true, "noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "NodeNext",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"importHelpers": true, "importHelpers": true,
"isolatedModules": true, "isolatedModules": true,