Compare commits
12 Commits
v1.1.0
...
feature/in
Author | SHA1 | Date | |
---|---|---|---|
a041ce0e6d | |||
1425503021 | |||
bc4e56388a | |||
b03f272d88 | |||
9a7d2bb0c3 | |||
af099a25d6 | |||
ac139cad43 | |||
97bf2be745 | |||
816a6d128a | |||
405b7ff99a | |||
3563f9c590 | |||
c23ac1de98 |
133
CHANGELOG.md
133
CHANGELOG.md
@@ -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
|
@@ -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 {
|
||||
|
@@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
@@ -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
7
package-lock.json
generated
@@ -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",
|
||||
|
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -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.
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
/**
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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'
|
||||
);
|
||||
}
|
@@ -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.
|
||||
|
@@ -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;
|
||||
|
||||
/**
|
||||
|
22
src/index.ts
22
src/index.ts
@@ -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';
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user