From cbf82f678a1b0c7805e81684e649109c203c4d24 Mon Sep 17 00:00:00 2001 From: mag123c Date: Wed, 1 Oct 2025 10:03:54 +0900 Subject: [PATCH] fix(common): resolve extras in configurable module builder async methods --- .../configurable-module.builder.ts | 25 +++++++++++++++-- packages/common/module-utils/constants.ts | 13 +++++++++ .../configurable-module.builder.spec.ts | 28 +++++++++++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/packages/common/module-utils/configurable-module.builder.ts b/packages/common/module-utils/configurable-module.builder.ts index cec0ea6ae7d..bf8357b1a6f 100644 --- a/packages/common/module-utils/configurable-module.builder.ts +++ b/packages/common/module-utils/configurable-module.builder.ts @@ -4,6 +4,7 @@ import { Logger } from '../services/logger.service'; import { randomStringGenerator } from '../utils/random-string-generator.util'; import { ASYNC_METHOD_SUFFIX, + ASYNC_OPTIONS_METADATA_KEYS, CONFIGURABLE_MODULE_ID, DEFAULT_FACTORY_CLASS_METHOD_KEY, DEFAULT_METHOD_KEY, @@ -254,7 +255,7 @@ export class ConfigurableModuleBuilder< }, { ...self.extras, - ...options, + ...this.extractExtrasFromAsyncOptions(options, self.extras), }, ); } @@ -277,8 +278,28 @@ export class ConfigurableModuleBuilder< return moduleOptions as ModuleOptions; } + private static extractExtrasFromAsyncOptions( + input: ConfigurableModuleAsyncOptions & + ExtraModuleDefinitionOptions, + extras: ExtraModuleDefinitionOptions | undefined, + ): Partial { + if (!extras) { + return {}; + } + const extrasOptions = {}; + + Object.keys(input as object) + .filter(key => !ASYNC_OPTIONS_METADATA_KEYS.includes(key as any)) + .forEach(key => { + extrasOptions[key] = input[key]; + }); + + return extrasOptions; + } + private static createAsyncProviders( - options: ConfigurableModuleAsyncOptions, + options: ConfigurableModuleAsyncOptions & + ExtraModuleDefinitionOptions, ): Provider[] { if (options.useExisting || options.useFactory) { if (options.inject && options.provideInjectionTokensFrom) { diff --git a/packages/common/module-utils/constants.ts b/packages/common/module-utils/constants.ts index 2d97c0c8cf7..cce0974cef2 100644 --- a/packages/common/module-utils/constants.ts +++ b/packages/common/module-utils/constants.ts @@ -3,3 +3,16 @@ export const DEFAULT_FACTORY_CLASS_METHOD_KEY = 'create'; export const ASYNC_METHOD_SUFFIX = 'Async'; export const CONFIGURABLE_MODULE_ID = 'CONFIGURABLE_MODULE_ID'; + +/** + * List of keys that are specific to ConfigurableModuleAsyncOptions + * and should be excluded when extracting user-defined extras. + */ +export const ASYNC_OPTIONS_METADATA_KEYS = [ + 'useFactory', + 'useClass', + 'useExisting', + 'inject', + 'imports', + 'provideInjectionTokensFrom', +] as const; diff --git a/packages/common/test/module-utils/configurable-module.builder.spec.ts b/packages/common/test/module-utils/configurable-module.builder.spec.ts index 756c01d2556..26f9c19ed33 100644 --- a/packages/common/test/module-utils/configurable-module.builder.spec.ts +++ b/packages/common/test/module-utils/configurable-module.builder.spec.ts @@ -24,6 +24,34 @@ describe('ConfigurableModuleBuilder', () => { global: true, }); }); + + it('should preserve extras in registerAsync transformation', () => { + let capturedExtras: any; + + const { ConfigurableModuleClass } = new ConfigurableModuleBuilder() + .setExtras( + { folder: 'default' }, + (definition, extras: { folder: string }) => { + capturedExtras = extras; + return { + ...definition, + customProperty: `folder: ${extras.folder}`, + }; + }, + ) + .build(); + + const asyncResult = ConfigurableModuleClass.registerAsync({ + useFactory: () => ({}), + folder: 'forRootAsync', + }); + + expect(capturedExtras).to.deep.equal({ folder: 'forRootAsync' }); + expect(asyncResult).to.have.property( + 'customProperty', + 'folder: forRootAsync', + ); + }); }); describe('setClassMethodName', () => { it('should set static class method name and return typed builder', () => {