Compare commits
35 Commits
dev/v1.0.0
...
feature/de
Author | SHA1 | Date | |
---|---|---|---|
c55f39d1e5
|
|||
3ac76b09d3
|
|||
46c9a8b990
|
|||
9e1b7a8d7b
|
|||
3a52f14dcd
|
|||
4fa78afbc2
|
|||
67f587d0d0
|
|||
a70869a941 | |||
7a6253a386 | |||
31de73db00 | |||
688a9d4ee4 | |||
39dbd6d816 | |||
0718ff9d68 | |||
115b3181e0 | |||
c7fa78270c | |||
3401656219 | |||
7e7fd996b3 | |||
a6fabc329b | |||
81fb7f0071 | |||
2e89cdd619 | |||
b157f48261 | |||
bbf68bff34 | |||
b994a074c0 | |||
1da8efff94 | |||
c6e9fbd2a3 | |||
9bd899581f | |||
b43e4ee12f | |||
ef4a2295bf | |||
f4f8c7b78e | |||
beaad4f65e | |||
![]() |
95ef003e99 | ||
9660d77e0c | |||
dce76a5fb3 | |||
23e6248299 | |||
d8fb82943f |
@@ -1,13 +0,0 @@
|
||||
node_modules/
|
||||
|
||||
main.js
|
||||
|
||||
**/*.js
|
||||
*.js
|
||||
|
||||
**/*.mjs
|
||||
*.mjs
|
||||
|
||||
dist/*
|
||||
|
||||
*.cjs
|
299
.eslintrc
299
.eslintrc
@@ -1,299 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"deprecation",
|
||||
"prettier",
|
||||
"import",
|
||||
"jsdoc",
|
||||
"override",
|
||||
"@stylistic"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:jsdoc/recommended-typescript"
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"override/require-override": "error",
|
||||
"override/require-static-override": "off",
|
||||
"prettier/prettier": "warn",
|
||||
"array-callback-return": [
|
||||
"error"
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"caughtErrorsIgnorePattern": "^_",
|
||||
"args": "none"
|
||||
}
|
||||
],
|
||||
"no-warning-comments": [
|
||||
"warn",
|
||||
{
|
||||
"terms": [
|
||||
"@todo"
|
||||
],
|
||||
"location": "anywhere"
|
||||
}
|
||||
],
|
||||
"no-unused-vars": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"no-prototype-builtins": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"deprecation/deprecation": "warn",
|
||||
"no-console": "off",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"warn",
|
||||
{
|
||||
"selector": "classProperty",
|
||||
"modifiers": [
|
||||
"private"
|
||||
],
|
||||
"format": [],
|
||||
"custom": {
|
||||
"regex": "^(_{1,2}I[A-Z][a-zA-Z0-9]*_?|_{1,2}[a-z][a-zA-Z0-9]*)$",
|
||||
"match": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"selector": "classProperty",
|
||||
"modifiers": [
|
||||
"protected"
|
||||
],
|
||||
"format": [],
|
||||
"custom": {
|
||||
"regex": "^(_{1,2}I[A-Z][a-zA-Z0-9]*_?|_{1,2}[a-z][a-zA-Z0-9]*)$",
|
||||
"match": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"selector": [
|
||||
"typeProperty",
|
||||
"classProperty"
|
||||
],
|
||||
"types": [
|
||||
"boolean"
|
||||
],
|
||||
"format": [
|
||||
"PascalCase"
|
||||
],
|
||||
"prefix": [
|
||||
"is",
|
||||
"has",
|
||||
"can",
|
||||
"did",
|
||||
"will",
|
||||
"should"
|
||||
],
|
||||
"leadingUnderscore": "allow"
|
||||
},
|
||||
{
|
||||
"selector": "memberLike",
|
||||
"modifiers": [
|
||||
"public"
|
||||
],
|
||||
"format": [
|
||||
"camelCase"
|
||||
],
|
||||
"leadingUnderscore": "forbid",
|
||||
"filter": {
|
||||
"regex": "^(Events|Styles|Classes|Then)$",
|
||||
"match": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"selector": "typeProperty",
|
||||
"modifiers": [
|
||||
"public"
|
||||
],
|
||||
"format": null,
|
||||
"filter": {
|
||||
"regex": ".*-event$",
|
||||
"match": true
|
||||
},
|
||||
"custom": {
|
||||
"regex": "^[a-z]+(-[a-z]+)*-event$",
|
||||
"match": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"selector": "typeProperty",
|
||||
"modifiers": [
|
||||
"public"
|
||||
],
|
||||
"format": [
|
||||
"camelCase"
|
||||
],
|
||||
"filter": {
|
||||
"regex": ".*-event$|^(Events|Styles|Classes|Then)$",
|
||||
"match": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "off",
|
||||
"@stylistic/padding-line-between-statements": [
|
||||
"warn",
|
||||
{
|
||||
"blankLine": "always",
|
||||
"prev": "*",
|
||||
"next": [
|
||||
"return",
|
||||
"if",
|
||||
"multiline-const",
|
||||
"function",
|
||||
"multiline-expression",
|
||||
"multiline-let",
|
||||
"block-like"
|
||||
]
|
||||
},
|
||||
{
|
||||
"blankLine": "always",
|
||||
"prev": [
|
||||
"function"
|
||||
],
|
||||
"next": "*"
|
||||
}
|
||||
],
|
||||
"import/order": [
|
||||
"warn",
|
||||
{
|
||||
"groups": [
|
||||
[
|
||||
"builtin",
|
||||
"external"
|
||||
],
|
||||
[
|
||||
"internal"
|
||||
],
|
||||
[
|
||||
"parent",
|
||||
"sibling"
|
||||
]
|
||||
],
|
||||
"newlines-between": "never",
|
||||
"alphabetize": {
|
||||
"order": "asc",
|
||||
"caseInsensitive": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"jsdoc/no-undefined-types": [
|
||||
"warn",
|
||||
{
|
||||
"disableReporting": true,
|
||||
"markVariablesAsUsed": true
|
||||
}
|
||||
],
|
||||
"jsdoc/require-jsdoc": [
|
||||
"warn",
|
||||
{
|
||||
"require": {
|
||||
"FunctionDeclaration": true,
|
||||
"MethodDefinition": true,
|
||||
"ClassDeclaration": true,
|
||||
"ArrowFunctionExpression": false,
|
||||
"FunctionExpression": false
|
||||
},
|
||||
"minLineCount": 10
|
||||
}
|
||||
],
|
||||
"jsdoc/require-param": [
|
||||
"warn",
|
||||
{
|
||||
"exemptedBy": [
|
||||
"deprecated",
|
||||
"inheritdoc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"jsdoc/require-description": [
|
||||
"warn",
|
||||
{
|
||||
"contexts": [
|
||||
"FunctionDeclaration",
|
||||
"MethodDefinition",
|
||||
"ClassDeclaration",
|
||||
"ClassExpression"
|
||||
],
|
||||
"descriptionStyle": "body",
|
||||
"exemptedBy": [
|
||||
"deprecated",
|
||||
"inheritdoc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"jsdoc/require-returns": [
|
||||
"warn",
|
||||
{
|
||||
"checkGetters": false,
|
||||
"exemptedBy": [
|
||||
"deprecated",
|
||||
"inheritdoc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"jsdoc/check-tag-names": [
|
||||
"warn",
|
||||
{
|
||||
"definedTags": [
|
||||
"remarks",
|
||||
"jest-environment",
|
||||
"singleton"
|
||||
]
|
||||
}
|
||||
],
|
||||
"jsdoc/check-alignment": "warn",
|
||||
"jsdoc/check-indentation": "warn",
|
||||
"jsdoc/no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
"contexts": [
|
||||
{
|
||||
"context": "MethodDefinition[kind='get']",
|
||||
"comment": "JsdocBlock:has(JsdocTag[tag='returns'])",
|
||||
"message": "JSDoc @returns comments are not allowed in getters."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/prefer-readonly": "warn",
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"warn",
|
||||
{
|
||||
"allowExpressions": true,
|
||||
"allowTypedFunctionExpressions": true,
|
||||
"allowHigherOrderFunctions": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/prefer-string-starts-ends-with": "warn",
|
||||
"@typescript-eslint/no-misused-promises": "warn",
|
||||
"@typescript-eslint/prefer-optional-chain": "warn"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.test.ts",
|
||||
"*.spec.ts"
|
||||
],
|
||||
"rules": {
|
||||
"jsdoc/require-jsdoc": "off",
|
||||
"jsdoc/require-param": "off",
|
||||
"jsdoc/require-description": "off",
|
||||
"jsdoc/require-returns": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@@ -1,12 +0,0 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
open-pull-requests-limit: 10
|
||||
schedule:
|
||||
interval: "weekly"
|
117
.github/workflows/CreateRelease.yml
vendored
117
.github/workflows/CreateRelease.yml
vendored
@@ -1,117 +0,0 @@
|
||||
name: Create Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'dev/*'
|
||||
paths:
|
||||
- 'package.json'
|
||||
workflow_dispatch: # Allows manual execution of the workflow.
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.8.0'
|
||||
|
||||
- name: Install dependencies, run tests and build
|
||||
run: npm run prepare:deploy
|
||||
|
||||
- name: Get the version
|
||||
id: get_version
|
||||
run: |
|
||||
VERSION=$(npm run version:show | tail -n 1)
|
||||
echo "VERSION=v$VERSION" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Get previous release tag
|
||||
id: get_previous_release
|
||||
run: |
|
||||
echo "Fetching previous release tag..."
|
||||
previous_tag=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
||||
if [ -z "$previous_tag" ]; then
|
||||
echo "No previous tag found, using initial commit."
|
||||
previous_tag=$(git rev-list --max-parents=0 HEAD)
|
||||
fi
|
||||
echo "Previous tag: $previous_tag"
|
||||
echo "PREVIOUS_TAG=$previous_tag" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Check if version changed
|
||||
id: check_version
|
||||
run: |
|
||||
# Check if the version already exists as a tag
|
||||
if git rev-parse "refs/tags/${{ env.VERSION }}" >/dev/null 2>&1; then
|
||||
echo "skip_release=true" >> $GITHUB_OUTPUT
|
||||
echo "Version ${{ env.VERSION }} already exists as a tag. No release will be created."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Compare current version with previous tag
|
||||
if [ "${{ env.VERSION }}" == "${{ env.PREVIOUS_TAG }}" ]; then
|
||||
echo "skip_release=true" >> $GITHUB_OUTPUT
|
||||
echo "Version has not changed. No release will be created."
|
||||
exit 0
|
||||
fi
|
||||
echo "skip_release=false" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Generate release notes
|
||||
id: generate_notes
|
||||
if: steps.check_version.outputs.skip_release == 'false'
|
||||
run: |
|
||||
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 "$notes"
|
||||
echo ""
|
||||
echo "### Changes in this release" > release_notes.md
|
||||
echo "$notes" >> release_notes.md
|
||||
shell: bash
|
||||
|
||||
- name: Set Git user
|
||||
if: steps.check_version.outputs.skip_release == 'false'
|
||||
run: |
|
||||
git config --local user.name "GitHub Actions"
|
||||
git config --local user.email "actions@github.com"
|
||||
shell: bash
|
||||
|
||||
- name: Create and push tag
|
||||
id: create_tag
|
||||
if: steps.check_version.outputs.skip_release == 'false'
|
||||
run: |
|
||||
git tag ${{ env.VERSION }}
|
||||
git push origin ${{ env.VERSION }}
|
||||
env:
|
||||
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
|
||||
with:
|
||||
tag_name: ${{ env.VERSION }}
|
||||
name: Release ${{ env.VERSION }}
|
||||
body_path: release_notes.md
|
||||
prerelease: ${{ env.PRE_RELEASE }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
44
.github/workflows/DeployTypeDoc.yml
vendored
44
.github/workflows/DeployTypeDoc.yml
vendored
@@ -1,44 +0,0 @@
|
||||
name: Deploy Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch: # Allows manual execution of the workflow.
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.8.0'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run TypeDoc Generation (TypeDoc, Test Coverage, fixes and badges)
|
||||
run: npm run docs:generate
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git clone --single-branch --branch gh-pages https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} gh-pages
|
||||
rm -rf gh-pages/*
|
||||
cp -r .locale/docs/* gh-pages/
|
||||
mkdir -p gh-pages/coverage
|
||||
cp -r .locale/coverage/* gh-pages/coverage/
|
||||
cd gh-pages
|
||||
git add .
|
||||
git commit -m 'Deploy documentation and coverage'
|
||||
git push origin gh-pages
|
31
.github/workflows/PullRequestTest.yml
vendored
31
.github/workflows/PullRequestTest.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: Run Build and Tests on Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch: # Allows manual execution of the workflow.
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.8.0'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run Tests
|
||||
run: npm run test:verbose
|
||||
|
||||
- name: Build the Project
|
||||
run: npm run build:tsc
|
23
.github/workflows/ValidateBranchName.yml
vendored
23
.github/workflows/ValidateBranchName.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Validate Branch Name on Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- UNDEFINED
|
||||
|
||||
jobs:
|
||||
validate-branch-name-on-pull-request:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Validate Branch Name on Pull Request
|
||||
run: |
|
||||
BRANCH_NAME=${GITHUB_HEAD_REF}
|
||||
if [[ ! "$BRANCH_NAME" =~ ^(feature\/|fix\/|refactoring\/|testing\/|dependabot\/|gh-pages) ]]; then
|
||||
echo "Invalid branch name: $BRANCH_NAME"
|
||||
echo "Branch name must start with 'feature/', 'fix/', 'refactoring/', 'testing/', dependabot/" or "gh-pages"
|
||||
exit 1
|
||||
fi
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,9 +1 @@
|
||||
# Internal
|
||||
.vscode
|
||||
.locale
|
||||
.VSCodeCounter
|
||||
|
||||
*.ignore.*
|
||||
|
||||
dist/*
|
||||
node_modules/*
|
||||
coverage/
|
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": true,
|
||||
"useTabs": false,
|
||||
"tabWidth": 4,
|
||||
"endOfLine": "auto",
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
".prettierrc",
|
||||
".eslintrc"
|
||||
],
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
20
.vscode/launch.json
vendored
Normal file
20
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Deno Tests",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"runtimeExecutable": "/home/maxp/.deno/bin/deno",
|
||||
"runtimeArgs": [
|
||||
"test",
|
||||
"--inspect-brk",
|
||||
"--allow-all",
|
||||
"tests/*.ts"
|
||||
],
|
||||
"attachSimplePort": 9229,
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"editor.tabSize": 4,
|
||||
"editor.insertSpaces": true,
|
||||
"deno.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "denoland.vscode-deno",
|
||||
"editor.detectIndentation": false,
|
||||
"editor.indentSize": "tabSize"
|
||||
}
|
126
CHANGELOG.md
126
CHANGELOG.md
@@ -9,24 +9,116 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- feat: Add new `IdentifierRequiredError` class for missing identifiers.
|
||||
- feat: Add the option to use the decorators without passing the identifier: In this case, the identifier will be the class name (register) or the property name (inject).
|
||||
- Add pre release building to release workflow on dev/* branches an version changes.
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
||||
### Removed
|
||||
|
||||
- feat!: Disable `experimentalDecorators` and `emitDecoratorMetadata` in the `tsconfig.json` file to reflect the change to the stable decorators api.
|
||||
|
||||
### Fixed
|
||||
|
||||
- feat!: Update `Register`, `RegisterInstance` and `Inject` decorators to reflect the change to the stable decorators api.
|
||||
- feat!: Update `Inject` Decorator typing to reflect the correct property type.
|
||||
|
||||
### Security
|
||||
|
||||
## [1.2.0]
|
||||
|
||||
### Added
|
||||
|
||||
- docs: added complete Typedoc-style documentation for all `inject()` overloads
|
||||
Includes parameter descriptions, usage examples, and detailed error annotations for each variant.
|
||||
|
||||
## [1.1.0]
|
||||
|
||||
### Added
|
||||
|
||||
- feat: exported `inject()` function from the public API via `index.ts`
|
||||
The function is now available as part of the main module exports.
|
||||
|
||||
## [1.0.0]
|
||||
|
||||
### Added
|
||||
|
||||
- feat: Enable native ESM support using `"type": "module"` and `moduleResolution: "NodeNext"` in the compiler settings.
|
||||
- feat: All internal imports now explicitly include `.js` extensions for full Node.js ESM compatibility.
|
||||
- feat: Updated `tsconfig.json` to reflect changes for ESM builds (`module: "NodeNext"`, `target: "ES2020"`, etc.).
|
||||
- feat(cli): add `--without-extension` (`-x`) flag to optionally omit file extensions in generated import paths
|
||||
- feat: introduce `inject()` function as a programmatic alternative to the `@Inject` decorator
|
||||
Supports optional initializers and constructor instantiation for resolved dependencies.
|
||||
Designed for cases where decorators are not suitable or dynamic resolution is needed.
|
||||
- test: added comprehensive test suite for `inject()` function, covering resolution, initialization, error cases and instantiation behavior
|
||||
|
||||
### Changed
|
||||
|
||||
- All source files using relative or internal imports were updated to use `.js` extensions to support Node.js ESM runtime resolution.
|
||||
- test: update Jest config for ts-jest ESM compatibility and .js import support
|
||||
- renamed internal parameter `necessary` → `isNecessary` for naming clarity
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed implicit support for CommonJS-style imports without file extensions.
|
||||
|
||||
### Deprecated
|
||||
|
||||
- Support for CommonJS consumers using `require()` is no longer available. Use `import` with an ESM-compatible environment instead.
|
||||
|
||||
### Fixed
|
||||
|
||||
### Security
|
||||
|
||||
### ⚠️ Breaking Changes
|
||||
|
||||
- **BREAKING CHANGE**: This version migrates the entire codebase to native ES modules.
|
||||
- Consumers must use Node.js in ESM mode or compatible bundlers.
|
||||
- Import paths now include `.js` extensions.
|
||||
- Using `require()` (CommonJS) to load this library will no longer work.
|
||||
- All consuming projects must either:
|
||||
- Use `"type": "module"` in their `package.json`, or
|
||||
- Use an ESM-aware bundler (e.g. Webpack, Vite, etc.)
|
||||
|
||||
## [0.4.0]
|
||||
|
||||
### Added
|
||||
|
||||
- feat: Export ImplementsStatic helper function
|
||||
|
||||
### Deprecated
|
||||
|
||||
### Removed
|
||||
|
||||
### Fixed
|
||||
|
||||
### Security
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
### Added
|
||||
|
||||
- refactor: consolidate registration decorators
|
||||
Introduced Register decorator to handle class and instance registration in the DI container.
|
||||
Deprecated RegisterInstance in favor of Register, which now internally handles instance registration.
|
||||
Added support for marking dependencies as deprecated with a warning logged upon first resolution.
|
||||
Updated documentation with examples and notes on deprecation.
|
||||
- tests: add mode parameter to RegisterInstanceDecorator
|
||||
Introduced a mode parameter to the test_RegisterInstanceDecorator function allowing 'instance' or 'standalone' modes.
|
||||
Updated test cases to utilize the new mode parameter when registering an instance.
|
||||
Disabled specific ESLint rule in Decorators.test.ts for deprecation warnings.
|
||||
Added an additional test call to test_RegisterInstanceDecorator with 'instance' mode.
|
||||
- refactor: add region tags for overloads in Register.ts
|
||||
|
||||
## [0.2.0]
|
||||
|
||||
### 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
|
||||
|
||||
### Fixed
|
||||
|
||||
### Security
|
||||
|
||||
## [0.0.14]
|
||||
|
||||
@@ -42,21 +134,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- 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`.
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
### Security
|
||||
|
||||
|
||||
---
|
||||
|
||||
[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
|
||||
[unreleased]: https://github.com/20Max01/TSinjex/compare/v1.0.0...HEAD
|
||||
[1.1.0]: https://github.com/20Max01/TSinjex/compare/v1.1.0...v1.2.0
|
||||
[1.1.0]: https://github.com/20Max01/TSinjex/compare/v1.0.0...v1.1.0
|
||||
[1.0.0]: https://github.com/20Max01/TSinjex/compare/v0.4.0...v1.0.0
|
||||
[0.4.0]: https://github.com/20Max01/TSinjex/compare/v0.3.0...v0.4.0
|
||||
[0.3.0]: https://github.com/20Max01/TSinjex/compare/v0.2.0...v0.3.0
|
||||
[0.2.0]: https://github.com/20Max01/TSinjex/compare/v0.0.14...v0.2.0
|
||||
[0.0.14]: https://github.com/20Max01/TSinjex/compare/v0.0.13...v0.0.14
|
||||
|
34
README.md
34
README.md
@@ -1,34 +0,0 @@
|
||||

|
||||
|
||||
[    ](https://pxammaxp.github.io/TSinjex/coverage/lcov-report/index.html)
|
||||
|
||||
# TSinjex
|
||||
|
||||
## Configuration
|
||||
|
||||
### Identifiers
|
||||
|
||||
Strings and symbols are possible for the **identifiers**.
|
||||
|
||||
### Jest
|
||||
|
||||
For the use of TSinjex with Jest, the corresponding source files can be found under `./src` of the TSinjex node_module folder. To use these files, the `moduleNameMapper` must be configured in the Jest configuration file. The following example shows how to configure the Jest configuration file to use the source files of TSinjex.
|
||||
|
||||
#### Example jest setup
|
||||
|
||||
```ts
|
||||
module.exports = {
|
||||
setupFilesAfterEnv: ['./scripts/jest.setup.js'],
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'],
|
||||
moduleDirectories: ['node_modules', 'src'],
|
||||
moduleNameMapper: {
|
||||
'^src/(.*)$': '<rootDir>/src/$1', // Map src to the source folder
|
||||
'^ts-injex$': '<rootDir>/node_modules/ts-injex/src', // Map ts-injex to the source folder
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!ts-injex)' // **Dont** ignore ts-injex on preset `ts-jest`
|
||||
],
|
||||
};
|
||||
```
|
35
deno.jsonc
Normal file
35
deno.jsonc
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"compilerOptions": {},
|
||||
"lint": {
|
||||
"files": {
|
||||
"include": [
|
||||
"src/"
|
||||
],
|
||||
}
|
||||
},
|
||||
"fmt": {
|
||||
"files": {
|
||||
"include": [
|
||||
"src/",
|
||||
"tests/"
|
||||
],
|
||||
},
|
||||
"options": {
|
||||
"lineWidth": 100,
|
||||
"indentWidth": 4,
|
||||
"useTabs": false
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"include": [
|
||||
"tests/"
|
||||
],
|
||||
},
|
||||
"tasks": {
|
||||
"test": "deno test --coverage tests/*.ts",
|
||||
"lint": "deno lint",
|
||||
"fmt": "deno fmt",
|
||||
"build": "echo 'Kein Build nötig bei Deno'"
|
||||
}
|
||||
}
|
39
deno.lock
generated
Normal file
39
deno.lock
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": "5",
|
||||
"remote": {
|
||||
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
|
||||
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
|
||||
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
|
||||
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
|
||||
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
|
||||
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
|
||||
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
|
||||
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
|
||||
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
|
||||
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
|
||||
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
|
||||
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
|
||||
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
|
||||
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
|
||||
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
|
||||
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
|
||||
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
|
||||
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
|
||||
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
|
||||
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
|
||||
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
|
||||
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
|
||||
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
|
||||
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
|
||||
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
|
||||
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
|
||||
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
|
||||
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
|
||||
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
|
||||
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
|
||||
"https://deno.land/std@0.224.0/testing/asserts.ts": "d0cdbabadc49cc4247a50732ee0df1403fdcd0f95360294ad448ae8c240f3f5c"
|
||||
}
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
module.exports = {
|
||||
setupFilesAfterEnv: ['./scripts/jest.setup.js'],
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'],
|
||||
testPathIgnorePatterns: ['\\.spec\\.ts$', '\\.performance\\.test\\.ts$'],
|
||||
moduleDirectories: ['node_modules', 'src'],
|
||||
moduleNameMapper: {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
},
|
||||
collectCoverage: true,
|
||||
coverageDirectory: '.locale/coverage',
|
||||
coverageReporters: ['text', 'lcov'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 70,
|
||||
functions: 70,
|
||||
lines: 70,
|
||||
statements: 70,
|
||||
},
|
||||
},
|
||||
};
|
@@ -1,30 +0,0 @@
|
||||
module.exports = {
|
||||
setupFilesAfterEnv: ['./scripts/jest.setup.js'],
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/__tests__/**/*.test.ts', '**/?(*.)+(test).ts'],
|
||||
testPathIgnorePatterns: ['\\.spec\\.ts$', '\\.performance\\.test\\.ts$'],
|
||||
moduleDirectories: ['node_modules', 'src'],
|
||||
moduleNameMapper: {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
},
|
||||
collectCoverage: true,
|
||||
coverageDirectory: '.locale/coverage',
|
||||
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'
|
||||
],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 90,
|
||||
functions: 90,
|
||||
lines: 90,
|
||||
statements: 90,
|
||||
},
|
||||
},
|
||||
};
|
7174
package-lock.json
generated
7174
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
63
package.json
63
package.json
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"name": "ts-injex",
|
||||
"version": "0.1.0-alpha",
|
||||
"description": "Simple boilerplate code free dependency injection system for TypeScript.",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"prepare": "npm run build",
|
||||
"build": "npm run build:tsc",
|
||||
"build:tsc": "tsc",
|
||||
"lint": "eslint --ext .ts .",
|
||||
"lint:fix": "eslint --fix --ext .ts .",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch --onlyChanged",
|
||||
"test:file": "jest --watch --onlyChanged --coverage=true --verbose",
|
||||
"test:verbose": "jest --verbose",
|
||||
"test:coverage": "jest --config jest.config.coverage.cjs --coverage",
|
||||
"docs": "typedoc",
|
||||
"docs:generate": "npm run docs && npm run docs:generate:coverage && npm run docs:fix:coverage && npm run docs:generate:badge && npm run docs:fix:escape",
|
||||
"docs:generate:coverage": "npm run test:coverage || exit 0",
|
||||
"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"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PxaMMaxP/TSinjex.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Max P. (@Github: PxaMMaxP)",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.5.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^20.14.11",
|
||||
"@stylistic/eslint-plugin": "^2.6.2",
|
||||
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
||||
"@typescript-eslint/parser": "^8.1.0",
|
||||
"eslint-plugin-deprecation": "^3.0.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsdoc": "^50.2.2",
|
||||
"eslint-plugin-override": "https://github.com/PxaMMaxP/eslint-plugin-override",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.2.3",
|
||||
"typedoc": "^0.26.5",
|
||||
"istanbul-badges-readme": "^1.9.0",
|
||||
"axios": "^1.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"jest-environment-jsdom": "^29.7.0"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"src/**/*",
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
"package.json"
|
||||
]
|
||||
}
|
@@ -1,46 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const coverageDir = path.join(__dirname, '..', '.locale', 'coverage');
|
||||
const typedocUrl = '../../';
|
||||
|
||||
const getAllFiles = (dir, files = []) => {
|
||||
fs.readdirSync(dir).forEach(file => {
|
||||
const fullPath = path.join(dir, file);
|
||||
if (fs.statSync(fullPath).isDirectory()) {
|
||||
getAllFiles(fullPath, files);
|
||||
} else {
|
||||
files.push(fullPath);
|
||||
}
|
||||
});
|
||||
return files;
|
||||
};
|
||||
|
||||
// Alle HTML-Dateien im coverage-Ordner finden
|
||||
const htmlFiles = getAllFiles(coverageDir).filter(file => file.endsWith('.html'));
|
||||
|
||||
// Alle HTML-Dateien bearbeiten
|
||||
htmlFiles.forEach(filePath => {
|
||||
fs.readFile(filePath, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error(`Error reading file ${filePath}:`, err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Relative Pfade anpassen
|
||||
let fixedData = data.replace(/(src|href)="(?!\.)/g, '$1="./');
|
||||
|
||||
// Link zur TypeDoc-Dokumentation hinzufügen
|
||||
const linkHtml = `<div style="position: fixed; bottom: 10px; right: 10px;"><a href="${typedocUrl}">Zur TypeDoc-Dokumentation</a></div>`;
|
||||
fixedData = fixedData.replace('</body>', `${linkHtml}</body>`);
|
||||
|
||||
fs.writeFile(filePath, fixedData, 'utf8', (err) => {
|
||||
if (err) {
|
||||
console.error(`Error writing file ${filePath}:`, err);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Fixed paths and added link in ${filePath}`);
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,55 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { exec } = require('child_process');
|
||||
const axios = require('axios');
|
||||
|
||||
// Step 1: Create README.md in the coverage directory
|
||||
const coverageReadmePath = path.join(__dirname, '..', '.locale', 'coverage', 'README.md');
|
||||
const readmeContent = `
|
||||

|
||||

|
||||

|
||||

|
||||
`;
|
||||
|
||||
fs.writeFileSync(coverageReadmePath, readmeContent, 'utf8');
|
||||
|
||||
// Step 2: Execute the istanbul-badges-readme tool
|
||||
exec('npx istanbul-badges-readme --coverageDir=./.locale/coverage --readmeDir=./.locale/coverage', (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
console.error(`Error executing istanbul-badges-readme: ${stderr}`);
|
||||
return;
|
||||
}
|
||||
console.log('Badges generated successfully.');
|
||||
|
||||
// Step 3: Extract the badge links from README.md
|
||||
const updatedReadmeContent = fs.readFileSync(coverageReadmePath, 'utf8');
|
||||
const badgeLines = updatedReadmeContent.split('\n').filter(line => line.includes('https://img.shields.io'));
|
||||
|
||||
// Ensure the target directory exists
|
||||
const badgesDir = path.join(__dirname, '..', '.locale', 'coverage', 'badges');
|
||||
if (!fs.existsSync(badgesDir)) {
|
||||
fs.mkdirSync(badgesDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Badge types and their order
|
||||
const badgeTypes = ['statements', 'branches', 'functions', 'lines'];
|
||||
|
||||
// Save the badge images
|
||||
badgeLines.forEach(async (line, index) => {
|
||||
const match = line.match(/\((https:\/\/img\.shields\.io\/badge\/[^)]+)\)/);
|
||||
if (match) {
|
||||
const url = match[1];
|
||||
const response = await axios.get(url, { responseType: 'arraybuffer' });
|
||||
const buffer = Buffer.from(response.data, 'binary');
|
||||
const fileName = `badge-${badgeTypes[index]}.svg`;
|
||||
const filePath = path.join(badgesDir, fileName);
|
||||
fs.writeFileSync(filePath, buffer);
|
||||
console.log(`Saved ${fileName}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Step 4: Delete the README.md file
|
||||
fs.unlinkSync(coverageReadmePath);
|
||||
console.log('README.md file deleted.');
|
||||
});
|
@@ -1,17 +0,0 @@
|
||||
// jest.setup.js
|
||||
const getCircularReplacer = () => {
|
||||
const seen = new WeakSet();
|
||||
return (key, value) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.has(value)) {
|
||||
return;
|
||||
}
|
||||
seen.add(value);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
};
|
||||
|
||||
global.customJSONStringify = (object) => {
|
||||
return JSON.stringify(object, getCircularReplacer());
|
||||
};
|
@@ -1,42 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const docsDir = path.join(__dirname, '..', '.locale', 'docs');
|
||||
|
||||
const getAllFiles = (dir, files = []) => {
|
||||
fs.readdirSync(dir).forEach(file => {
|
||||
const fullPath = path.join(dir, file);
|
||||
if (fs.statSync(fullPath).isDirectory()) {
|
||||
getAllFiles(fullPath, files);
|
||||
} else {
|
||||
files.push(fullPath);
|
||||
}
|
||||
});
|
||||
return files;
|
||||
};
|
||||
|
||||
// Alle HTML-Dateien im docs-Ordner finden
|
||||
const htmlFiles = getAllFiles(docsDir).filter(file => file.endsWith('.html'));
|
||||
|
||||
// Alle HTML-Dateien bearbeiten
|
||||
htmlFiles.forEach(filePath => {
|
||||
fs.readFile(filePath, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
console.error(`Error reading file ${filePath}:`, err);
|
||||
return;
|
||||
}
|
||||
|
||||
// `\@` durch `@` ersetzen
|
||||
let fixedData = data.replace(/\\@/g, '@');
|
||||
|
||||
fs.writeFile(filePath, fixedData, 'utf8', (err) => {
|
||||
if (err) {
|
||||
console.error(`Error writing file ${filePath}:`, err);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Fixed escaping in ${filePath}`);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@@ -1,440 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Inject } from 'src/decorators/Inject';
|
||||
import { DependencyResolutionError } from '../interfaces/Exceptions';
|
||||
import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex';
|
||||
import { ForceConstructor } from '../types/GenericContructor';
|
||||
|
||||
/**
|
||||
* Test the Inject decorator.
|
||||
* @param Container The implementation to test.
|
||||
* @param inject The Inject decorator to test.
|
||||
*/
|
||||
export function test_InjectDecorator(
|
||||
Container: ITSinjex_,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
inject: Function,
|
||||
): void {
|
||||
describe('Inject Decorator Tests', () => {
|
||||
let container: ITSinjex;
|
||||
|
||||
beforeEach(() => {
|
||||
// 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 inject dependency when necessary is true', () => {
|
||||
container.register('MockDependencyIdentifier', {
|
||||
value: 'test-value',
|
||||
});
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier')
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
expect(instance.getDependency().value).toBe('test-value');
|
||||
});
|
||||
|
||||
it('should inject dependency and run initializer', () => {
|
||||
container.register('MockDependencyIdentifier', {
|
||||
value: 'test-value',
|
||||
});
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier', (x: string) => {
|
||||
(x as unknown as { value: string }).value =
|
||||
'test-value-init';
|
||||
|
||||
return x;
|
||||
})
|
||||
dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this.dependency;
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
expect(instance.getDependency().value).toBe('test-value-init');
|
||||
});
|
||||
|
||||
it('should inject dependency and run initializer without identifier', () => {
|
||||
container.register('MockDependencyIdentifier', {
|
||||
value: 'test-value',
|
||||
});
|
||||
|
||||
class TestClass {
|
||||
@Inject(undefined, (x: string) => {
|
||||
(x as unknown as { value: string }).value =
|
||||
'test-value-init';
|
||||
|
||||
return x;
|
||||
})
|
||||
MockDependencyIdentifier!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this.MockDependencyIdentifier;
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
expect(instance.getDependency().value).toBe('test-value-init');
|
||||
});
|
||||
|
||||
it('should throw an error when necessary is true and the initializer throws an error', () => {
|
||||
let _error: Error | undefined = undefined;
|
||||
|
||||
container.register('InitThrowDependencie', {
|
||||
value: 'test-value',
|
||||
});
|
||||
|
||||
try {
|
||||
class TestClass {
|
||||
@Inject(
|
||||
'InitThrowDependencie',
|
||||
(): any => {
|
||||
throw new Error('Initializer error');
|
||||
},
|
||||
true,
|
||||
)
|
||||
dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this.dependency;
|
||||
}
|
||||
}
|
||||
|
||||
const _instance = new TestClass();
|
||||
console.log(_instance.getDependency());
|
||||
} catch (error) {
|
||||
_error = error;
|
||||
}
|
||||
expect(_error).toBeInstanceOf(Error);
|
||||
});
|
||||
|
||||
it('should throw an error when necessary is true and dependency is not found', () => {
|
||||
let _error: Error | undefined = undefined;
|
||||
|
||||
try {
|
||||
class TestClass {
|
||||
@Inject('NonExistentDependencyIdentifier')
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
const _instance = new TestClass();
|
||||
console.log(_instance.getDependency());
|
||||
} catch (error) {
|
||||
_error = error;
|
||||
}
|
||||
expect(_error).toBeInstanceOf(DependencyResolutionError);
|
||||
});
|
||||
|
||||
it('should replace the property with the resolved dependency', () => {
|
||||
container.register('MockDependencyIdentifier', {
|
||||
value: 'test-value',
|
||||
});
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier')
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public isDependencyTypeofFunction() {
|
||||
return typeof this._dependency === 'function';
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
|
||||
expect(instance.getDependency().value).toBe('test-value');
|
||||
|
||||
expect(instance.isDependencyTypeofFunction()).toBe(false);
|
||||
expect(instance.getDependency().value).toBe('test-value');
|
||||
});
|
||||
|
||||
it('should use a empty initializer when none is provided but true', () => {
|
||||
container.register(
|
||||
'MockDependencyIdentifier',
|
||||
class X {
|
||||
public value: string = 'test-value';
|
||||
constructor() {}
|
||||
},
|
||||
);
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier', true)
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
expect(instance.getDependency().value).toBe('test-value');
|
||||
});
|
||||
|
||||
it('should throw an error when the dependency has no instantiation method', () => {
|
||||
container.register('MockDependencyIdentifier', {
|
||||
value: 'test-value',
|
||||
});
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier', true)
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const instance = new TestClass();
|
||||
instance.getDependency();
|
||||
}).toThrow(new RegExp('No instantiation method found for.*'));
|
||||
});
|
||||
|
||||
it('should not throw an error when the dependency has no instantiation method if not necessary', () => {
|
||||
container.register('MockDependencyIdentifier', {
|
||||
value: 'test-value',
|
||||
});
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier', true, false)
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const instance = new TestClass();
|
||||
instance.getDependency();
|
||||
}).not.toThrow(new RegExp('No instantiation method found for.*'));
|
||||
});
|
||||
|
||||
it('should throw an error when the dependency cannot be resolved', () => {
|
||||
container.register('MockDependencyIdentifier', null);
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier', true)
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const instance = new TestClass();
|
||||
instance.getDependency();
|
||||
}).toThrow(new RegExp('.*could not be resolved.*'));
|
||||
});
|
||||
|
||||
it('should not throw an error when the dependency cannot be resolved if not necessary', () => {
|
||||
container.register('MockDependencyIdentifier', null);
|
||||
|
||||
class TestClass {
|
||||
@Inject('MockDependencyIdentifier', true, false)
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => {
|
||||
const instance = new TestClass();
|
||||
instance.getDependency();
|
||||
}).not.toThrow(new RegExp('.*could not be resolved.*'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function test_RegisterDecorator(
|
||||
Container: ITSinjex_,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
register: Function,
|
||||
): void {
|
||||
describe('Register Decorator Tests', () => {
|
||||
let container: ITSinjex;
|
||||
|
||||
beforeEach(() => {
|
||||
// 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 register a dependency', () => {
|
||||
@register('MockDependencyIdentifier')
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
expect(container.resolve('MockDependencyIdentifier')).toBe(
|
||||
TestClass,
|
||||
);
|
||||
});
|
||||
|
||||
it('should register a dependency without an identifier', () => {
|
||||
@register()
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
expect(container.resolve('TestClass')).toBe(TestClass);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function test_RegisterInstanceDecorator(
|
||||
Container: ITSinjex_,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
registerInstance: Function,
|
||||
): void {
|
||||
describe('RegisterInstance Decorator Tests', () => {
|
||||
let container: ITSinjex;
|
||||
|
||||
beforeEach(() => {
|
||||
// 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 register an instance of a dependency', () => {
|
||||
@registerInstance('InstanceIdentifier')
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = 'instance';
|
||||
}
|
||||
|
||||
expect(
|
||||
container.resolve<TestClass>('InstanceIdentifier').mark,
|
||||
).toBe('instance');
|
||||
});
|
||||
|
||||
it('should register an instance of a dependency with an identifier', () => {
|
||||
@registerInstance()
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = 'instance';
|
||||
}
|
||||
|
||||
expect(container.resolve<TestClass>('TestClass').mark).toBe(
|
||||
'instance',
|
||||
);
|
||||
});
|
||||
|
||||
it('should register an instance of a dependency an run the init function', () => {
|
||||
@registerInstance(
|
||||
'InstanceIdentifier',
|
||||
(x: ForceConstructor<TestClass>) => {
|
||||
const instance = new x();
|
||||
instance.mark = 'init';
|
||||
|
||||
return instance;
|
||||
},
|
||||
)
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = 'instance';
|
||||
}
|
||||
|
||||
expect(
|
||||
container.resolve<TestClass>('InstanceIdentifier').mark,
|
||||
).toBe('init');
|
||||
});
|
||||
|
||||
it('should register an instance of a dependency and get it on set', () => {
|
||||
@registerInstance('InstanceIdentifier')
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = 'instance';
|
||||
public test: string = 'test';
|
||||
}
|
||||
|
||||
container.resolve<TestClass>('InstanceIdentifier').test = 'test2';
|
||||
|
||||
expect(
|
||||
container.resolve<TestClass>('InstanceIdentifier').test,
|
||||
).toBe('test2');
|
||||
});
|
||||
|
||||
it('should register an instance of a dependency an run the init function on set', () => {
|
||||
@registerInstance(
|
||||
'InstanceIdentifier',
|
||||
(x: ForceConstructor<TestClass>) => {
|
||||
const instance = new x();
|
||||
instance.mark = 'init';
|
||||
|
||||
return instance;
|
||||
},
|
||||
)
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = 'instance';
|
||||
public test: string = 'test';
|
||||
}
|
||||
|
||||
container.resolve<TestClass>('InstanceIdentifier').test = 'test2';
|
||||
|
||||
expect(
|
||||
container.resolve<TestClass>('InstanceIdentifier').test,
|
||||
).toBe('test2');
|
||||
});
|
||||
});
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
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';
|
||||
|
||||
test_InjectDecorator(TSinjex, Inject);
|
||||
|
||||
test_RegisterDecorator(TSinjex, Register);
|
||||
|
||||
test_RegisterInstanceDecorator(TSinjex, RegisterInstance);
|
@@ -1,71 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { ITSinjex, ITSinjex_ } from 'src/interfaces/ITSinjex';
|
||||
|
||||
export function test_RegisterFunction(
|
||||
Container: ITSinjex_,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
register: Function,
|
||||
): void {
|
||||
describe('Register Function Tests', () => {
|
||||
let container: ITSinjex;
|
||||
|
||||
beforeEach(() => {
|
||||
// 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 register a dependency', () => {
|
||||
const identifier = 'MockDependencyIdentifier';
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
register(identifier, TestClass, false);
|
||||
|
||||
const resolvedDependency = container.resolve(identifier);
|
||||
expect(resolvedDependency).toBe(TestClass);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function test_ResolveFunction(
|
||||
Container: ITSinjex_,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||
resolve: Function,
|
||||
): void {
|
||||
describe('Resolve Function Tests', () => {
|
||||
let container: ITSinjex;
|
||||
|
||||
beforeEach(() => {
|
||||
// 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 a dependency', () => {
|
||||
const identifier = 'MockDependencyIdentifier';
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
container.register(identifier, TestClass);
|
||||
|
||||
const resolvedDependency = resolve(identifier);
|
||||
expect(resolvedDependency).toBe(TestClass);
|
||||
});
|
||||
});
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
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);
|
@@ -1,79 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
import { ITSinjex_, ITSinjex } from '../interfaces/ITSinjex';
|
||||
|
||||
/**
|
||||
* Test the implementation of the `ITSinjex` interface.
|
||||
* @param Container The implementation to test.
|
||||
* Must implement {@link ITSinjex}, {@link ITSinjex_}
|
||||
*/
|
||||
export function test_ITSinjex(Container: ITSinjex_): void {
|
||||
describe('IDIContainer Implementation Tests', () => {
|
||||
let container: ITSinjex;
|
||||
|
||||
beforeEach(() => {
|
||||
// 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 register and resolve a dependency', () => {
|
||||
const identifier = 'myDependency';
|
||||
const dependency = { value: 42 };
|
||||
|
||||
container.register(identifier, dependency);
|
||||
|
||||
const resolvedDependency =
|
||||
container.resolve<typeof dependency>(identifier);
|
||||
expect(resolvedDependency).toBe(dependency);
|
||||
});
|
||||
|
||||
it('should register and resolve a dependency static', () => {
|
||||
const identifier = 'myDependency';
|
||||
const dependency = { value: 42 };
|
||||
|
||||
Container.register(identifier, dependency);
|
||||
|
||||
const resolvedDependency =
|
||||
Container.resolve<typeof dependency>(identifier);
|
||||
expect(resolvedDependency).toBe(dependency);
|
||||
});
|
||||
|
||||
it('should throw an error when resolving a non-registered dependency static', () => {
|
||||
const identifier = 'nonExistentDependency';
|
||||
|
||||
expect(() => Container.resolve<unknown>(identifier)).toThrow();
|
||||
});
|
||||
|
||||
it('should return undefined when resolving a non-registered, non-necessary dependency', () => {
|
||||
const resolvedDependency = Container.resolve<unknown>(
|
||||
'nonExistentDependency',
|
||||
false,
|
||||
);
|
||||
expect(resolvedDependency).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should warn when resolving a deprecated dependency', () => {
|
||||
const identifier = 'deprecatedDependency';
|
||||
const dependency = { value: 42 };
|
||||
|
||||
// Spy on console.warn
|
||||
const warnSpy = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(() => {});
|
||||
|
||||
Container.register(identifier, dependency, true);
|
||||
|
||||
const resolvedDependency =
|
||||
Container.resolve<typeof dependency>(identifier);
|
||||
expect(resolvedDependency).toBe(dependency);
|
||||
|
||||
// Expect console.warn to be called
|
||||
expect(warnSpy).toHaveBeenCalled();
|
||||
|
||||
// Restore the original console.warn
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
}
|
@@ -1,4 +0,0 @@
|
||||
import { test_ITSinjex } from './ITSinjex.spec';
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
|
||||
test_ITSinjex(TSinjex);
|
@@ -1,14 +1,3 @@
|
||||
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
|
||||
* The main class for the Dependency Injection Container **TSinjex**.
|
||||
@@ -21,6 +10,11 @@ import { Identifier } from '../types/Identifier';
|
||||
* @see {@link register} for registering a dependency (class or instance) as a function.
|
||||
* @see {@link resolve} for resolving a dependency as a function.
|
||||
*/
|
||||
|
||||
import { ImplementsStatic } from "../helper/mod.ts";
|
||||
import { DependencyResolutionError, IDependency, ITSinjex, ITSinjex_ } from "../interfaces/mod.ts";
|
||||
import { Identifier } from "../types/mod.ts";
|
||||
|
||||
@ImplementsStatic<ITSinjex_>()
|
||||
export class TSinjex implements ITSinjex {
|
||||
/**
|
||||
@@ -123,5 +117,9 @@ export class TSinjex implements ITSinjex {
|
||||
return dependency.dependency as T;
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this._dependencies.clear();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
1
src/classes/mod.ts
Normal file
1
src/classes/mod.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { TSinjex } from "./TSinjex.ts";
|
@@ -1,164 +1,54 @@
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
import {
|
||||
DependencyResolutionError,
|
||||
IdentifierRequiredError,
|
||||
InitializationError,
|
||||
InjectorError,
|
||||
NoInstantiationMethodError,
|
||||
} from '../interfaces/Exceptions';
|
||||
import { Identifier } from '../types/Identifier';
|
||||
import { InitDelegate } from '../types/InitDelegate';
|
||||
import { TSinjex } from "../classes/mod.ts";
|
||||
import { InitializationError } from "../interfaces/mod.ts";
|
||||
import { Identifier, InitDelegate } from "../types/mod.ts";
|
||||
|
||||
/**
|
||||
* A decorator to inject a dependency from a DI (Dependency Injection) container into a class property.
|
||||
* @template TargetType The type of the class to inject the dependency into.
|
||||
* @template DependencyType The type of the dependency to be injected.
|
||||
* @template PropertyType The type of the property to be injected.
|
||||
* @param identifier The {@link Identifier|identifier} used to resolve the dependencie in the DI container or the property name if not provided.
|
||||
* @param init An optional initializer {@link InitDelegate|function} to transform the dependency before injection
|
||||
* or true to instantiate the dependency if it has a constructor.
|
||||
* @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.**
|
||||
* @throws An {@link IdentifierRequiredError} if the identifier is not provided and the class name is not available.
|
||||
* @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
|
||||
* class MyClass {
|
||||
* \@Inject<MyDependency>('MyDependencyIdentifier')
|
||||
* private myDependency!: MyDependency;
|
||||
* }
|
||||
* ```
|
||||
* @example
|
||||
* ```ts
|
||||
* class MyClass {
|
||||
* \@Inject('ILogger_', (x: ILogger_) => x.getLogger('Tags'), false)
|
||||
* private _logger?: ILogger;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function Inject<TargetType, DependencyType, PropertyType>(
|
||||
identifier?: Identifier,
|
||||
init?: InitDelegate<DependencyType, PropertyType> | true,
|
||||
necessary = true,
|
||||
) {
|
||||
export function Inject<InstanzType, DependencyType, FieldType extends object>(
|
||||
identifier: Identifier,
|
||||
init?: InitDelegate<DependencyType, FieldType>,
|
||||
isNecessary = true,
|
||||
): (
|
||||
target: undefined,
|
||||
context: ClassFieldDecoratorContext<InstanzType, FieldType>,
|
||||
) => (initialValue: FieldType) => FieldType {
|
||||
return function (
|
||||
constructor: undefined,
|
||||
context: ClassFieldDecoratorContext<TargetType>,
|
||||
): (
|
||||
this: TargetType,
|
||||
initialValue: PropertyType | undefined,
|
||||
) => PropertyType {
|
||||
const _identifier = identifier ?? context.name;
|
||||
|
||||
if (_identifier == null && necessary === true)
|
||||
throw new IdentifierRequiredError();
|
||||
|
||||
/**
|
||||
* Function to evaluate the dependency lazily
|
||||
* to avoid circular dependencies, not found dependencies, etc.
|
||||
* @returns The resolved dependency or undefined if the dependency is not found.
|
||||
*/
|
||||
const resolve = (): DependencyType | undefined => {
|
||||
return TSinjex.getInstance().resolve<DependencyType>(
|
||||
_identifier,
|
||||
necessary,
|
||||
);
|
||||
};
|
||||
|
||||
return function (
|
||||
this: TargetType,
|
||||
initialValue: PropertyType | undefined,
|
||||
): PropertyType {
|
||||
let instance: DependencyType | PropertyType | undefined;
|
||||
|
||||
const dependency: DependencyType | undefined = tryAndCatch(
|
||||
() => resolve(),
|
||||
necessary,
|
||||
_identifier,
|
||||
DependencyResolutionError,
|
||||
);
|
||||
|
||||
if (dependency != null) {
|
||||
const initFunction: (() => PropertyType) | undefined =
|
||||
typeof init === 'function' && dependency != null
|
||||
? (): PropertyType => init(dependency)
|
||||
: init === true && hasConstructor(dependency)
|
||||
? (): PropertyType => new dependency() as PropertyType
|
||||
: undefined;
|
||||
|
||||
if (init == null) instance = dependency;
|
||||
else if (initFunction != null)
|
||||
instance = tryAndCatch(
|
||||
initFunction,
|
||||
necessary,
|
||||
_identifier,
|
||||
InitializationError,
|
||||
);
|
||||
else if (necessary)
|
||||
throw new NoInstantiationMethodError(_identifier);
|
||||
} else if (necessary)
|
||||
throw new DependencyResolutionError(_identifier);
|
||||
|
||||
/**
|
||||
* Replace itself with the resolved dependency
|
||||
* for performance reasons.
|
||||
*/
|
||||
Object.defineProperty(this, context.name, {
|
||||
value: instance,
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return instance as any;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
_target: undefined,
|
||||
context: ClassFieldDecoratorContext<InstanzType, FieldType>,
|
||||
): (initialValue: FieldType) => FieldType {
|
||||
if (context.kind !== "field") {
|
||||
throw new Error("Inject decorator can only be used on fields.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 } };
|
||||
const initializer = () => {
|
||||
let instance: DependencyType | FieldType | undefined;
|
||||
|
||||
return (
|
||||
_obj?.prototype != null &&
|
||||
typeof _obj.prototype.constructor === 'function'
|
||||
const dependency: DependencyType | undefined = TSinjex.getInstance()
|
||||
.resolve<DependencyType>(identifier, isNecessary);
|
||||
|
||||
if (init == null || dependency == null) {
|
||||
instance = dependency;
|
||||
} else {
|
||||
try {
|
||||
instance = init(dependency);
|
||||
} catch (error) {
|
||||
if (isNecessary) {
|
||||
throw new InitializationError(
|
||||
identifier,
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
);
|
||||
} else {
|
||||
console.warn(
|
||||
`Error initializing not necessary dependency ${identifier.toString()}: ${error}`,
|
||||
);
|
||||
instance = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instance as FieldType;
|
||||
};
|
||||
|
||||
return function (_initialValue: FieldType): FieldType {
|
||||
return initializer();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@@ -1,34 +1,88 @@
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
import { IdentifierRequiredError } from '../interfaces/Exceptions';
|
||||
import { Identifier } from '../types/Identifier';
|
||||
import { TSinjex } from "../classes/mod.ts";
|
||||
import { ClassConstructor, Identifier, InitDelegate } from "../types/mod.ts";
|
||||
|
||||
/**
|
||||
* 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 {@link Identifier|identifier} used to register the class in the DI container or the class name if not provided.
|
||||
* @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.
|
||||
* @throws An {@link IdentifierRequiredError} if the identifier is not provided and the class name is not available.
|
||||
* @example
|
||||
* ```ts
|
||||
* \@Register('MyClassIdentifier')
|
||||
* class MyClass {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function Register<
|
||||
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
|
||||
>(identifier?: Identifier, deprecated?: boolean) {
|
||||
export function Register<ClassType extends ClassConstructor>(
|
||||
identifier: Identifier,
|
||||
init?: InitDelegate<ClassType, InstanceType<ClassType>>,
|
||||
deprecated?: boolean,
|
||||
): (target: ClassType, context: ClassDecoratorContext<ClassType>) => void {
|
||||
return function (
|
||||
constructor: TargetType,
|
||||
context: ClassDecoratorContext<TargetType>,
|
||||
) {
|
||||
const _identifier = identifier ?? context.name;
|
||||
|
||||
if (_identifier == null) throw new IdentifierRequiredError();
|
||||
target: ClassType,
|
||||
context: ClassDecoratorContext<ClassType>,
|
||||
): void {
|
||||
if (context.kind !== "class") {
|
||||
throw new Error("Register decorator can only be used on classes.");
|
||||
}
|
||||
|
||||
const diContainer = TSinjex.getInstance();
|
||||
diContainer.register(_identifier, constructor, deprecated);
|
||||
let _instance: InstanceType<ClassType> | undefined;
|
||||
|
||||
if (init == undefined) {
|
||||
diContainer.register(identifier, target, deprecated);
|
||||
} else {
|
||||
diContainer.register(
|
||||
identifier,
|
||||
createLazyProxy(
|
||||
_instance,
|
||||
init,
|
||||
target,
|
||||
),
|
||||
deprecated,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createLazyProxy<ClassType extends ClassConstructor>(
|
||||
instance: InstanceType<ClassType> | undefined,
|
||||
init: InitDelegate<ClassType, InstanceType<ClassType>>,
|
||||
constructor: ClassType,
|
||||
): InstanceType<ClassType> {
|
||||
const initializeInstance = (
|
||||
instance: InstanceType<ClassType> | undefined,
|
||||
init: InitDelegate<ClassType, InstanceType<ClassType>>,
|
||||
constructor: ClassType,
|
||||
) => {
|
||||
if (instance == undefined) {
|
||||
if (init != undefined) {
|
||||
instance = init(constructor);
|
||||
} else {
|
||||
instance = new constructor() as InstanceType<ClassType>;
|
||||
}
|
||||
}
|
||||
|
||||
return { instance: instance, lazyProxy: instance };
|
||||
};
|
||||
|
||||
let lazyProxy = new Proxy(
|
||||
{} as InstanceType<ClassType>,
|
||||
{
|
||||
get(_target, prop, _receiver) {
|
||||
({ instance, lazyProxy } = initializeInstance(
|
||||
instance,
|
||||
init,
|
||||
constructor,
|
||||
));
|
||||
|
||||
if (!instance) throw new Error("Instance is not defined");
|
||||
|
||||
// Return the requested property of the instance
|
||||
return instance[prop as keyof InstanceType<ClassType>];
|
||||
},
|
||||
set(_target, prop, value, _receiver) {
|
||||
({ instance, lazyProxy } = initializeInstance(
|
||||
instance,
|
||||
init,
|
||||
constructor,
|
||||
));
|
||||
|
||||
if (!instance) throw new Error("Instance is not defined");
|
||||
|
||||
// Set the requested property of the instance
|
||||
return (instance[prop as keyof InstanceType<ClassType>] = value);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return lazyProxy;
|
||||
}
|
||||
|
@@ -1,80 +0,0 @@
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
import { IdentifierRequiredError } from '../interfaces/Exceptions';
|
||||
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.
|
||||
* @template TargetType The type of the class whose instance is to be registered.
|
||||
* @param identifier The {@link Identifier|identifier} used to register the class in the DI container or the class name if not provided.
|
||||
* @param init An optional initializer {@link InitDelegate|function} which get the constructor of the class
|
||||
* as input and returns an instance of the class.
|
||||
* @returns The decorator function to be applied on the class.
|
||||
* @throws An {@link IdentifierRequiredError} if the identifier is not provided and the class name is not available.
|
||||
* @example
|
||||
* ```ts
|
||||
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor())
|
||||
* class MyClass {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function RegisterInstance<
|
||||
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
|
||||
>(
|
||||
identifier?: Identifier,
|
||||
init?: InitDelegate<
|
||||
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
|
||||
InstanceType<TargetType>
|
||||
>,
|
||||
) {
|
||||
return function (
|
||||
constructor: TargetType,
|
||||
context: ClassDecoratorContext<TargetType>,
|
||||
): void {
|
||||
const _identifier = identifier ?? context.name;
|
||||
|
||||
if (_identifier == null) throw new IdentifierRequiredError();
|
||||
|
||||
const diContainer = TSinjex.getInstance();
|
||||
let instance: InstanceType<TargetType>;
|
||||
|
||||
/**
|
||||
* Get the instance of the class
|
||||
* and replace the lazy proxy with the instance
|
||||
* for performance optimization.
|
||||
*/
|
||||
const getAndRegisterInstance = (): void => {
|
||||
if (instance == null) {
|
||||
if (init) {
|
||||
instance = init(constructor);
|
||||
} else {
|
||||
instance = new constructor();
|
||||
}
|
||||
}
|
||||
diContainer.register(_identifier, instance);
|
||||
};
|
||||
|
||||
// Create a proxy to instantiate the class when needed (Lazy Initialization)
|
||||
const lazyProxy: unknown = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_target, prop, _receiver) {
|
||||
getAndRegisterInstance();
|
||||
|
||||
// Return the requested property of the instance
|
||||
return instance[prop as keyof InstanceType<TargetType>];
|
||||
},
|
||||
set(_target, prop, value, _receiver) {
|
||||
getAndRegisterInstance();
|
||||
|
||||
// Set the requested property of the instance
|
||||
return (instance[prop as keyof InstanceType<TargetType>] =
|
||||
value);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
diContainer.register(_identifier, lazyProxy);
|
||||
};
|
||||
}
|
2
src/decorators/mod.ts
Normal file
2
src/decorators/mod.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { Register } from "./Register.ts";
|
||||
export { Inject } from "./Inject.ts";
|
0
src/functions/mod.ts
Normal file
0
src/functions/mod.ts
Normal file
@@ -1,39 +0,0 @@
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
import { Identifier } from '../types/Identifier';
|
||||
|
||||
/**
|
||||
* Register a dependency.
|
||||
* @param identifier The identifier used to register the class in the DI container.
|
||||
* @see {@link Identifier} for more information on identifiers..
|
||||
* @param dependency The dependency to register.
|
||||
*/
|
||||
export function register<T>(identifier: Identifier, dependency: T): void;
|
||||
|
||||
/**
|
||||
* Register a dependency.
|
||||
* @param identifier The identifier used to register the class in the DI container.
|
||||
* @see {@link Identifier} for more information on identifiers.
|
||||
* @param dependency The dependency to register.
|
||||
* @param deprecated A warning is logged when the dependency is resolved.
|
||||
*/
|
||||
export function register<T>(
|
||||
identifier: Identifier,
|
||||
dependency: T,
|
||||
deprecated?: true,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Register a dependency.
|
||||
* @param identifier The identifier used to register the class in the DI container.
|
||||
* @see {@link Identifier} for more information on identifiers.
|
||||
* @param dependency The dependency to register.
|
||||
* @param deprecated If true, the dependency is deprecated => a warning
|
||||
* is logged when the dependency is resolved.
|
||||
*/
|
||||
export function register<T>(
|
||||
identifier: Identifier,
|
||||
dependency: T,
|
||||
deprecated?: boolean,
|
||||
): void {
|
||||
TSinjex.getInstance().register(identifier, dependency, deprecated);
|
||||
}
|
@@ -1,40 +0,0 @@
|
||||
import { TSinjex } from '../classes/TSinjex';
|
||||
import { DependencyResolutionError } from '../interfaces/Exceptions';
|
||||
import { Identifier } from '../types/Identifier';
|
||||
|
||||
/**
|
||||
* Resolve a dependency.
|
||||
* @param identifier The identifier used to register the class in the DI container.
|
||||
* @see {@link Identifier} for more information on identifiers.
|
||||
* @returns The resolved dependency.
|
||||
* @throws A {@link DependencyResolutionError} if the dependency is not found.
|
||||
*/
|
||||
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 necessary The dependency is **not** necessary.
|
||||
* @returns The resolved dependency or undefined if the dependency is not found.
|
||||
*/
|
||||
export function resolve<T>(
|
||||
identifier: Identifier,
|
||||
necessary: false,
|
||||
): T | undefined;
|
||||
|
||||
/**
|
||||
* 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 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 A {@link DependencyResolutionError} if the dependency is not found and necessary.
|
||||
*/
|
||||
export function resolve<T>(
|
||||
identifier: Identifier,
|
||||
necessary?: boolean,
|
||||
): T | undefined {
|
||||
return TSinjex.getInstance().resolve<T>(identifier, necessary);
|
||||
}
|
@@ -6,5 +6,5 @@
|
||||
* @returns A decorator function
|
||||
*/
|
||||
export function ImplementsStatic<I>() {
|
||||
return <T extends I>(constructor: T, ...args: unknown[]) => {};
|
||||
return <T extends I>(_constructor: T, ..._args: unknown[]) => {};
|
||||
}
|
||||
|
1
src/helper/mod.ts
Normal file
1
src/helper/mod.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { ImplementsStatic } from "./ImplementsStatic.ts";
|
18
src/index.ts
18
src/index.ts
@@ -1,18 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
// Main
|
||||
export * from './classes/TSinjex';
|
||||
|
||||
// Decorators
|
||||
export * from './decorators/Inject';
|
||||
export * from './decorators/Register';
|
||||
export * from './decorators/RegisterInstance';
|
||||
|
||||
// Functions
|
||||
export * from './functions/resolve';
|
||||
export * from './functions/register';
|
||||
|
||||
// Interfaces & Types
|
||||
export type * from './interfaces/ITSinjex';
|
||||
export type * from './types/InitDelegate';
|
||||
export type * from './types/GenericContructor';
|
@@ -1,5 +1,4 @@
|
||||
import { Identifier } from 'src/types/Identifier';
|
||||
import { ITSinjex } from './ITSinjex';
|
||||
import { Identifier } from "../types/mod.ts";
|
||||
|
||||
/**
|
||||
* General error class for {@link ITSinjex} interface.
|
||||
@@ -11,20 +10,7 @@ export class TSinjexError extends Error {
|
||||
*/
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'TSinjex';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error class for missing identifiers in {@link ITSinjex} methods.
|
||||
*/
|
||||
export class IdentifierRequiredError extends TSinjexError {
|
||||
/**
|
||||
* Creates a new instance of {@link IdentifierRequiredError}
|
||||
*/
|
||||
constructor() {
|
||||
super('Identifier is required.');
|
||||
this.name = 'TSinjexIdentifierRequiredError';
|
||||
this.name = "TSinjex";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +25,7 @@ export class DependencyResolutionError extends TSinjexError {
|
||||
*/
|
||||
constructor(identifier: Identifier) {
|
||||
super(`Dependency ${identifier.toString()} could not be resolved.`);
|
||||
this.name = 'TSinjexResolutionError';
|
||||
this.name = "TSinjexResolutionError";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +43,7 @@ export class InjectorError extends TSinjexError {
|
||||
super(
|
||||
`Error injecting dependency ${identifier.toString()} with error: "${originalError}"`,
|
||||
);
|
||||
this.name = 'TSinjexInjectorError';
|
||||
this.name = "TSinjexInjectorError";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +60,7 @@ export class NoInstantiationMethodError extends TSinjexError {
|
||||
super(
|
||||
`No instantiation method found for dependency ${identifier.toString()}.`,
|
||||
);
|
||||
this.name = 'TSinjexNoInstantiationMethodError';
|
||||
this.name = "TSinjexNoInstantiationMethodError";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +78,6 @@ export class InitializationError extends TSinjexError {
|
||||
super(
|
||||
`Error initializing dependency ${identifier.toString()} with error: "${originalError}"`,
|
||||
);
|
||||
this.name = 'TSinjexInitializationError';
|
||||
this.name = "TSinjexInitializationError";
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,4 @@
|
||||
import { DependencyResolutionError } from './Exceptions';
|
||||
import { Identifier } from '../types/Identifier';
|
||||
import { Identifier } from "../types/mod.ts";
|
||||
|
||||
/**
|
||||
* Static TSInjex Interface
|
||||
|
9
src/interfaces/mod.ts
Normal file
9
src/interfaces/mod.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export type { ITSinjex, ITSinjex_ } from "./ITSinjex.ts";
|
||||
export type { IDependency } from "./IDependency.ts";
|
||||
export {
|
||||
DependencyResolutionError,
|
||||
InitializationError,
|
||||
InjectorError,
|
||||
NoInstantiationMethodError,
|
||||
TSinjexError,
|
||||
} from "./Exceptions.ts";
|
5
src/mod.ts
Normal file
5
src/mod.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export { TSinjex } from "./classes/mod.ts";
|
||||
export type { Identifier, InitDelegate } from "./types/mod.ts";
|
||||
export type { ITSinjex, ITSinjex_ } from "./interfaces/mod.ts";
|
||||
export { Inject, Register } from "./decorators/mod.ts";
|
||||
export type { ImplementsStatic } from "./helper/mod.ts";
|
@@ -11,3 +11,8 @@ export type GenericConstructor<
|
||||
* This type is used to force a class to has a constructor.
|
||||
*/
|
||||
export type ForceConstructor<T> = new (...args: unknown[]) => T;
|
||||
|
||||
/**
|
||||
* Represents any concrete (non-abstract) class constructor.
|
||||
*/
|
||||
export type ClassConstructor = new (...args: unknown[]) => object;
|
5
src/types/mod.ts
Normal file
5
src/types/mod.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// Export all type aliases for external use
|
||||
|
||||
export type { Identifier } from "./Identifier.ts";
|
||||
export type { InitDelegate } from "./InitDelegate.ts";
|
||||
export type { ClassConstructor, ForceConstructor, GenericConstructor } from "./Constructor.ts";
|
229
tests/Decorators.ts
Normal file
229
tests/Decorators.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
// deno-coverage-ignore-file
|
||||
// deno-lint-ignore-file no-explicit-any
|
||||
import {
|
||||
assertEquals,
|
||||
assertInstanceOf,
|
||||
assertStrictEquals,
|
||||
assertThrows,
|
||||
} from "https://deno.land/std@0.224.0/assert/mod.ts";
|
||||
import { TSinjex } from "../src/classes/mod.ts";
|
||||
import { Inject, Register } from "../src/decorators/mod.ts";
|
||||
import { DependencyResolutionError } from "../src/interfaces/mod.ts";
|
||||
|
||||
const container = TSinjex.getInstance() as TSinjex;
|
||||
|
||||
Deno.test("should inject dependency when necessary is true", () => {
|
||||
container.clear();
|
||||
container.register("MockDependencyIdentifier", { value: "test-value" });
|
||||
|
||||
class TestClass {
|
||||
@Inject("MockDependencyIdentifier")
|
||||
private _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
assertEquals(instance.getDependency().value, "test-value");
|
||||
});
|
||||
|
||||
Deno.test("should inject dependency and run initializer", () => {
|
||||
container.clear();
|
||||
container.register("MockDependencyIdentifier", { value: "test-value" });
|
||||
|
||||
class TestClass {
|
||||
@Inject("MockDependencyIdentifier", (x: any) => {
|
||||
x.value = "test-value-init";
|
||||
return x;
|
||||
})
|
||||
dependency!: any;
|
||||
public getDependency() {
|
||||
return this.dependency;
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
assertEquals(instance.getDependency().value, "test-value-init");
|
||||
});
|
||||
|
||||
Deno.test("should throw error if initializer fails and dependency is necessary", () => {
|
||||
container.clear();
|
||||
container.register("InitThrowDependencie", { value: "test-value" });
|
||||
|
||||
class TestClass {
|
||||
@Inject<TestClass, any, any>("InitThrowDependencie", () => {
|
||||
throw new Error("Initializer error");
|
||||
}, true)
|
||||
dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this.dependency;
|
||||
}
|
||||
}
|
||||
|
||||
assertThrows(
|
||||
() => {
|
||||
const instance = new TestClass();
|
||||
instance.getDependency()();
|
||||
},
|
||||
Error,
|
||||
"Initializer error",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("should throw DependencyResolutionError if dependency not found", () => {
|
||||
container.clear();
|
||||
|
||||
class TestClass {
|
||||
@Inject("NonExistentDependencyIdentifier")
|
||||
private _dependency!: any;
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
assertThrows(() => {
|
||||
const instance = new TestClass();
|
||||
instance.getDependency()();
|
||||
}, DependencyResolutionError);
|
||||
});
|
||||
|
||||
Deno.test("should replace the property with the resolved dependency", () => {
|
||||
container.clear();
|
||||
container.register("MockDependencyIdentifier", { value: "test-value" });
|
||||
|
||||
class TestClass {
|
||||
@Inject("MockDependencyIdentifier")
|
||||
private _dependency!: any;
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
public isDependencyTypeofFunction() {
|
||||
return typeof this._dependency === "function";
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
assertEquals(instance.getDependency().value, "test-value");
|
||||
assertEquals(instance.isDependencyTypeofFunction(), false);
|
||||
assertEquals(instance.getDependency().value, "test-value");
|
||||
});
|
||||
|
||||
Deno.test("Register Decorator: should register a dependency", () => {
|
||||
container.clear();
|
||||
|
||||
@Register("MockDependencyIdentifier")
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
}
|
||||
|
||||
assertStrictEquals(
|
||||
container.resolve("MockDependencyIdentifier"),
|
||||
TestClass,
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test("RegisterInstance: should register an instance of a dependency", () => {
|
||||
container.clear();
|
||||
|
||||
@Register("InstanceIdentifier", (x) => new x())
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = "instance";
|
||||
}
|
||||
|
||||
const resolved = container.resolve<TestClass>("InstanceIdentifier");
|
||||
assertEquals(resolved?.mark, "instance");
|
||||
});
|
||||
|
||||
Deno.test("RegisterInstance: should run init function during registration", () => {
|
||||
container.clear();
|
||||
|
||||
@Register("InstanceIdentifier", (x: new () => TestClass) => {
|
||||
const instance = new x();
|
||||
instance.mark = "init";
|
||||
return instance;
|
||||
})
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = "instance";
|
||||
}
|
||||
|
||||
const resolved = container.resolve<TestClass>("InstanceIdentifier");
|
||||
assertEquals(resolved?.mark, "init");
|
||||
});
|
||||
|
||||
Deno.test("RegisterInstance: instance should persist modifications", () => {
|
||||
container.clear();
|
||||
|
||||
@Register("InstanceIdentifier", (x) => new x())
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = "instance";
|
||||
public test: string = "test";
|
||||
}
|
||||
|
||||
const instance1 = container.resolve<TestClass>("InstanceIdentifier");
|
||||
if (instance1 == null) {
|
||||
throw new Error("Instance1 is null");
|
||||
}
|
||||
instance1.test = "test2";
|
||||
|
||||
const instance2 = container.resolve<TestClass>("InstanceIdentifier");
|
||||
if (instance2 == null) {
|
||||
throw new Error("Instance2 is null");
|
||||
}
|
||||
assertEquals(instance2.test, "test2");
|
||||
});
|
||||
|
||||
Deno.test("RegisterInstance: init function should persist modifications", () => {
|
||||
container.clear();
|
||||
|
||||
@Register("InstanceIdentifier", (x: new () => TestClass) => {
|
||||
const instance = new x();
|
||||
instance.mark = "init";
|
||||
return instance;
|
||||
})
|
||||
class TestClass {
|
||||
private readonly _dependency!: any;
|
||||
|
||||
public getDependency() {
|
||||
return this._dependency;
|
||||
}
|
||||
|
||||
public mark: string = "instance";
|
||||
public test: string = "test";
|
||||
}
|
||||
|
||||
const instance1 = container.resolve<TestClass>("InstanceIdentifier");
|
||||
if (instance1 == null) {
|
||||
throw new Error("Instance1 is null");
|
||||
}
|
||||
instance1.test = "test2";
|
||||
|
||||
const instance2 = container.resolve<TestClass>("InstanceIdentifier");
|
||||
if (instance2 == null) {
|
||||
throw new Error("Instance2 is null");
|
||||
}
|
||||
assertEquals(instance2.test, "test2");
|
||||
});
|
78
tests/TSInjex.ts
Normal file
78
tests/TSInjex.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
// deno-coverage-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
import {
|
||||
assertEquals,
|
||||
assertStrictEquals,
|
||||
assertThrows,
|
||||
} from "https://deno.land/std@0.224.0/assert/mod.ts";
|
||||
import { TSinjex } from "../src/classes/mod.ts";
|
||||
|
||||
const container = TSinjex.getInstance() as TSinjex;
|
||||
|
||||
Deno.test("should register and resolve a dependency (instance)", () => {
|
||||
container.clear();
|
||||
|
||||
const identifier = "myDependency";
|
||||
const dependency = { value: 42 };
|
||||
|
||||
container.register(identifier, dependency);
|
||||
|
||||
const resolved = container.resolve<typeof dependency>(identifier);
|
||||
assertStrictEquals(resolved, dependency);
|
||||
});
|
||||
|
||||
Deno.test("should register and resolve a dependency (static)", () => {
|
||||
container.clear();
|
||||
|
||||
const identifier = "myDependency";
|
||||
const dependency = { value: 42 };
|
||||
|
||||
TSinjex.register(identifier, dependency);
|
||||
const resolved = TSinjex.resolve<typeof dependency>(identifier);
|
||||
|
||||
assertStrictEquals(resolved, dependency);
|
||||
});
|
||||
|
||||
Deno.test("should throw error when resolving non-registered dependency (static)", () => {
|
||||
container.clear();
|
||||
|
||||
const identifier = "nonExistentDependency";
|
||||
assertThrows(() => {
|
||||
TSinjex.resolve<unknown>(identifier);
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test("should return undefined when resolving non-necessary dependency", () => {
|
||||
container.clear();
|
||||
|
||||
const result = TSinjex.resolve<unknown>("nonExistentDependency", false);
|
||||
assertEquals(result, undefined);
|
||||
});
|
||||
|
||||
Deno.test("should warn when resolving a deprecated dependency", () => {
|
||||
container.clear();
|
||||
|
||||
const identifier = "deprecatedDependency";
|
||||
const dependency = { value: 42 };
|
||||
|
||||
// Mock console.warn
|
||||
const originalWarn = console.warn;
|
||||
let wasCalled = false;
|
||||
let lastMessage = "";
|
||||
|
||||
console.warn = (msg: string) => {
|
||||
wasCalled = true;
|
||||
lastMessage = msg;
|
||||
};
|
||||
|
||||
try {
|
||||
TSinjex.register(identifier, dependency, true);
|
||||
const resolved = TSinjex.resolve<typeof dependency>(identifier);
|
||||
assertStrictEquals(resolved, dependency);
|
||||
if (!wasCalled) {
|
||||
throw new Error("console.warn was not called");
|
||||
}
|
||||
} finally {
|
||||
console.warn = originalWarn; // Restore
|
||||
}
|
||||
});
|
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": false,
|
||||
"sourceMap": true,
|
||||
"inlineSources": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
"strictNullChecks": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
"ES6",
|
||||
"ES7",
|
||||
"ES2021.WeakRef"
|
||||
],
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
18
typedoc.json
18
typedoc.json
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"entryPoints": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"out": ".locale/docs",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"excludePrivate": false,
|
||||
"excludeProtected": false,
|
||||
"excludeExternals": false,
|
||||
"includeVersion": true,
|
||||
"readme": "README.md",
|
||||
"exclude": [
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts"
|
||||
],
|
||||
"theme": "default",
|
||||
"hideGenerator": true
|
||||
}
|
Reference in New Issue
Block a user