Skip to content

Commit

Permalink
Merge pull request #84 from inversify/feat/update-get-class-metadata-…
Browse files Browse the repository at this point in the history
…modules-with-better-error-descriptions

Update get class metadata modules with better error descriptions
  • Loading branch information
notaphplover authored Nov 8, 2024
2 parents 9bcf456 + 0e347ab commit 5d1a1a0
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-bulldogs-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inversifyjs/core": patch
---

Updated get metadata flow to provide better error messages when missing metadata.
4 changes: 4 additions & 0 deletions packages/container/libraries/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
"ts-node": "10.9.2",
"typescript": "5.6.3"
},
"devEngines": {
"node": "^20.18.0",
"pnpm": "^9.12.1"
},
"homepage": "https://inversify.io",
"keywords": [
"dependency injection",
Expand Down
4 changes: 4 additions & 0 deletions packages/container/libraries/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
"node",
"typescript"
],
"devEngines": {
"node": "^20.18.0",
"pnpm": "^9.12.1"
},
"license": "MIT",
"main": "lib/cjs/index.js",
"module": "lib/esm/index.js",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { beforeAll, describe, expect, it } from '@jest/globals';

import { InversifyCoreError } from '../../error/models/InversifyCoreError';
import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind';
import { ClassElementMetadata } from '../models/ClassElementMetadata';
import { assertConstructorMetadataArrayFilled } from './assertConstructorMetadataArrayFilled';

describe(assertConstructorMetadataArrayFilled.name, () => {
describe('having an array with no empty values', () => {
class Foo {}

let arrayFixture: [ClassElementMetadata];

beforeAll(() => {
arrayFixture = [Symbol() as unknown as ClassElementMetadata];
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
try {
assertConstructorMetadataArrayFilled(Foo, arrayFixture);
} catch (error: unknown) {
result = error;
}
});

it('should not throw an error', () => {
expect(result).toBeUndefined();
});
});
});

describe('having an array with empty values', () => {
class Foo {}

let arrayFixture: (ClassElementMetadata | undefined)[];

beforeAll(() => {
arrayFixture = new Array<ClassElementMetadata | undefined>(3);

arrayFixture[1] = Symbol() as unknown as ClassElementMetadata;
});

describe('when called', () => {
let result: unknown;

beforeAll(() => {
try {
assertConstructorMetadataArrayFilled(Foo, arrayFixture);
} catch (error: unknown) {
result = error;
}
});

it('should throw an error', () => {
const expectedErrorProperties: Partial<InversifyCoreError> = {
kind: InversifyCoreErrorKind.missingInjectionDecorator,
message: `Found unexpected missing metadata on type "Foo" at constructor indexes "0", "2".
Are you using @inject, @multiInject or @unmanaged decorators at those indexes?
If you're using typescript and want to rely on auto injection, set "emitDecoratorMetadata" compiler option to true`,
};

expect(result).toStrictEqual(
expect.objectContaining(expectedErrorProperties),
);
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Newable } from '@inversifyjs/common';

import { InversifyCoreError } from '../../error/models/InversifyCoreError';
import { InversifyCoreErrorKind } from '../../error/models/InversifyCoreErrorKind';
import { ClassElementMetadata } from '../models/ClassElementMetadata';

export function assertConstructorMetadataArrayFilled(
type: Newable,
value: (ClassElementMetadata | undefined)[],
): asserts value is ClassElementMetadata[] {
const undefinedIndexes: number[] = [];

// Using a for loop to ensure empty values are traversed as well
for (let i: number = 0; i < value.length; ++i) {
const element: ClassElementMetadata | undefined = value[i];

if (element === undefined) {
undefinedIndexes.push(i);
}
}

if (undefinedIndexes.length > 0) {
throw new InversifyCoreError(
InversifyCoreErrorKind.missingInjectionDecorator,
`Found unexpected missing metadata on type "${type.name}" at constructor indexes "${undefinedIndexes.join('", "')}".
Are you using @inject, @multiInject or @unmanaged decorators at those indexes?
If you're using typescript and want to rely on auto injection, set "emitDecoratorMetadata" compiler option to true`,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ jest.mock('@inversifyjs/reflect-metadata-utils');
import { Newable } from '@inversifyjs/common';
import { getReflectMetadata } from '@inversifyjs/reflect-metadata-utils';

jest.mock('./assertConstructorMetadataArrayFilled');
jest.mock('./getClassElementMetadataFromNewable');
jest.mock('./getConstructorArgumentMetadataFromLegacyMetadata');

Expand All @@ -13,6 +14,7 @@ import { ClassElementMetadata } from '../models/ClassElementMetadata';
import { ClassElementMetadataKind } from '../models/ClassElementMetadataKind';
import { LegacyMetadata } from '../models/LegacyMetadata';
import { LegacyMetadataMap } from '../models/LegacyMetadataMap';
import { assertConstructorMetadataArrayFilled } from './assertConstructorMetadataArrayFilled';
import { getClassElementMetadataFromNewable } from './getClassElementMetadataFromNewable';
import { getClassMetadataConstructorArguments } from './getClassMetadataConstructorArguments';
import { getConstructorArgumentMetadataFromLegacyMetadata } from './getConstructorArgumentMetadataFromLegacyMetadata';
Expand Down Expand Up @@ -73,6 +75,14 @@ describe(getClassMetadataConstructorArguments.name, () => {
);
});

it('should call assertConstructorMetadataArrayFilled()', () => {
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledTimes(1);
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledWith(
typeFixture,
[classElementMetadataFixture],
);
});

it('should return ClassElementMetadata[]', () => {
expect(result).toStrictEqual([classElementMetadataFixture]);
});
Expand Down Expand Up @@ -147,6 +157,14 @@ describe(getClassMetadataConstructorArguments.name, () => {
).toHaveBeenCalledWith(typeFixture, 0, legacyMetadataListFixture);
});

it('should call assertConstructorMetadataArrayFilled()', () => {
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledTimes(1);
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledWith(
typeFixture,
[classElementMetadataFixture],
);
});

it('should return ClassElementMetadata[]', () => {
expect(result).toStrictEqual([classElementMetadataFixture]);
});
Expand Down Expand Up @@ -229,6 +247,14 @@ describe(getClassMetadataConstructorArguments.name, () => {
expect(getClassElementMetadataFromNewable).not.toHaveBeenCalled();
});

it('should call assertConstructorMetadataArrayFilled()', () => {
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledTimes(1);
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledWith(
typeFixture,
[classElementMetadataFixture],
);
});

it('should return ClassElementMetadata[]', () => {
expect(result).toStrictEqual([classElementMetadataFixture]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getReflectMetadata } from '@inversifyjs/reflect-metadata-utils';
import { DESIGN_PARAM_TYPES, TAGGED } from '../../reflectMetadata/data/keys';
import { ClassElementMetadata } from '../models/ClassElementMetadata';
import { LegacyMetadataMap } from '../models/LegacyMetadataMap';
import { assertConstructorMetadataArrayFilled } from './assertConstructorMetadataArrayFilled';
import { getClassElementMetadataFromNewable } from './getClassElementMetadataFromNewable';
import { getConstructorArgumentMetadataFromLegacyMetadata } from './getConstructorArgumentMetadataFromLegacyMetadata';

Expand All @@ -18,7 +19,7 @@ export function getClassMetadataConstructorArguments(
const constructorParametersLegacyMetadata: LegacyMetadataMap | undefined =
getReflectMetadata(type, TAGGED);

const constructorArgumentsMetadata: ClassElementMetadata[] = [];
const constructorArgumentsMetadata: (ClassElementMetadata | undefined)[] = [];

if (constructorParametersLegacyMetadata !== undefined) {
for (const [stringifiedIndex, metadataList] of Object.entries(
Expand Down Expand Up @@ -48,5 +49,7 @@ export function getClassMetadataConstructorArguments(
}
}

assertConstructorMetadataArrayFilled(type, constructorArgumentsMetadata);

return constructorArgumentsMetadata;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals';

import { Newable } from '@inversifyjs/common';

jest.mock('./assertConstructorMetadataArrayFilled');
jest.mock('./getClassElementMetadataFromNewable');
jest.mock('./getConstructorArgumentMetadataFromLegacyMetadata');

Expand All @@ -10,6 +11,7 @@ import { ClassElementMetadataKind } from '../models/ClassElementMetadataKind';
import { LegacyMetadata } from '../models/LegacyMetadata';
import { LegacyMetadataMap } from '../models/LegacyMetadataMap';
import { LegacyMetadataReader } from '../models/LegacyMetadataReader';
import { assertConstructorMetadataArrayFilled } from './assertConstructorMetadataArrayFilled';
import { getClassElementMetadataFromNewable } from './getClassElementMetadataFromNewable';
import { getClassMetadataConstructorArgumentsFromMetadataReader } from './getClassMetadataConstructorArgumentsFromMetadataReader';
import { getConstructorArgumentMetadataFromLegacyMetadata } from './getConstructorArgumentMetadataFromLegacyMetadata';
Expand Down Expand Up @@ -75,6 +77,14 @@ describe(getClassMetadataConstructorArgumentsFromMetadataReader.name, () => {
);
});

it('should call assertConstructorMetadataArrayFilled()', () => {
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledTimes(1);
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledWith(
typeFixture,
[classElementMetadataFixture],
);
});

it('should return ClassElementMetadata[]', () => {
expect(result).toStrictEqual([classElementMetadataFixture]);
});
Expand Down Expand Up @@ -153,6 +163,14 @@ describe(getClassMetadataConstructorArgumentsFromMetadataReader.name, () => {
).toHaveBeenCalledWith(typeFixture, 0, legacyMetadataListFixture);
});

it('should call assertConstructorMetadataArrayFilled()', () => {
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledTimes(1);
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledWith(
typeFixture,
[classElementMetadataFixture],
);
});

it('should return ClassElementMetadata[]', () => {
expect(result).toStrictEqual([classElementMetadataFixture]);
});
Expand Down Expand Up @@ -239,6 +257,14 @@ describe(getClassMetadataConstructorArgumentsFromMetadataReader.name, () => {
expect(getClassElementMetadataFromNewable).not.toHaveBeenCalled();
});

it('should call assertConstructorMetadataArrayFilled()', () => {
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledTimes(1);
expect(assertConstructorMetadataArrayFilled).toHaveBeenCalledWith(
typeFixture,
[classElementMetadataFixture],
);
});

it('should return ClassElementMetadata[]', () => {
expect(result).toStrictEqual([classElementMetadataFixture]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Newable } from '@inversifyjs/common';
import { ClassElementMetadata } from '../models/ClassElementMetadata';
import { LegacyConstructorMetadata } from '../models/LegacyConstructorMetadata';
import { LegacyMetadataReader } from '../models/LegacyMetadataReader';
import { assertConstructorMetadataArrayFilled } from './assertConstructorMetadataArrayFilled';
import { getClassElementMetadataFromNewable } from './getClassElementMetadataFromNewable';
import { getConstructorArgumentMetadataFromLegacyMetadata } from './getConstructorArgumentMetadataFromLegacyMetadata';

Expand All @@ -13,7 +14,7 @@ export function getClassMetadataConstructorArgumentsFromMetadataReader(
const legacyConstructorMetadata: LegacyConstructorMetadata =
metadataReader.getConstructorMetadata(type);

const constructorArgumentsMetadata: ClassElementMetadata[] = [];
const constructorArgumentsMetadata: (ClassElementMetadata | undefined)[] = [];

for (const [stringifiedIndex, metadataList] of Object.entries(
legacyConstructorMetadata.userGeneratedMetadata,
Expand Down Expand Up @@ -44,5 +45,7 @@ export function getClassMetadataConstructorArgumentsFromMetadataReader(
}
}

assertConstructorMetadataArrayFilled(type, constructorArgumentsMetadata);

return constructorArgumentsMetadata;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
"ts-node": "10.9.2",
"typescript": "5.6.3"
},
"devEngines": {
"node": "^20.18.0",
"pnpm": "^9.12.1"
},
"homepage": "https://inversify.io",
"peerDependencies": {
"reflect-metadata": "0.2.2"
Expand Down

0 comments on commit 5d1a1a0

Please sign in to comment.