Compare commits

..

2 Commits

Author SHA1 Message Date
7feb0fc694 Add manual trigger to ValidateBranchName workflow
- Included `workflow_dispatch` event in GitHub Actions
- Allows for manual execution of the ValidateBranchName workflow
2024-08-23 21:29:14 +02:00
ea2383234f Add branch name validation configuration
- **Added** `.github/config.conf` for branch name prefixes configuration
- **Updated** `ValidateBranchName.yml` to read and utilize branch name prefixes from the config file
2024-08-23 21:28:22 +02:00
12 changed files with 113 additions and 463 deletions

2
.github/config.conf vendored Normal file
View File

@@ -0,0 +1,2 @@
[branch_validation]
valid_prefixes = feature/,fix/,refactoring/,testing/,dependabot/,gh-pages

View File

@@ -4,7 +4,6 @@ on:
push: push:
branches: branches:
- main - main
- 'dev/*'
paths: paths:
- 'package.json' - 'package.json'
workflow_dispatch: # Allows manual execution of the workflow. workflow_dispatch: # Allows manual execution of the workflow.
@@ -23,8 +22,14 @@ jobs:
with: with:
node-version: '20.8.0' node-version: '20.8.0'
- name: Install dependencies, run tests and build - name: Install Dependencies
run: npm run prepare:deploy run: npm install
- name: Run Tests
run: npm run test:verbose
- name: Build the Project
run: npm run build:tsc
- name: Get the version - name: Get the version
id: get_version id: get_version
@@ -72,9 +77,8 @@ jobs:
echo "Generating release notes from ${{ env.PREVIOUS_TAG }} to HEAD..." echo "Generating release notes from ${{ env.PREVIOUS_TAG }} to HEAD..."
repo_url=$(git config --get remote.origin.url) repo_url=$(git config --get remote.origin.url)
notes=$(git log ${{ env.PREVIOUS_TAG }}..HEAD --pretty=format:"- [\`%h\`]($repo_url/commit/%H): %s%n") 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 "$notes"
echo ""
echo "### Changes in this release" > release_notes.md echo "### Changes in this release" > release_notes.md
echo "$notes" >> release_notes.md echo "$notes" >> release_notes.md
shell: bash shell: bash
@@ -86,6 +90,17 @@ jobs:
git config --local user.email "actions@github.com" git config --local user.email "actions@github.com"
shell: bash 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 - name: Create and push tag
id: create_tag id: create_tag
if: steps.check_version.outputs.skip_release == 'false' if: steps.check_version.outputs.skip_release == 'false'
@@ -96,15 +111,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash 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 - name: Release
if: steps.check_version.outputs.skip_release == 'false' if: steps.check_version.outputs.skip_release == 'false'
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
@@ -112,6 +118,6 @@ jobs:
tag_name: ${{ env.VERSION }} tag_name: ${{ env.VERSION }}
name: Release ${{ env.VERSION }} name: Release ${{ env.VERSION }}
body_path: release_notes.md body_path: release_notes.md
prerelease: ${{ env.PRE_RELEASE }} prerelease: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -3,7 +3,8 @@ name: Validate Branch Name on Pull Request
on: on:
pull_request: pull_request:
branches: branches:
- UNDEFINED - main
workflow_dispatch: # Allows manual execution of the workflow.
jobs: jobs:
validate-branch-name-on-pull-request: validate-branch-name-on-pull-request:
@@ -13,11 +14,27 @@ jobs:
- name: Check out repository - name: Check out repository
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Read Config File
id: config
run: |
VALID_PREFIXES=$(grep 'valid_prefixes' .github/config.conf | cut -d '=' -f2 | tr -d ' ')
echo "VALID_PREFIXES=$VALID_PREFIXES" >> $GITHUB_ENV
- name: Validate Branch Name on Pull Request - name: Validate Branch Name on Pull Request
run: | run: |
BRANCH_NAME=${GITHUB_HEAD_REF} BRANCH_NAME=${{ github.head_ref }}
if [[ ! "$BRANCH_NAME" =~ ^(feature\/|fix\/|refactoring\/|testing\/|dependabot\/|gh-pages) ]]; then VALID_PREFIXES_ARRAY=(${VALID_PREFIXES//,/ })
VALID=false
for PREFIX in "${VALID_PREFIXES_ARRAY[@]}"; do
if [[ "$BRANCH_NAME" =~ ^$PREFIX ]]; then
VALID=true
break
fi
done
if [ "$VALID" = false ]; then
echo "Invalid branch name: $BRANCH_NAME" echo "Invalid branch name: $BRANCH_NAME"
echo "Branch name must start with 'feature/', 'fix/', 'refactoring/', 'testing/', dependabot/" or "gh-pages" echo "Branch name must start with one of the following prefixes: $VALID_PREFIXES"
exit 1 exit 1
fi fi

View File

@@ -9,90 +9,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
### Deprecated
### Removed
### Fixed
### Security
## [0.4.0]
### Added
- feat: Export ImplementsStatic helper function
### Deprecated ### 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.
### Deprecated
### Removed ### Removed
### Fixed ### Fixed
### Security ### Security
## [0.0.14] ## [0.0.14]
### Added ### Added
- Added **ChangeLog** file and format it according to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Added **ChangeLog** file and format it according to [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- Added reference to **Semantic Versioning** in the changelog file. (History will be updated on time). - Added reference to **Semantic Versioning** in the changelog file. (History will be updated on time).
- Version format is now `v0.0.0` instead of `0.0.0`. Changes to this are also reflected in the workflos. - Version format is now `v0.0.0` instead of `0.0.0`. Changes to this are also reflected in the workflos.
- Add `Identifiers` and `Jest` Sections to the `README.md` file. - Add `Identifiers` and `Jest` Sections to the `README.md` file.
- feat: Add new Error `InitializationError` to reflect errors during initialization of a dependency. - feat: Add new Error `InitializationError` to reflect errors during initialization of a dependency.
- feat: Add initialization error handling and refactor Inject. - feat: Add initialization error handling and refactor Inject.
- feat: After injecting a dependency, the lazzy loading getter will be replaced with the dependency itself. - feat: After injecting a dependency, the lazzy loading getter will be replaced with the dependency itself.
- feat: remove the use of a private property to store the injected dependencies. Now the dependencies are stored in the property itself. - feat: remove the use of a private property to store the injected dependencies. Now the dependencies are stored in the property itself.
- test: Add tests for the new features. - test: Add tests for the new features.
### Deprecated ### Deprecated
- Deprecated the old version format `0.0.0`. - Deprecated the old version format `0.0.0`.
### Removed ### Removed
### Fixed ### Fixed
### Security ### Security
--- ---
[unreleased]: https://github.com/20Max01/TSinjex/compare/0.0.14...HEAD [unreleased]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.14...HEAD
[0.0.14]: https://github.com/20Max01/TSinjex/compare/0.0.13...v0.0.14 [0.0.14]: https://github.com/PxaMMaxP/TSinjex/compare/0.0.13...v0.0.14
[0.2.0]: https://github.com/20Max01/TSinjex/compare/v0.0.14...v0.2.0
[0.3.0]: https://github.com/20Max01/TSinjex/compare/v0.2.0...v0.3.0
[0.4.0]: https://github.com/20Max01/TSinjex/compare/v0.3.0...v0.4.0

View File

@@ -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();

7
package-lock.json generated
View File

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

View File

@@ -1,13 +1,10 @@
{ {
"name": "ts-injex", "name": "ts-injex",
"version": "0.4.0", "version": "0.1.0",
"description": "Simple boilerplate code free dependency injection system for TypeScript.", "description": "Simple boilerplate code free dependency injection system for TypeScript.",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
"bin": {
"tsinjex-generate": "./bin/generate-imports.cjs"
},
"scripts": { "scripts": {
"prepare": "npm run build", "prepare": "npm run build",
"build": "npm run build:tsc", "build": "npm run build:tsc",
@@ -25,8 +22,7 @@
"docs:fix:coverage": "node scripts/fix-coverage-paths.cjs", "docs:fix:coverage": "node scripts/fix-coverage-paths.cjs",
"docs:generate:badge": "node scripts/generate-badge.cjs", "docs:generate:badge": "node scripts/generate-badge.cjs",
"docs:fix:escape": "node scripts/replace-doc-escaping.cjs", "docs:fix:escape": "node scripts/replace-doc-escaping.cjs",
"version:show": "node -e \"console.log(require('./package.json').version)\"", "version:show": "node -e \"console.log(require('./package.json').version)\""
"prepare:deploy": "npm install && npm run test:verbose && npm run build:tsc"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@@ -282,7 +282,6 @@ export function test_RegisterInstanceDecorator(
Container: ITSinjex_, Container: ITSinjex_,
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
registerInstance: Function, registerInstance: Function,
mode: 'instance' | 'standalone' = 'standalone',
): void { ): void {
describe('RegisterInstance Decorator Tests', () => { describe('RegisterInstance Decorator Tests', () => {
let container: ITSinjex; let container: ITSinjex;
@@ -296,10 +295,7 @@ export function test_RegisterInstanceDecorator(
}); });
it('should register an instance of a dependency', () => { it('should register an instance of a dependency', () => {
@registerInstance( @registerInstance('InstanceIdentifier')
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass { class TestClass {
private readonly _dependency!: any; 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', () => { it('should register an instance of a dependency and get it on set', () => {
@registerInstance( @registerInstance('InstanceIdentifier')
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass { class TestClass {
private readonly _dependency!: any; private readonly _dependency!: any;

View File

@@ -1,4 +1,3 @@
/* eslint-disable deprecation/deprecation */
import { TSinjex } from 'src/classes/TSinjex'; import { TSinjex } from 'src/classes/TSinjex';
import { Inject } from 'src/decorators/Inject'; import { Inject } from 'src/decorators/Inject';
import { Register } from 'src/decorators/Register'; import { Register } from 'src/decorators/Register';
@@ -14,5 +13,3 @@ test_InjectDecorator(TSinjex, Inject);
test_RegisterDecorator(TSinjex, Register); test_RegisterDecorator(TSinjex, Register);
test_RegisterInstanceDecorator(TSinjex, RegisterInstance); test_RegisterInstanceDecorator(TSinjex, RegisterInstance);
test_RegisterInstanceDecorator(TSinjex, Register, 'instance');

View File

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

View File

@@ -1,4 +1,4 @@
import { Register } from './Register'; import { TSinjex } from '../classes/TSinjex';
import { Identifier } from '../types/Identifier'; import { Identifier } from '../types/Identifier';
import { InitDelegate } from '../types/InitDelegate'; import { InitDelegate } from '../types/InitDelegate';
@@ -9,8 +9,6 @@ import { InitDelegate } from '../types/InitDelegate';
* @see {@link Identifier} for more information on identifiers. * @see {@link Identifier} for more information on identifiers.
* @param init An optional initializer function which get the constructor of the class * @param init An optional initializer function which get the constructor of the class
* as input and returns an instance of the class. * as input and returns an instance of the class.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @see {@link InitDelegate} for more information on initializer functions. * @see {@link InitDelegate} for more information on initializer functions.
* @returns The decorator function to be applied on the class. * @returns The decorator function to be applied on the class.
* @example * @example
@@ -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< export function RegisterInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>, TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
@@ -30,10 +27,47 @@ export function RegisterInstance<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> }, TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType> InstanceType<TargetType>
>, >,
deprecated?: boolean, ) {
): (constructor: TargetType, ...args: unknown[]) => void { return function (constructor: TargetType, ...args: unknown[]): void {
const initDelegate = typeof init === 'function' ? init : undefined; // Get the instance of the DI container
const diContainer = TSinjex.getInstance();
let instance: InstanceType<TargetType>;
if (initDelegate) return Register(identifier, initDelegate, deprecated); // Create a proxy to instantiate the class when needed (Lazy Initialization)
else return Register(identifier, 'instance', deprecated); 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);
};
} }

View File

@@ -8,9 +8,6 @@ export * from './decorators/Inject';
export * from './decorators/Register'; export * from './decorators/Register';
export * from './decorators/RegisterInstance'; export * from './decorators/RegisterInstance';
// Helper
export * from './helper/ImplementsStatic';
// Functions // Functions
export * from './functions/resolve'; export * from './functions/resolve';
export * from './functions/register'; export * from './functions/register';