Skip to content

Commit

Permalink
feat(module-token): added module token to faster module compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
H4ad committed Jan 21, 2023
1 parent 0e0e24b commit f051833
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 11 deletions.
5 changes: 2 additions & 3 deletions packages/core/injector/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ export class ModuleCompiler {
dynamicMetadata?: Partial<DynamicModule> | undefined;
} {
if (!this.isDynamicModule(metatype)) {
return { type: metatype };
return { type: metatype, dynamicMetadata: undefined };
}
const { module: type, ...dynamicMetadata } = metatype;
return { type, dynamicMetadata };
return { type: metatype.module, dynamicMetadata: metatype };
}

public isDynamicModule(
Expand Down
42 changes: 42 additions & 0 deletions packages/core/injector/module-token-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,47 @@ import { DynamicModule } from '@nestjs/common';
import { Type } from '@nestjs/common/interfaces/type.interface';
import { randomStringGenerator } from '@nestjs/common/utils/random-string-generator.util';
import { isFunction, isSymbol } from '@nestjs/common/utils/shared.utils';
import { createHash } from 'crypto';
import stringify from 'fast-safe-stringify';
import * as hash from 'object-hash';

export class ModuleToken implements DynamicModule {
constructor(props: DynamicModule) {
this.moduleId = randomStringGenerator();
this.module = props.module;
this.global = props.global;
this.imports = props.imports;
this.controllers = props.controllers;
this.providers = props.providers;
this.exports = props.exports;
}

moduleId: string;

module: DynamicModule['module'];
global?: DynamicModule['global'];
imports?: DynamicModule['imports'];
controllers?: DynamicModule['controllers'];
providers?: DynamicModule['providers'];
exports?: DynamicModule['exports'];
}

export class ModuleTokenFactory {
private readonly moduleIdsCache = new WeakMap<Type<unknown>, string>();
private readonly moduleTokensCache = new Map<string, string>();

public create(
metatype: Type<unknown>,
dynamicModuleMetadata?: Partial<DynamicModule> | undefined,
): string {
if (dynamicModuleMetadata instanceof ModuleToken)
return dynamicModuleMetadata.moduleId;

const moduleId = this.getModuleId(metatype);

if (!dynamicModuleMetadata)
return this.getFastModuleToken(moduleId, this.getModuleName(metatype));

const opaqueToken = {
id: moduleId,
module: this.getModuleName(metatype),
Expand All @@ -21,6 +51,18 @@ export class ModuleTokenFactory {
return hash(opaqueToken, { ignoreUnknown: true });
}

public getFastModuleToken(moduleId: string, moduleName: string): string {
const key = `${moduleId}_${moduleName}`;

if (this.moduleTokensCache.has(key)) return this.moduleTokensCache.get(key);

const hash = createHash('sha1').update(key).digest('hex');

this.moduleTokensCache.set(key, hash);

return hash;
}

public getDynamicMetadataToken(
dynamicModuleMetadata: Partial<DynamicModule> | undefined,
): string {
Expand Down
23 changes: 23 additions & 0 deletions packages/core/injector/module-token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { DynamicModule } from '../../common/interfaces/modules/dynamic-module.interface';
import { randomStringGenerator } from '../../common/utils/random-string-generator.util';

export class ModuleToken implements DynamicModule {
private constructor(props: DynamicModule) {
this.moduleId = randomStringGenerator();
this.module = props.module;
this.global = props.global;
this.imports = props.imports;
this.controllers = props.controllers;
this.providers = props.providers;
this.exports = props.exports;
}

moduleId: string;

module: DynamicModule['module'];
global?: DynamicModule['global'];
imports?: DynamicModule['imports'];
controllers?: DynamicModule['controllers'];
providers?: DynamicModule['providers'];
exports?: DynamicModule['exports'];
}
5 changes: 2 additions & 3 deletions packages/core/test/injector/compiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ describe('ModuleCompiler', () => {
describe('when module is a dynamic module', () => {
it('should return object with "type" and "dynamicMetadata" property', async () => {
const obj = { module: 'test', providers: [] };
const { module, ...dynamicMetadata } = obj;
expect(await compiler.extractMetadata(obj as any)).to.be.deep.equal({
type: module,
dynamicMetadata,
type: obj.module,
dynamicMetadata: obj,
});
});
});
Expand Down
7 changes: 2 additions & 5 deletions packages/core/test/injector/module-token-factory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import stringify from 'fast-safe-stringify';
import * as hash from 'object-hash';
import * as sinon from 'sinon';
import { ModuleTokenFactory } from '../../injector/module-token-factory';
import { createHash } from 'crypto';

describe('ModuleTokenFactory', () => {
const moduleId = 'constId';
Expand All @@ -18,11 +19,7 @@ describe('ModuleTokenFactory', () => {
const type = Module;
const token = factory.create(type, undefined);
expect(token).to.be.deep.eq(
hash({
id: moduleId,
module: Module.name,
dynamic: '',
}),
createHash('sha1').update(`${moduleId}_${Module.name}`).digest('hex'),
);
});
it('should include dynamic metadata', () => {
Expand Down

0 comments on commit f051833

Please sign in to comment.