Compare commits

..

12 Commits

Author SHA1 Message Date
a041ce0e6d docs: Reflect changes to changelog und push version 2025-03-14 13:40:25 +01:00
1425503021 Merge branch 'feature/integrate-register-instance-in-register' of https://github.com/20Max01/TSinjex into feature/integrate-register-instance-in-register 2025-03-14 13:38:21 +01:00
bc4e56388a docs: Reflect changes to changelog 2025-03-14 13:38:11 +01:00
b03f272d88 refactor: add region tags for overloads in Register.ts 2025-03-14 13:38:11 +01:00
9a7d2bb0c3 docs: Reflect changes to changelog 2025-03-14 13:38:10 +01:00
af099a25d6 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.
2025-03-14 13:37:38 +01:00
ac139cad43 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.
2025-03-14 13:37:38 +01:00
97bf2be745 docs: Reflect changes to changelog 2025-03-12 09:35:43 +01:00
816a6d128a refactor: add region tags for overloads in Register.ts 2025-03-12 09:34:53 +01:00
405b7ff99a docs: Reflect changes to changelog 2025-03-11 17:46:00 +01:00
3563f9c590 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.
2025-03-11 17:44:03 +01:00
c23ac1de98 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.
2025-03-11 17:43:32 +01:00
23 changed files with 129 additions and 447 deletions

View File

@@ -9,140 +9,87 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
### Deprecated
### Removed
### Fixed
### Security
## [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
- 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]
### Added
- 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.
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.
The CLI can be executed via `npx tsinjex-generate` or added as a script in `package.json` for easier integration.
- 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.
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.
The CLI can be executed via `npx tsinjex-generate` or added as a script in `package.json` for easier integration.
### Deprecated
### Removed
### Fixed
### Security
## [0.0.14]
### Added
- 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).
- 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.
- feat: Add new Error `InitializationError` to reflect errors during initialization of a dependency.
- 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: 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.
- 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).
- 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.
- feat: Add new Error `InitializationError` to reflect errors during initialization of a dependency.
- 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: 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.
### Deprecated
- Deprecated the old version format `0.0.0`.
- Deprecated the old version format `0.0.0`.
### Removed
### Fixed
### Security
---
[unreleased]: https://github.com/20Max01/TSinjex/compare/v1.0.0...HEAD
[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
[unreleased]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.14...HEAD
[0.0.14]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.13...v0.0.14
[0.2.00]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.14...v0.2.0
[0.3.00]: https://github.com/PxaMMaxP/TSinjex/compare/0.2.0...v0.3.0

View File

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

View File

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

View File

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

7
package-lock.json generated
View File

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

View File

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

View File

@@ -1,9 +1,9 @@
/* istanbul ignore file */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Inject } from '../decorators/Inject.js';
import { DependencyResolutionError } from '../interfaces/Exceptions.js';
import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex.js';
import { ForceConstructor } from '../types/GenericContructor.js';
import { Inject } from 'src/decorators/Inject';
import { DependencyResolutionError } from 'src/interfaces/Exceptions';
import { ForceConstructor } from 'src/types/GenericContructor';
import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex';
/**
* Test the Inject decorator.

View File

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

View File

@@ -1,11 +1,6 @@
/* istanbul ignore file */
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
DependencyResolutionError,
InitializationError,
NoInstantiationMethodError,
} from '../interfaces/Exceptions.js';
import { ITSinjex, ITSinjex_ } from '../interfaces/ITSinjex.js';
import { ITSinjex, ITSinjex_ } from 'src/interfaces/ITSinjex';
export function test_RegisterFunction(
Container: ITSinjex_,
@@ -74,111 +69,3 @@ 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,13 +1,7 @@
import {
test_injectFunction,
test_RegisterFunction,
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';
import { TSinjex } from 'src/classes/TSinjex';
import { register } from 'src/functions/register';
import { resolve } from 'src/functions/resolve';
import { test_RegisterFunction, test_ResolveFunction } from './Functions.spec';
test_RegisterFunction(TSinjex, register);
test_ResolveFunction(TSinjex, resolve);
test_injectFunction(TSinjex, inject);

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import { InitDelegate } from 'src/types/InitDelegate.js';
import { TSinjex } from '../classes/TSinjex.js';
import { Identifier } from '../types/Identifier.js';
import { InitDelegate } from 'src/types/InitDelegate';
import { TSinjex } from '../classes/TSinjex';
import { Identifier } from '../types/Identifier';
//#region Overloads

View File

@@ -1,6 +1,6 @@
import { Register } from './Register.js';
import { Identifier } from '../types/Identifier.js';
import { InitDelegate } from '../types/InitDelegate.js';
import { Register } from './Register';
import { Identifier } from '../types/Identifier';
import { InitDelegate } from '../types/InitDelegate';
/**
* A decorator to register an instance of a class in the DI (Dependency Injection) container.

View File

@@ -1,113 +0,0 @@
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';
/**
* A function to inject a dependency from a DI (Dependency Injection) container into a variable.
* @template T The type of the dependency to be injected.
* @template U The type of the property to be injected.
* @param identifier The identifier used to resolve the class in the DI container.
* @see {@link Identifier} for more information on identifiers.
* @param init Optional an initializer function to transform the dependency before injection
* or true to instantiate the dependency if it has a constructor.
* @see {@link InitDelegate} for more information on initializer functions.
* @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
* 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 A {@link DependencyResolutionError} if the dependency is not found.
* @throws A {@link InjectorError} if an error occurs during the injection process.
* @throws A {@link NoInstantiationMethodError} if the dependency does not have a constructor.
* @throws An {@link InitializationError} if an error occurs during the initialization process.
* @example
* ```ts
* let myDependency = inject<MyDependency>('MyDependencyIdentifier');
* ```
* @example
* ```ts
* let logger = inject<ILogger>('ILogger_', (x: ILogger_) => x.getLogger('Tags'), false);
* ```
*/
export function inject<T, U>(
identifier: Identifier,
init?: InitDelegate<T, U> | true,
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) 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.js';
import { Identifier } from '../types/Identifier.js';
import { TSinjex } from '../classes/TSinjex';
import { Identifier } from '../types/Identifier';
/**
* Register a dependency.

View File

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

View File

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

View File

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

View File

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

View File

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