Skip to content

Commit b0d93cd

Browse files
authored
perf: make metadata resolver a function (#201)
* perf: make metadata resolver a function * refactor: remove "type" suffix from resolver
1 parent 4f922ab commit b0d93cd

File tree

5 files changed

+65
-45
lines changed

5 files changed

+65
-45
lines changed

projects/ngx-meta/src/core/src/metadata-resolver.spec.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { TestBed } from '@angular/core/testing'
22

3-
import { MetadataResolver } from './metadata-resolver'
43
import { MockProvider, MockProviders } from 'ng-mocks'
54
import { enableAutoSpy } from '../../__tests__/enable-auto-spy'
65
import { MetadataJsonResolver } from './metadata-json-resolver'
@@ -11,8 +10,13 @@ import { MaybeUndefined } from './maybe-undefined'
1110
import { Provider } from '@angular/core'
1211
import { DEFAULTS_TOKEN } from './defaults-token'
1312
import { makeMetadata } from './make-metadata'
13+
import {
14+
METADATA_RESOLVER,
15+
METADATA_RESOLVER_PROVIDER,
16+
MetadataResolver,
17+
} from './metadata-resolver'
1418

15-
describe('MetadataResolver', () => {
19+
describe('Metadata resolver', () => {
1620
enableAutoSpy()
1721

1822
describe('get', () => {
@@ -51,7 +55,7 @@ describe('MetadataResolver', () => {
5155
})
5256

5357
it('should resolve value using values', () => {
54-
sut.get(dummyMetadata, dummyValues)
58+
sut(dummyMetadata, dummyValues)
5559

5660
expect(jsonResolver.get).toHaveBeenCalledWith(
5761
dummyMetadata,
@@ -60,7 +64,7 @@ describe('MetadataResolver', () => {
6064
})
6165

6266
it('should return its value', () => {
63-
expect(sut.get(dummyMetadata, dummyValues)).toEqual(value)
67+
expect(sut(dummyMetadata, dummyValues)).toEqual(value)
6468
})
6569
})
6670

@@ -73,7 +77,7 @@ describe('MetadataResolver', () => {
7377
})
7478

7579
it('should resolve value using route metadata values', () => {
76-
sut.get(dummyMetadata, dummyValues)
80+
sut(dummyMetadata, dummyValues)
7781

7882
expect(routeMetadataValues.get).toHaveBeenCalledOnceWith()
7983
expect(jsonResolver.get).toHaveBeenCalledWith(
@@ -83,7 +87,7 @@ describe('MetadataResolver', () => {
8387
})
8488

8589
it('should return value obtained from route metadata values', () => {
86-
expect(sut.get(dummyMetadata, dummyValues)).toEqual(value)
90+
expect(sut(dummyMetadata, dummyValues)).toEqual(value)
8791
})
8892
})
8993

@@ -96,13 +100,13 @@ describe('MetadataResolver', () => {
96100
})
97101

98102
it('should resolve value using default values', () => {
99-
sut.get(dummyMetadata, dummyValues)
103+
sut(dummyMetadata, dummyValues)
100104

101105
expect(jsonResolver.get).toHaveBeenCalledWith(dummyMetadata, defaults)
102106
})
103107

104108
it('should return value obtained from defaults', () => {
105-
expect(sut.get(dummyMetadata, dummyValues)).toEqual(value)
109+
expect(sut(dummyMetadata, dummyValues)).toEqual(value)
106110
})
107111
})
108112

@@ -126,7 +130,7 @@ describe('MetadataResolver', () => {
126130
})
127131

128132
it('should return the merged object, with value props having more priority', () => {
129-
expect(sut.get(dummyMetadata, dummyValues)).toEqual({
133+
expect(sut(dummyMetadata, dummyValues)).toEqual({
130134
...routeValueObject,
131135
...valueObject,
132136
})
@@ -149,7 +153,7 @@ describe('MetadataResolver', () => {
149153
})
150154

151155
it('should return value from values object', () => {
152-
expect(sut.get(dummyMetadata, dummyValues)).toEqual(value)
156+
expect(sut(dummyMetadata, dummyValues)).toEqual(value)
153157
})
154158
})
155159
})
@@ -160,15 +164,15 @@ describe('MetadataResolver', () => {
160164
injectSpies()
161165
})
162166
it('should return nothing', () => {
163-
expect(sut.get(dummyMetadata, dummyValues)).toBeUndefined()
167+
expect(sut(dummyMetadata, dummyValues)).toBeUndefined()
164168
})
165169
})
166170
})
167171
})
168172

169-
function makeSut(opts: { defaults?: MetadataValues } = {}) {
173+
function makeSut(opts: { defaults?: MetadataValues } = {}): MetadataResolver {
170174
const providers: Provider[] = [
171-
MetadataResolver,
175+
METADATA_RESOLVER_PROVIDER,
172176
MockProviders(MetadataJsonResolver, RouteMetadataValues),
173177
]
174178
if (opts.defaults) {
@@ -177,5 +181,5 @@ function makeSut(opts: { defaults?: MetadataValues } = {}) {
177181
TestBed.configureTestingModule({
178182
providers,
179183
})
180-
return TestBed.inject(MetadataResolver)
184+
return TestBed.inject(METADATA_RESOLVER)
181185
}
Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Inject, Injectable, Optional } from '@angular/core'
1+
import { FactoryProvider, InjectionToken, Optional } from '@angular/core'
22
import { MetadataJsonResolver } from './metadata-json-resolver'
33
import { MetadataValues } from './metadata-values'
44
import { RouteMetadataValues } from './route-metadata-values'
@@ -7,28 +7,39 @@ import { isObject } from './is-object'
77
import { Metadata } from './metadata'
88
import { MaybeUndefined } from './maybe-undefined'
99

10-
@Injectable({ providedIn: 'root' })
11-
export class MetadataResolver {
12-
constructor(
13-
private readonly jsonResolver: MetadataJsonResolver,
14-
private readonly routeMetadataValues: RouteMetadataValues,
15-
@Optional()
16-
@Inject(DEFAULTS_TOKEN)
17-
private readonly defaults: MetadataValues | null,
18-
) {}
10+
export type MetadataResolver<T = unknown> = (
11+
metadata: Metadata,
12+
values: MetadataValues,
13+
) => T | undefined
14+
export const METADATA_RESOLVER = new InjectionToken<MetadataResolver>(
15+
ngDevMode ? 'NgxMeta Metadata Resolver' : 'NgxMetaMR',
16+
)
1917

20-
get<T>(metadata: Metadata, values: MetadataValues): T | undefined {
21-
const value = this.jsonResolver.get(metadata, values)
22-
const routeValue = this.jsonResolver.get(
23-
metadata,
24-
this.routeMetadataValues.get(),
25-
)
26-
const defaultValue = this.jsonResolver.get(metadata, this.defaults ?? {})
18+
export const METADATA_RESOLVER_FACTORY: <T>(
19+
...deps: Exclude<FactoryProvider['deps'], undefined>
20+
) => MetadataResolver<T> =
21+
(
22+
jsonResolver: MetadataJsonResolver,
23+
routeMetadataValues: RouteMetadataValues,
24+
defaults: MetadataValues | null,
25+
) =>
26+
<T>(metadata: Metadata, values: MetadataValues) => {
27+
const value = jsonResolver.get(metadata, values)
28+
const routeValue = jsonResolver.get(metadata, routeMetadataValues.get())
29+
const defaultValue = jsonResolver.get(metadata, defaults ?? {})
2730
const effectiveValue =
2831
isObject(value) && (isObject(routeValue) || isObject(defaultValue))
2932
? { ...(defaultValue as object), ...(routeValue as object), ...value }
3033
: [value, routeValue, defaultValue].find((v) => v !== undefined)
3134

3235
return effectiveValue as MaybeUndefined<T>
3336
}
37+
export const METADATA_RESOLVER_PROVIDER: FactoryProvider = {
38+
provide: METADATA_RESOLVER,
39+
useFactory: METADATA_RESOLVER_FACTORY,
40+
deps: [
41+
MetadataJsonResolver,
42+
RouteMetadataValues,
43+
[DEFAULTS_TOKEN, new Optional()],
44+
],
3445
}

projects/ngx-meta/src/core/src/metadata.service.spec.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { TestBed } from '@angular/core/testing'
22
import { MetadataService } from './metadata.service'
3-
import { MockProviders } from 'ng-mocks'
3+
import { MockProvider, MockProviders } from 'ng-mocks'
44
import { makeMetadataProvider } from './__tests__/make-metadata-provider'
55
import { enableAutoSpy } from '../../__tests__/enable-auto-spy'
6-
import { MetadataResolver } from './metadata-resolver'
6+
import { METADATA_RESOLVER, MetadataResolver } from './metadata-resolver'
77
import { RouteMetadataValues } from './route-metadata-values'
88
import { MetadataRegistry } from './metadata-registry'
99
import { MaybeUndefined } from './maybe-undefined'
@@ -35,11 +35,11 @@ describe('MetadataService', () => {
3535

3636
it('should set each metadata using resolved values', () => {
3737
const resolver = TestBed.inject(
38-
MetadataResolver,
39-
) as unknown as jasmine.SpyObj<MetadataResolver>
38+
METADATA_RESOLVER,
39+
) as unknown as jasmine.Spy<MetadataResolver>
4040
const dummyFirstMetadataValue = 'firstMetadataValue'
4141
const dummySecondMetadataValue = 'secondMetadataValue'
42-
resolver.get.and.callFake(<T>(definition: Metadata) => {
42+
resolver.and.callFake(<T>(definition: Metadata) => {
4343
switch (definition) {
4444
case firstMetadataProvider.metadata:
4545
return dummyFirstMetadataValue as MaybeUndefined<T>
@@ -52,15 +52,15 @@ describe('MetadataService', () => {
5252
sut.set(dummyValues)
5353

5454
expect(metadataRegistry.getAll).toHaveBeenCalledOnceWith()
55-
expect(resolver.get).toHaveBeenCalledTimes(2)
56-
expect(resolver.get).toHaveBeenCalledWith(
55+
expect(resolver).toHaveBeenCalledTimes(2)
56+
expect(resolver).toHaveBeenCalledWith(
5757
firstMetadataProvider.metadata,
5858
dummyValues,
5959
)
6060
expect(firstMetadataProvider.set).toHaveBeenCalledWith(
6161
dummyFirstMetadataValue,
6262
)
63-
expect(resolver.get).toHaveBeenCalledWith(
63+
expect(resolver).toHaveBeenCalledWith(
6464
secondMetadataProvider.metadata,
6565
dummyValues,
6666
)
@@ -85,7 +85,8 @@ function makeSut() {
8585
TestBed.configureTestingModule({
8686
providers: [
8787
MetadataService,
88-
MockProviders(MetadataRegistry, MetadataResolver, RouteMetadataValues),
88+
MockProviders(MetadataRegistry, RouteMetadataValues),
89+
MockProvider(METADATA_RESOLVER, jasmine.createSpy('Metadata resolver')),
8990
],
9091
})
9192
return TestBed.inject(MetadataService)

projects/ngx-meta/src/core/src/metadata.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
1-
import { Injectable } from '@angular/core'
2-
import { MetadataResolver } from './metadata-resolver'
1+
import { Inject, Injectable } from '@angular/core'
32
import { MetadataValues } from './metadata-values'
43
import { RouteMetadataValues } from './route-metadata-values'
54
import { MetadataRegistry } from './metadata-registry'
5+
import { METADATA_RESOLVER, MetadataResolver } from './metadata-resolver'
66

77
@Injectable({ providedIn: 'root' })
88
export class MetadataService {
99
constructor(
1010
private readonly registry: MetadataRegistry,
11-
private readonly resolver: MetadataResolver,
11+
@Inject(METADATA_RESOLVER) private readonly resolver: MetadataResolver,
1212
private readonly routeValues: RouteMetadataValues,
1313
) {}
1414

1515
public set(values: MetadataValues = {}): void {
1616
const allMetadata = this.registry.getAll()
1717
for (const metadata of allMetadata) {
18-
metadata.set(this.resolver.get(metadata.metadata, values))
18+
metadata.set(this.resolver(metadata.metadata, values))
1919
}
2020
this.routeValues.set(values)
2121
}

projects/ngx-meta/src/core/src/provide-core.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ import {
66
import { MetadataValues } from './metadata-values'
77
import { DEFAULTS_TOKEN } from './defaults-token'
88
import { HEAD_ELEMENT_UPSERT_OR_REMOVE_PROVIDER } from './head-element-upsert-or-remove'
9+
import { METADATA_RESOLVER_PROVIDER } from './metadata-resolver'
910

10-
export const CORE_PROVIDERS = [HEAD_ELEMENT_UPSERT_OR_REMOVE_PROVIDER]
11+
export const CORE_PROVIDERS = [
12+
HEAD_ELEMENT_UPSERT_OR_REMOVE_PROVIDER,
13+
METADATA_RESOLVER_PROVIDER,
14+
]
1115

1216
export function provideCore(
1317
...features: ReadonlyArray<CoreFeature>

0 commit comments

Comments
 (0)