Compare commits
2 Commits
v0.3.0
...
feature/es
Author | SHA1 | Date | |
---|---|---|---|
b6ce0ec52d | |||
319f364daf |
36
.github/workflows/CreateRelease.yml
vendored
36
.github/workflows/CreateRelease.yml
vendored
@@ -4,7 +4,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'dev/*'
|
||||
paths:
|
||||
- 'package.json'
|
||||
workflow_dispatch: # Allows manual execution of the workflow.
|
||||
@@ -23,8 +22,14 @@ jobs:
|
||||
with:
|
||||
node-version: '20.8.0'
|
||||
|
||||
- name: Install dependencies, run tests and build
|
||||
run: npm run prepare:deploy
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run Tests
|
||||
run: npm run test:verbose
|
||||
|
||||
- name: Build the Project
|
||||
run: npm run build:tsc
|
||||
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
@@ -72,9 +77,8 @@ jobs:
|
||||
echo "Generating release notes from ${{ env.PREVIOUS_TAG }} to HEAD..."
|
||||
repo_url=$(git config --get remote.origin.url)
|
||||
notes=$(git log ${{ env.PREVIOUS_TAG }}..HEAD --pretty=format:"- [\`%h\`]($repo_url/commit/%H): %s%n")
|
||||
echo "See [CHANGELOG.md](./CHANGELOG.md) for more details."
|
||||
echo "Release notes:"
|
||||
echo "$notes"
|
||||
echo ""
|
||||
echo "### Changes in this release" > release_notes.md
|
||||
echo "$notes" >> release_notes.md
|
||||
shell: bash
|
||||
@@ -86,6 +90,17 @@ jobs:
|
||||
git config --local user.email "actions@github.com"
|
||||
shell: bash
|
||||
|
||||
# - name: Create temporary branch
|
||||
# id: create_temp_branch
|
||||
# if: steps.check_version.outputs.skip_release == 'false'
|
||||
# run: |
|
||||
# git checkout --orphan release/v${{ env.VERSION }}
|
||||
# git reset
|
||||
# rm -f .gitignore
|
||||
# git add README.md package.json LICENSE dist/ src/ tsconfig.json
|
||||
# git commit -m "Prepare files for release ${{ env.VERSION }}"
|
||||
# shell: bash
|
||||
|
||||
- name: Create and push tag
|
||||
id: create_tag
|
||||
if: steps.check_version.outputs.skip_release == 'false'
|
||||
@@ -96,15 +111,6 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
|
||||
- name: Set Release Prerelease Flag
|
||||
id: set_prerelease_flag
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
|
||||
echo "PRE_RELEASE=false" >> $GITHUB_ENV
|
||||
elif [[ "${{ github.ref }}" == refs/heads/dev/* ]]; then
|
||||
echo "PRE_RELEASE=true" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Release
|
||||
if: steps.check_version.outputs.skip_release == 'false'
|
||||
uses: softprops/action-gh-release@v2
|
||||
@@ -112,6 +118,6 @@ jobs:
|
||||
tag_name: ${{ env.VERSION }}
|
||||
name: Release ${{ env.VERSION }}
|
||||
body_path: release_notes.md
|
||||
prerelease: ${{ env.PRE_RELEASE }}
|
||||
prerelease: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
2
.github/workflows/ValidateBranchName.yml
vendored
2
.github/workflows/ValidateBranchName.yml
vendored
@@ -3,7 +3,7 @@ name: Validate Branch Name on Pull Request
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- UNDEFINED
|
||||
- main
|
||||
|
||||
jobs:
|
||||
validate-branch-name-on-pull-request:
|
||||
|
44
CHANGELOG.md
44
CHANGELOG.md
@@ -9,43 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
### 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]
|
||||
|
||||
### 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.
|
||||
- feat: Add a full description of `Identifiers` as JSDoc comment.
|
||||
This establishes the definition of what an `Identifiers` is.
|
||||
|
||||
|
||||
### Deprecated
|
||||
@@ -59,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Security
|
||||
|
||||
|
||||
## [0.0.14]
|
||||
|
||||
### Added
|
||||
@@ -90,6 +56,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
---
|
||||
|
||||
[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
|
||||
[0.0.14]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.13...v0.0.14
|
@@ -1,111 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const yargs = require('yargs');
|
||||
|
||||
// CLI argument parsing
|
||||
const argv = yargs
|
||||
.option('src', {
|
||||
alias: 's',
|
||||
type: 'string',
|
||||
description: 'Directory to search for files',
|
||||
default: 'src',
|
||||
})
|
||||
.option('output', {
|
||||
alias: 'o',
|
||||
type: 'string',
|
||||
description: 'Path to the output file',
|
||||
default: 'src/auto-imports.ts',
|
||||
})
|
||||
.option('pattern', {
|
||||
alias: 'p',
|
||||
type: 'string',
|
||||
description: 'File pattern to search for (e.g., .ts, .js)',
|
||||
default: '.ts',
|
||||
})
|
||||
.help()
|
||||
.argv;
|
||||
|
||||
// Fixed RegEx patterns for decorator detection
|
||||
const SEARCH_PATTERNS = [
|
||||
/^@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}`; // Ensure the pattern starts with a dot
|
||||
|
||||
/**
|
||||
* 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);
|
||||
let arrayOfFiles = [];
|
||||
|
||||
files.forEach((file) => {
|
||||
const fullPath = path.join(dirPath, file);
|
||||
if (fs.statSync(fullPath).isDirectory()) {
|
||||
arrayOfFiles = arrayOfFiles.concat(getAllFiles(fullPath));
|
||||
} else if (file.endsWith(FILE_PATTERN)) {
|
||||
arrayOfFiles.push(fullPath);
|
||||
}
|
||||
});
|
||||
|
||||
return arrayOfFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) => {
|
||||
const content = fs.readFileSync(file, 'utf8');
|
||||
return SEARCH_PATTERNS.some((pattern) => pattern.test(content));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) => `import '${file.replace(/\\/g, '/')}';`).join('\n') + '\n';
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function that executes the script.
|
||||
*/
|
||||
function main() {
|
||||
try {
|
||||
const srcDir = path.resolve(process.cwd(), argv.src);
|
||||
const outputFile = path.resolve(process.cwd(), argv.output);
|
||||
|
||||
if (!fs.existsSync(srcDir)) {
|
||||
console.error(`❌ Error: The directory '${srcDir}' does not exist.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const allFiles = getAllFiles(srcDir);
|
||||
const filesWithPattern = findFilesWithPattern(allFiles);
|
||||
|
||||
if (filesWithPattern.length === 0) {
|
||||
console.log(`ℹ️ No ${FILE_PATTERN} files found matching the specified decorator patterns.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const importContent = generateImports(filesWithPattern);
|
||||
fs.writeFileSync(outputFile, importContent);
|
||||
|
||||
console.log(`✅ Imports successfully generated: ${outputFile}`);
|
||||
} catch (error) {
|
||||
console.error('❌ An error occurred:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
10
package.json
10
package.json
@@ -1,13 +1,10 @@
|
||||
{
|
||||
"name": "ts-injex",
|
||||
"version": "0.3.0",
|
||||
"version": "0.1.0",
|
||||
"description": "Simple boilerplate code free dependency injection system for TypeScript.",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"bin": {
|
||||
"tsinjex-generate": "./bin/generate-imports.cjs"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "npm run build",
|
||||
"build": "npm run build:tsc",
|
||||
@@ -25,8 +22,7 @@
|
||||
"docs:fix:coverage": "node scripts/fix-coverage-paths.cjs",
|
||||
"docs:generate:badge": "node scripts/generate-badge.cjs",
|
||||
"docs:fix:escape": "node scripts/replace-doc-escaping.cjs",
|
||||
"version:show": "node -e \"console.log(require('./package.json').version)\"",
|
||||
"prepare:deploy": "npm install && npm run test:verbose && npm run build:tsc"
|
||||
"version:show": "node -e \"console.log(require('./package.json').version)\""
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -63,4 +59,4 @@
|
||||
"LICENSE",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
}
|
@@ -282,7 +282,6 @@ export function test_RegisterInstanceDecorator(
|
||||
Container: ITSinjex_,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
registerInstance: Function,
|
||||
mode: 'instance' | 'standalone' = 'standalone',
|
||||
): void {
|
||||
describe('RegisterInstance Decorator Tests', () => {
|
||||
let container: ITSinjex;
|
||||
@@ -296,10 +295,7 @@ export function test_RegisterInstanceDecorator(
|
||||
});
|
||||
|
||||
it('should register an instance of a dependency', () => {
|
||||
@registerInstance(
|
||||
'InstanceIdentifier',
|
||||
mode === 'instance' ? 'instance' : undefined,
|
||||
)
|
||||
@registerInstance('InstanceIdentifier')
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
@@ -341,10 +337,7 @@ export function test_RegisterInstanceDecorator(
|
||||
});
|
||||
|
||||
it('should register an instance of a dependency and get it on set', () => {
|
||||
@registerInstance(
|
||||
'InstanceIdentifier',
|
||||
mode === 'instance' ? 'instance' : undefined,
|
||||
)
|
||||
@registerInstance('InstanceIdentifier')
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable deprecation/deprecation */
|
||||
import { TSinjex } from 'src/classes/TSinjex';
|
||||
import { Inject } from 'src/decorators/Inject';
|
||||
import { Register } from 'src/decorators/Register';
|
||||
@@ -14,5 +13,3 @@ test_InjectDecorator(TSinjex, Inject);
|
||||
test_RegisterDecorator(TSinjex, Register);
|
||||
|
||||
test_RegisterInstanceDecorator(TSinjex, RegisterInstance);
|
||||
|
||||
test_RegisterInstanceDecorator(TSinjex, Register, 'instance');
|
||||
|
@@ -1,134 +1,6 @@
|
||||
import { InitDelegate } from 'src/types/InitDelegate';
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
import { Identifier } from '../types/Identifier';
|
||||
|
||||
//#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.
|
||||
* @template TargetType The type of the class to be registered.
|
||||
@@ -145,7 +17,7 @@ export function Register<
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
function _register<
|
||||
export function Register<
|
||||
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
|
||||
>(identifier: Identifier, deprecated?: boolean) {
|
||||
return function (constructor: TargetType, ...args: unknown[]): void {
|
||||
@@ -156,111 +28,3 @@ function _register<
|
||||
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 };
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Register } from './Register';
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
import { Identifier } from '../types/Identifier';
|
||||
import { InitDelegate } from '../types/InitDelegate';
|
||||
|
||||
@@ -9,8 +9,6 @@ import { InitDelegate } from '../types/InitDelegate';
|
||||
* @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
|
||||
@@ -20,7 +18,6 @@ import { InitDelegate } from '../types/InitDelegate';
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
* @deprecated Use {@link Register} instead. This decorator already uses the {@link Register} decorator internally.
|
||||
*/
|
||||
export function RegisterInstance<
|
||||
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
|
||||
@@ -30,10 +27,47 @@ export function RegisterInstance<
|
||||
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
|
||||
InstanceType<TargetType>
|
||||
>,
|
||||
deprecated?: boolean,
|
||||
): (constructor: TargetType, ...args: unknown[]) => void {
|
||||
const initDelegate = typeof init === 'function' ? init : undefined;
|
||||
) {
|
||||
return function (constructor: TargetType, ...args: unknown[]): void {
|
||||
// Get the instance of the DI container
|
||||
const diContainer = TSinjex.getInstance();
|
||||
let instance: InstanceType<TargetType>;
|
||||
|
||||
if (initDelegate) return Register(identifier, initDelegate, deprecated);
|
||||
else return Register(identifier, 'instance', deprecated);
|
||||
// Create a proxy to instantiate the class when needed (Lazy Initialization)
|
||||
let lazyProxy: unknown = new Proxy(
|
||||
{},
|
||||
{
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
@@ -1,11 +1,35 @@
|
||||
/**
|
||||
* The dependency identifier.
|
||||
* You can use any string as identifier.
|
||||
* To create order, it is also possible to
|
||||
* provide these with a separator: `GroupA.ClassZ`.
|
||||
* The convection for naming is as follows:
|
||||
* The name should generally correspond to the interface that is relevant.
|
||||
* I.e. a class `ClassA` that implements the interface `IClassA` and is
|
||||
* registered as a dependent class is registered under the interface name `IClassA`.
|
||||
* ## The dependency identifier
|
||||
*
|
||||
* You can use any string or Symbol as identifier. The identifier is used to
|
||||
* register a class in the TSinjex DI (Dependency Injection) container.
|
||||
* See **Hierarchical identifiers** for more information on `.` in identifiers.
|
||||
*
|
||||
* ### Hierarchical identifiers
|
||||
* To create hierarchical identifiers, you can use a dot `.` as a separator.
|
||||
* E.g. `Parent.Child` or `Parent.Child.Grandchild`.
|
||||
*
|
||||
* ### Merge functionality
|
||||
* You can merge multiple dependencies into one by resolving them with
|
||||
* a identifier that is a prefix of the actual identifiers.
|
||||
* E.g. `Parent` resolves `Parent.*` and returns a mixin of all children.
|
||||
* Merging will only work, if the main identifier is not registered.
|
||||
* Grandchildren are not included in the mixin but you can use something like
|
||||
* `Parent.Child.Grandchild` to merge all direct children of Grandchild,
|
||||
* e.g. `Parent.Child.Grandchild.*`.
|
||||
* If you don't want to allow merging, you must register the dependencies
|
||||
* with the full identifier. E.g. `Parent` or `Parent.Child.Grandchild`.
|
||||
*
|
||||
* ### Naming convention
|
||||
* The name of a dependency should generally correspond to the interface that is implemented.
|
||||
* E.g. a class `ClassA` that implements the interface `IClassA` and is registered
|
||||
* as a dependent class is registered under the interface name `IClassA`.
|
||||
*
|
||||
* The mixin parts of the class are registered under the interface name `IClassA.*`.
|
||||
* E.g. `IClassA.IClassZ`, `IClassA.IClassY`, etc.
|
||||
*
|
||||
* #### Static/Constructor Identifier naming
|
||||
* The identifier for a static or constructor dependency should be the same as the class name
|
||||
* postfixed with `_`. E.g. `IClassA_` for the the static or constructor interface of class `IClassA`.
|
||||
*/
|
||||
export type Identifier = string | symbol;
|
||||
|
Reference in New Issue
Block a user