• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nestjs / nest / 1e41e624-18ea-4ab6-9e2e-37016498fb6d

27 Nov 2024 07:30AM UTC coverage: 91.993% (+0.05%) from 91.944%
1e41e624-18ea-4ab6-9e2e-37016498fb6d

Pull #14213

circleci

mag123c
feat(common): add error messages for file validators
Pull Request #14213: feat(common): add error messages for file validators

2088 of 2514 branches covered (83.05%)

4 of 4 new or added lines in 2 files covered. (100.0%)

37 existing lines in 4 files now uncovered.

6813 of 7406 relevant lines covered (91.99%)

17.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.12
/packages/core/injector/module.ts
1
import {
1✔
2
  EnhancerSubtype,
3
  ENTRY_PROVIDER_WATERMARK,
4
} from '@nestjs/common/constants';
5
import {
6
  ClassProvider,
7
  Controller,
8
  DynamicModule,
9
  ExistingProvider,
10
  FactoryProvider,
11
  Injectable,
12
  InjectionToken,
13
  NestModule,
14
  Provider,
15
  Type,
16
  ValueProvider,
17
} from '@nestjs/common/interfaces';
18
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
1✔
19
import {
1✔
20
  isFunction,
21
  isNil,
22
  isObject,
23
  isString,
24
  isSymbol,
25
  isUndefined,
26
} from '@nestjs/common/utils/shared.utils';
27
import { iterate } from 'iterare';
1✔
28
import { ApplicationConfig } from '../application-config';
1✔
29
import {
1✔
30
  InvalidClassException,
31
  RuntimeException,
32
  UnknownExportException,
33
} from '../errors/exceptions';
34
import { createContextId } from '../helpers/context-id-factory';
1✔
35
import { getClassScope } from '../helpers/get-class-scope';
1✔
36
import { isDurable } from '../helpers/is-durable';
1✔
37
import { UuidFactory } from '../inspector/uuid-factory';
1✔
38
import { CONTROLLER_ID_KEY } from './constants';
1✔
39
import { NestContainer } from './container';
40
import { ContextId, InstanceWrapper } from './instance-wrapper';
1✔
41
import { ModuleRef, ModuleRefGetOrResolveOpts } from './module-ref';
1✔
42

43
export class Module {
1✔
44
  private readonly _id: string;
45
  private readonly _imports = new Set<Module>();
125✔
46
  private readonly _providers = new Map<
125✔
47
    InjectionToken,
48
    InstanceWrapper<Injectable>
49
  >();
50
  private readonly _injectables = new Map<
125✔
51
    InjectionToken,
52
    InstanceWrapper<Injectable>
53
  >();
54
  private readonly _middlewares = new Map<
125✔
55
    InjectionToken,
56
    InstanceWrapper<Injectable>
57
  >();
58
  private readonly _controllers = new Map<
125✔
59
    InjectionToken,
60
    InstanceWrapper<Controller>
61
  >();
62
  private readonly _entryProviderKeys = new Set<InjectionToken>();
125✔
63
  private readonly _exports = new Set<InjectionToken>();
125✔
64

65
  private _distance = 0;
125✔
66
  private _initOnPreview = false;
125✔
67
  private _isGlobal = false;
125✔
68
  private _token: string;
69

70
  constructor(
71
    private readonly _metatype: Type<any>,
125✔
72
    private readonly container: NestContainer,
125✔
73
  ) {
74
    this.addCoreProviders();
125✔
75
    this._id = this.generateUuid();
125✔
76
  }
77

78
  get id(): string {
79
    return this._id;
217✔
80
  }
81

82
  get token(): string {
83
    return this._token;
86✔
84
  }
85

86
  set token(token: string) {
87
    this._token = token;
51✔
88
  }
89

90
  get name() {
91
    return this.metatype.name;
804✔
92
  }
93

94
  get isGlobal() {
95
    return this._isGlobal;
25✔
96
  }
97

98
  set isGlobal(global: boolean) {
99
    this._isGlobal = global;
2✔
100
  }
101

102
  get initOnPreview() {
UNCOV
103
    return this._initOnPreview;
×
104
  }
105

106
  set initOnPreview(initOnPreview: boolean) {
107
    this._initOnPreview = initOnPreview;
51✔
108
  }
109

110
  get providers(): Map<InjectionToken, InstanceWrapper<Injectable>> {
111
    return this._providers;
292✔
112
  }
113

114
  get middlewares(): Map<InjectionToken, InstanceWrapper<Injectable>> {
115
    return this._middlewares;
19✔
116
  }
117

118
  get imports(): Set<Module> {
119
    return this._imports;
32✔
120
  }
121

122
  get injectables(): Map<InjectionToken, InstanceWrapper<Injectable>> {
123
    return this._injectables;
138✔
124
  }
125

126
  get controllers(): Map<InjectionToken, InstanceWrapper<Controller>> {
127
    return this._controllers;
115✔
128
  }
129

130
  get entryProviders(): Array<InstanceWrapper<Injectable>> {
UNCOV
131
    return Array.from(this._entryProviderKeys).map(token =>
×
132
      this.providers.get(token),
×
133
    );
134
  }
135

136
  get exports(): Set<InjectionToken> {
137
    return this._exports;
122✔
138
  }
139

140
  get instance(): NestModule {
141
    if (!this._providers.has(this._metatype)) {
2✔
142
      throw new RuntimeException();
1✔
143
    }
144
    const module = this._providers.get(this._metatype);
1✔
145
    return module.instance as NestModule;
1✔
146
  }
147

148
  get metatype(): Type<any> {
149
    return this._metatype;
976✔
150
  }
151

152
  get distance(): number {
UNCOV
153
    return this._distance;
×
154
  }
155

156
  set distance(value: number) {
UNCOV
157
    this._distance = value;
×
158
  }
159

160
  public addCoreProviders() {
161
    this.addModuleAsProvider();
125✔
162
    this.addModuleRef();
125✔
163
    this.addApplicationConfig();
125✔
164
  }
165

166
  public addModuleRef() {
167
    const moduleRef = this.createModuleReferenceType();
125✔
168
    this._providers.set(
125✔
169
      ModuleRef,
170
      new InstanceWrapper({
171
        token: ModuleRef,
172
        name: ModuleRef.name,
173
        metatype: ModuleRef as any,
174
        isResolved: true,
175
        instance: new moduleRef(),
176
        host: this,
177
      }),
178
    );
179
  }
180

181
  public addModuleAsProvider() {
182
    this._providers.set(
125✔
183
      this._metatype,
184
      new InstanceWrapper({
185
        token: this._metatype,
186
        name: this._metatype.name,
187
        metatype: this._metatype,
188
        isResolved: false,
189
        instance: null,
190
        host: this,
191
      }),
192
    );
193
  }
194

195
  public addApplicationConfig() {
196
    this._providers.set(
125✔
197
      ApplicationConfig,
198
      new InstanceWrapper({
199
        token: ApplicationConfig,
200
        name: ApplicationConfig.name,
201
        isResolved: true,
202
        instance: this.container.applicationConfig,
203
        host: this,
204
      }),
205
    );
206
  }
207

208
  public addInjectable<T extends Injectable>(
209
    injectable: Provider,
210
    enhancerSubtype: EnhancerSubtype,
211
    host?: Type<T>,
212
  ) {
213
    if (this.isCustomProvider(injectable)) {
23✔
214
      return this.addCustomProvider(
21✔
215
        injectable,
216
        this._injectables,
217
        enhancerSubtype,
218
      );
219
    }
220
    let instanceWrapper = this.injectables.get(injectable);
2✔
221
    if (!instanceWrapper) {
2✔
222
      instanceWrapper = new InstanceWrapper({
2✔
223
        token: injectable,
224
        name: injectable.name,
225
        metatype: injectable,
226
        instance: null,
227
        isResolved: false,
228
        scope: getClassScope(injectable),
229
        durable: isDurable(injectable),
230
        subtype: enhancerSubtype,
231
        host: this,
232
      });
233
      this._injectables.set(injectable, instanceWrapper);
2✔
234
    }
235
    if (host) {
2✔
236
      const hostWrapper =
237
        this._controllers.get(host) || this._providers.get(host);
1✔
238
      hostWrapper && hostWrapper.addEnhancerMetadata(instanceWrapper);
1✔
239
    }
240
    return instanceWrapper;
2✔
241
  }
242

243
  public addProvider(provider: Provider): InjectionToken;
244
  public addProvider(
245
    provider: Provider,
246
    enhancerSubtype: EnhancerSubtype,
247
  ): InjectionToken;
248
  public addProvider(provider: Provider, enhancerSubtype?: EnhancerSubtype) {
249
    if (this.isCustomProvider(provider)) {
52✔
250
      if (this.isEntryProvider(provider.provide)) {
35!
UNCOV
251
        this._entryProviderKeys.add(provider.provide);
×
252
      }
253
      return this.addCustomProvider(provider, this._providers, enhancerSubtype);
35✔
254
    }
255

256
    this._providers.set(
17✔
257
      provider,
258
      new InstanceWrapper({
259
        token: provider,
260
        name: (provider as Type<Injectable>).name,
261
        metatype: provider as Type<Injectable>,
262
        instance: null,
263
        isResolved: false,
264
        scope: getClassScope(provider),
265
        durable: isDurable(provider),
266
        host: this,
267
      }),
268
    );
269

270
    if (this.isEntryProvider(provider)) {
17!
UNCOV
271
      this._entryProviderKeys.add(provider);
×
272
    }
273

274
    return provider as Type<Injectable>;
17✔
275
  }
276

277
  public isCustomProvider(
278
    provider: Provider,
279
  ): provider is
280
    | ClassProvider
281
    | FactoryProvider
282
    | ValueProvider
283
    | ExistingProvider {
284
    return !isNil(
79✔
285
      (
286
        provider as
287
          | ClassProvider
288
          | FactoryProvider
289
          | ValueProvider
290
          | ExistingProvider
291
      ).provide,
292
    );
293
  }
294

295
  public addCustomProvider(
296
    provider:
297
      | ClassProvider
298
      | FactoryProvider
299
      | ValueProvider
300
      | ExistingProvider,
301
    collection: Map<Function | string | symbol, any>,
302
    enhancerSubtype?: EnhancerSubtype,
303
  ) {
304
    if (this.isCustomClass(provider)) {
59✔
305
      this.addCustomClass(provider, collection, enhancerSubtype);
43✔
306
    } else if (this.isCustomValue(provider)) {
16✔
307
      this.addCustomValue(provider, collection, enhancerSubtype);
14✔
308
    } else if (this.isCustomFactory(provider)) {
2✔
309
      this.addCustomFactory(provider, collection, enhancerSubtype);
1✔
310
    } else if (this.isCustomUseExisting(provider)) {
1!
UNCOV
311
      this.addCustomUseExisting(provider, collection, enhancerSubtype);
×
312
    }
313
    return provider.provide;
59✔
314
  }
315

316
  public isCustomClass(provider: any): provider is ClassProvider {
317
    return !isUndefined((provider as ClassProvider).useClass);
59✔
318
  }
319

320
  public isCustomValue(provider: any): provider is ValueProvider {
321
    return (
16✔
322
      isObject(provider) &&
32✔
323
      Object.prototype.hasOwnProperty.call(provider, 'useValue')
324
    );
325
  }
326

327
  public isCustomFactory(provider: any): provider is FactoryProvider {
328
    return !isUndefined((provider as FactoryProvider).useFactory);
2✔
329
  }
330

331
  public isCustomUseExisting(provider: any): provider is ExistingProvider {
332
    return !isUndefined((provider as ExistingProvider).useExisting);
1✔
333
  }
334

335
  public isDynamicModule(exported: any): exported is DynamicModule {
336
    return exported && exported.module;
1✔
337
  }
338

339
  public addCustomClass(
340
    provider: ClassProvider,
341
    collection: Map<InjectionToken, InstanceWrapper>,
342
    enhancerSubtype?: EnhancerSubtype,
343
  ) {
344
    let { scope, durable } = provider;
43✔
345

346
    const { useClass } = provider;
43✔
347
    if (isUndefined(scope)) {
43✔
348
      scope = getClassScope(useClass);
2✔
349
    }
350
    if (isUndefined(durable)) {
43✔
351
      durable = isDurable(useClass);
42✔
352
    }
353

354
    const token = provider.provide;
43✔
355
    collection.set(
43✔
356
      token,
357
      new InstanceWrapper({
358
        token,
359
        name: useClass?.name || useClass,
43!
360
        metatype: useClass,
361
        instance: null,
362
        isResolved: false,
363
        scope,
364
        durable,
365
        host: this,
366
        subtype: enhancerSubtype,
367
      }),
368
    );
369
  }
370

371
  public addCustomValue(
372
    provider: ValueProvider,
373
    collection: Map<Function | string | symbol, InstanceWrapper>,
374
    enhancerSubtype?: EnhancerSubtype,
375
  ) {
376
    const { useValue: value, provide: providerToken } = provider;
13✔
377
    collection.set(
13✔
378
      providerToken,
379
      new InstanceWrapper({
380
        token: providerToken,
381
        name: (providerToken as Function)?.name || providerToken,
15✔
382
        metatype: null,
383
        instance: value,
384
        isResolved: true,
385
        async: value instanceof Promise,
386
        host: this,
387
        subtype: enhancerSubtype,
388
      }),
389
    );
390
  }
391

392
  public addCustomFactory(
393
    provider: FactoryProvider,
394
    collection: Map<Function | string | symbol, InstanceWrapper>,
395
    enhancerSubtype?: EnhancerSubtype,
396
  ) {
397
    const {
398
      useFactory: factory,
399
      inject,
400
      scope,
401
      durable,
402
      provide: providerToken,
403
    } = provider;
1✔
404

405
    collection.set(
1✔
406
      providerToken,
407
      new InstanceWrapper({
408
        token: providerToken,
409
        name: (providerToken as Function)?.name || providerToken,
1!
410
        metatype: factory as any,
411
        instance: null,
412
        isResolved: false,
413
        inject: inject || [],
1!
414
        scope,
415
        durable,
416
        host: this,
417
        subtype: enhancerSubtype,
418
      }),
419
    );
420
  }
421

422
  public addCustomUseExisting(
423
    provider: ExistingProvider,
424
    collection: Map<Function | string | symbol, InstanceWrapper>,
425
    enhancerSubtype?: EnhancerSubtype,
426
  ) {
427
    const { useExisting, provide: providerToken } = provider;
1✔
428
    collection.set(
1✔
429
      providerToken,
430
      new InstanceWrapper({
431
        token: providerToken,
432
        name: (providerToken as Function)?.name || providerToken,
1!
433
        metatype: (instance => instance) as any,
1✔
434
        instance: null,
435
        isResolved: false,
436
        inject: [useExisting],
437
        host: this,
438
        isAlias: true,
439
        subtype: enhancerSubtype,
440
      }),
441
    );
442
  }
443

444
  public addExportedProvider(
445
    provider: Provider | string | symbol | DynamicModule,
446
  ) {
447
    const addExportedUnit = (token: InjectionToken) =>
4✔
448
      this._exports.add(this.validateExportedProvider(token));
1✔
449

450
    if (this.isCustomProvider(provider as any)) {
4✔
451
      return this.addCustomExportedProvider(provider as any);
3✔
452
    } else if (isString(provider) || isSymbol(provider)) {
1!
UNCOV
453
      return addExportedUnit(provider);
×
454
    } else if (this.isDynamicModule(provider)) {
1!
UNCOV
455
      const { module: moduleClassRef } = provider;
×
UNCOV
456
      return addExportedUnit(moduleClassRef);
×
457
    }
458
    addExportedUnit(provider as Type<any>);
1✔
459
  }
460

461
  public addCustomExportedProvider(
462
    provider:
463
      | FactoryProvider
464
      | ValueProvider
465
      | ClassProvider
466
      | ExistingProvider,
467
  ) {
468
    const provide = provider.provide;
3✔
469
    if (isString(provide) || isSymbol(provide)) {
3✔
470
      return this._exports.add(this.validateExportedProvider(provide));
3✔
471
    }
UNCOV
472
    this._exports.add(this.validateExportedProvider(provide));
×
473
  }
474

475
  public validateExportedProvider(token: InjectionToken) {
476
    if (this._providers.has(token)) {
5✔
477
      return token;
3✔
478
    }
479
    const imports = iterate(this._imports.values())
2✔
480
      .filter(item => !!item)
1✔
481
      .map(({ metatype }) => metatype)
1✔
482
      .filter(metatype => !!metatype)
1✔
483
      .toArray();
484

485
    if (!imports.includes(token as Type<unknown>)) {
2✔
486
      const { name } = this.metatype;
1✔
487
      const providerName = isFunction(token) ? (token as Function).name : token;
1!
488
      throw new UnknownExportException(providerName as string, name);
1✔
489
    }
490
    return token;
1✔
491
  }
492

493
  public addController(controller: Type<Controller>) {
494
    this._controllers.set(
10✔
495
      controller,
496
      new InstanceWrapper({
497
        token: controller,
498
        name: controller.name,
499
        metatype: controller,
500
        instance: null,
501
        isResolved: false,
502
        scope: getClassScope(controller),
503
        durable: isDurable(controller),
504
        host: this,
505
      }),
506
    );
507

508
    this.assignControllerUniqueId(controller);
10✔
509
  }
510

511
  public assignControllerUniqueId(controller: Type<Controller>) {
512
    Object.defineProperty(controller, CONTROLLER_ID_KEY, {
10✔
513
      enumerable: false,
514
      writable: false,
515
      configurable: true,
516
      value: randomStringGenerator(),
517
    });
518
  }
519

520
  public addImport(moduleRef: Module) {
521
    this._imports.add(moduleRef);
9✔
522
  }
523

524
  /**
525
   * @deprecated
526
   */
527
  public addRelatedModule(module: Module) {
UNCOV
528
    this._imports.add(module);
×
529
  }
530

531
  public replace(toReplace: InjectionToken, options: any) {
532
    if (options.isProvider && this.hasProvider(toReplace)) {
2✔
533
      const originalProvider = this._providers.get(toReplace);
1✔
534

535
      return originalProvider.mergeWith({ provide: toReplace, ...options });
1✔
536
    } else if (!options.isProvider && this.hasInjectable(toReplace)) {
1✔
537
      const originalInjectable = this._injectables.get(toReplace);
1✔
538

539
      return originalInjectable.mergeWith({
1✔
540
        provide: toReplace,
541
        ...options,
542
      });
543
    }
544
  }
545

546
  public hasProvider(token: InjectionToken): boolean {
547
    return this._providers.has(token);
2✔
548
  }
549

550
  public hasInjectable(token: InjectionToken): boolean {
551
    return this._injectables.has(token);
2✔
552
  }
553

554
  public getProviderByKey<T = any>(name: InjectionToken): InstanceWrapper<T> {
555
    return this._providers.get(name) as InstanceWrapper<T>;
48✔
556
  }
557

558
  public getProviderById<T = any>(id: string): InstanceWrapper<T> | undefined {
UNCOV
559
    return Array.from(this._providers.values()).find(
×
UNCOV
560
      item => item.id === id,
×
561
    ) as InstanceWrapper<T>;
562
  }
563

564
  public getControllerById<T = any>(
565
    id: string,
566
  ): InstanceWrapper<T> | undefined {
UNCOV
567
    return Array.from(this._controllers.values()).find(
×
UNCOV
568
      item => item.id === id,
×
569
    ) as InstanceWrapper<T>;
570
  }
571

572
  public getInjectableById<T = any>(
573
    id: string,
574
  ): InstanceWrapper<T> | undefined {
UNCOV
575
    return Array.from(this._injectables.values()).find(
×
UNCOV
576
      item => item.id === id,
×
577
    ) as InstanceWrapper<T>;
578
  }
579

580
  public getMiddlewareById<T = any>(
581
    id: string,
582
  ): InstanceWrapper<T> | undefined {
UNCOV
583
    return Array.from(this._middlewares.values()).find(
×
UNCOV
584
      item => item.id === id,
×
585
    ) as InstanceWrapper<T>;
586
  }
587

588
  public getNonAliasProviders(): Array<
589
    [InjectionToken, InstanceWrapper<Injectable>]
590
  > {
591
    return [...this._providers].filter(([_, wrapper]) => !wrapper.isAlias);
58✔
592
  }
593

594
  public createModuleReferenceType(): Type<ModuleRef> {
595
    // eslint-disable-next-line @typescript-eslint/no-this-alias
596
    const self = this;
127✔
597
    return class extends ModuleRef {
127✔
598
      constructor() {
599
        super(self.container);
127✔
600
      }
601

602
      public get<TInput = any, TResult = TInput>(
603
        typeOrToken: Type<TInput> | string | symbol,
604
        options: ModuleRefGetOrResolveOpts = {},
1✔
605
      ): TResult | Array<TResult> {
606
        options.strict ??= true;
2✔
607
        options.each ??= false;
2✔
608

609
        return this.find<TInput, TResult>(
2✔
610
          typeOrToken,
611
          options.strict
2✔
612
            ? {
613
                moduleId: self.id,
614
                each: options.each,
615
              }
616
            : options,
617
        );
618
      }
619

620
      public resolve<TInput = any, TResult = TInput>(
621
        typeOrToken: Type<TInput> | string | symbol,
622
        contextId = createContextId(),
×
623
        options: ModuleRefGetOrResolveOpts = {},
×
624
      ): Promise<TResult | Array<TResult>> {
UNCOV
625
        options.strict ??= true;
×
UNCOV
626
        options.each ??= false;
×
627

UNCOV
628
        return this.resolvePerContext<TInput, TResult>(
×
629
          typeOrToken,
630
          self,
631
          contextId,
632
          options,
633
        );
634
      }
635

636
      public async create<T = any>(
637
        type: Type<T>,
638
        contextId?: ContextId,
639
      ): Promise<T> {
UNCOV
640
        if (!(type && isFunction(type) && type.prototype)) {
×
UNCOV
641
          throw new InvalidClassException(type);
×
642
        }
UNCOV
643
        return this.instantiateClass<T>(type, self, contextId);
×
644
      }
645
    };
646
  }
647

648
  private isEntryProvider(metatype: InjectionToken): boolean {
649
    return typeof metatype === 'function'
52✔
650
      ? !!Reflect.getMetadata(ENTRY_PROVIDER_WATERMARK, metatype)
651
      : false;
652
  }
653

654
  private generateUuid(): string {
655
    const prefix = 'M_';
125✔
656
    const key = this.name?.toString() ?? this.token?.toString();
125!
657
    return key ? UuidFactory.get(`${prefix}_${key}`) : randomStringGenerator();
125✔
658
  }
659
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc