Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/inversify/InversifyJS int…
Browse files Browse the repository at this point in the history
…o next/6.1.4-beta
  • Loading branch information
notaphplover committed Nov 13, 2024
2 parents 62947bc + 5370a50 commit 0507748
Show file tree
Hide file tree
Showing 10 changed files with 1,200 additions and 54 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

### Fixed
- Updated container to allow deactivating singleton undefined values.
- Updated planner to provide right circular dependent services when such dependencies are detected.
- Updated ESM build to be compatible with both bundler and NodeJS module resolution algorithms.

## [6.1.4-beta.0]

Expand Down
1,066 changes: 1,046 additions & 20 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
},
"description": "A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.",
"dependencies": {
"@inversifyjs/common": "1.3.2",
"@inversifyjs/core": "1.3.3"
"@inversifyjs/common": "1.3.3",
"@inversifyjs/core": "1.3.4"
},
"devDependencies": {
"@eslint/js": "9.14.0",
Expand All @@ -27,10 +27,13 @@
"reflect-metadata": "0.2.2",
"rimraf": "6.0.1",
"sinon": "19.0.2",
"ts-loader": "9.5.1",
"ts-node": "10.9.2",
"typescript": "5.6.3",
"typescript-eslint": "8.14.0",
"updates": "16.4.0"
"updates": "16.4.0",
"webpack": "5.96.1",
"webpack-cli": "5.1.4"
},
"engines": {},
"homepage": "http://inversify.io",
Expand Down Expand Up @@ -62,7 +65,7 @@
"scripts": {
"build": "npm run build:lib && npm run build:amd && npm run build:es && npm run build:es6",
"build:amd": "tsc -p src/tsconfig-amd.json",
"build:es": "tsc -p src/tsconfig-es.json && node ./scripts/writeEsmPackageJson.mjs ./es",
"build:es": "webpack && node ./scripts/writeEsmPackageJson.mjs ./es",
"build:es6": "tsc -p src/tsconfig-es6.json",
"build:lib": "tsc -p src/tsconfig.json",
"clean": "rimraf amd es es6 lib",
Expand Down
39 changes: 25 additions & 14 deletions src/container/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,10 +530,13 @@ class Container implements interfaces.Container {
}

private _preDestroy(
constructor: NewableFunction,
constructor: NewableFunction | undefined,
instance: unknown,
): Promise<void> | void {
if (Reflect.hasMetadata(METADATA_KEY.PRE_DESTROY, constructor)) {
if (
constructor !== undefined &&
Reflect.hasMetadata(METADATA_KEY.PRE_DESTROY, constructor)
) {
const data: interfaces.Metadata = Reflect.getMetadata(
METADATA_KEY.PRE_DESTROY,
constructor,
Expand Down Expand Up @@ -569,9 +572,11 @@ class Container implements interfaces.Container {
instance: T,
): void | Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const constructor: NewableFunction =
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
Object.getPrototypeOf(instance).constructor;
const constructor: NewableFunction | undefined =
instance == undefined
? undefined
: // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
Object.getPrototypeOf(instance).constructor;

try {
if (this._deactivations.hasKey(binding.serviceIdentifier)) {
Expand All @@ -589,7 +594,7 @@ class Container implements interfaces.Container {
constructor,
),
),
constructor,
binding.serviceIdentifier,
);
}
}
Expand All @@ -604,28 +609,34 @@ class Container implements interfaces.Container {
if (isPromise(propagateDeactivationResult)) {
return this._handleDeactivationError(
propagateDeactivationResult,
constructor,
binding.serviceIdentifier,
);
}
} catch (ex) {
if (ex instanceof Error) {
throw new Error(
ERROR_MSGS.ON_DEACTIVATION_ERROR(constructor.name, ex.message),
ERROR_MSGS.ON_DEACTIVATION_ERROR(
getServiceIdentifierAsString(binding.serviceIdentifier),
ex.message,
),
);
}
}
}

private async _handleDeactivationError(
asyncResult: Promise<void>,
constructor: NewableFunction,
serviceIdentifier: interfaces.ServiceIdentifier,
): Promise<void> {
try {
await asyncResult;
} catch (ex) {
if (ex instanceof Error) {
throw new Error(
ERROR_MSGS.ON_DEACTIVATION_ERROR(constructor.name, ex.message),
ERROR_MSGS.ON_DEACTIVATION_ERROR(
getServiceIdentifierAsString(serviceIdentifier),
ex.message,
),
);
}
}
Expand Down Expand Up @@ -917,7 +928,7 @@ class Container implements interfaces.Container {
private _propagateContainerDeactivationThenBindingAndPreDestroy<T>(
binding: Binding<T>,
instance: T,
constructor: NewableFunction,
constructor: NewableFunction | undefined,
): void | Promise<void> {
if (this.parent) {
return this._deactivate.bind(this.parent)(binding, instance);
Expand All @@ -933,7 +944,7 @@ class Container implements interfaces.Container {
private async _propagateContainerDeactivationThenBindingAndPreDestroyAsync<T>(
binding: Binding<T>,
instance: T,
constructor: NewableFunction,
constructor: NewableFunction | undefined,
): Promise<void> {
if (this.parent) {
await this._deactivate.bind(this.parent)(binding, instance);
Expand Down Expand Up @@ -961,7 +972,7 @@ class Container implements interfaces.Container {
private _bindingDeactivationAndPreDestroy<T>(
binding: Binding<T>,
instance: T,
constructor: NewableFunction,
constructor: NewableFunction | undefined,
): void | Promise<void> {
if (typeof binding.onDeactivation === 'function') {
const result: void | Promise<void> = binding.onDeactivation(instance);
Expand All @@ -979,7 +990,7 @@ class Container implements interfaces.Container {
private async _bindingDeactivationAndPreDestroyAsync<T>(
binding: Binding<T>,
instance: T,
constructor: NewableFunction,
constructor: NewableFunction | undefined,
): Promise<void> {
if (typeof binding.onDeactivation === 'function') {
await binding.onDeactivation(instance);
Expand Down
3 changes: 2 additions & 1 deletion src/tsconfig-es.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../es",
"module": "es2015"
"module": "ES2022",
"moduleResolution": "Bundler"
},
}
2 changes: 1 addition & 1 deletion src/utils/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function dependencyChainToString(request: interfaces.Request) {

function circularDependencyToException(request: interfaces.Request) {
request.childRequests.forEach((childRequest: interfaces.Request) => {
if (alreadyDependencyChain(childRequest, childRequest.serviceIdentifier)) {
if (alreadyDependencyChain(request, childRequest.serviceIdentifier)) {
const services: string = dependencyChainToString(childRequest);
throw new Error(`${ERROR_MSGS.CIRCULAR_DEPENDENCY} ${services}`);
} else {
Expand Down
52 changes: 52 additions & 0 deletions test/bugs/issue_1515.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { expect } from 'chai';

import {
Container,
inject,
injectable,
multiInject,
} from '../../src/inversify';

describe('Issue 1515', () => {
it('should properly throw on circular dependency', () => {
@injectable()
class Circle1 {
constructor(@inject('circle-2') public readonly circle2: unknown) {}
}

@injectable()
class Circle2 {
constructor(@inject('circle-1') public circle1: unknown) {}
}

@injectable()
class Multi1 {}
@injectable()
class Multi2 {}
@injectable()
class Multi3 {}

@injectable()
class Top {
constructor(
@multiInject('multi-inject') public readonly multis: unknown[],
@inject('circle-1') public readonly circle1: unknown,
) {}
}

const container: Container = new Container();

container.bind('multi-inject').to(Multi1);
container.bind('multi-inject').to(Multi2);
container.bind('multi-inject').to(Multi3);
container.bind('circle-1').to(Circle1);
container.bind('circle-2').to(Circle2);
container.bind(Top).toSelf();

expect(() => {
container.get(Top);
}).to.throw(
'Circular dependency found: Top --> circle-1 --> circle-2 --> circle-1',
);
});
});
17 changes: 17 additions & 0 deletions test/bugs/issue_1518.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expect } from 'chai';

import { Container } from '../../src/inversify';

describe('Issue 1515', () => {
it('should not throw on deactivating undefined singleton values', () => {
const container: Container = new Container();
const symbol: symbol = Symbol.for('foo');
container.bind(symbol).toConstantValue(undefined);

console.log(container.get(symbol));

container.unbindAll();

expect(() => {}).not.to.throw();
});
});
14 changes: 0 additions & 14 deletions test/tsconfig.json

This file was deleted.

47 changes: 47 additions & 0 deletions webpack.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// ts-check

import path from 'node:path';

const outputPath = path.resolve(import.meta.dirname, 'es');

/** @type {!import("webpack").Configuration} */
export default {
devtool: 'inline-source-map',
entry: './src/inversify.ts',
experiments: {
outputModule: true,
},
externals: [/@inversifyjs\/.+/],
mode: 'production',
module: {
rules: [
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
configFile: 'src/tsconfig-es.json',
},
},
],
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.ts', '.js'],
},
output: {
filename: 'inversify.js',
library: {
type: 'module',
},
path: outputPath,
},
performance: {
maxEntrypointSize: 512000,
maxAssetSize: 512000,
},
stats: 'verbose',
};

0 comments on commit 0507748

Please sign in to comment.