Skip to content

Commit d98a5f8

Browse files
committed
feat: create a conditional module
With the new `ConditionalModule` devs can now register modules based on keys in the `process.env` or have them registered as blanks modules under the guise of a dyanmic module. This works for nested modules, multiple imports, and dynamic modules. Signed-off-by: Jay McDoniel <jmcdo29@gmail.com>
1 parent 3fe4ea0 commit d98a5f8

File tree

4 files changed

+160
-0
lines changed

4 files changed

+160
-0
lines changed

lib/conditional.module.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { ModuleMetadata } from '@nestjs/common';
2+
import { ConfigModule } from './config.module';
3+
4+
export class ConditionalModule {
5+
static async registerWhen(
6+
module: Required<ModuleMetadata>['imports'][number],
7+
condition: string | ((env: NodeJS.ProcessEnv) => boolean),
8+
) {
9+
if (typeof condition === 'string') {
10+
const key = condition;
11+
condition = env => {
12+
return env[key]?.toLowerCase() !== 'false';
13+
};
14+
}
15+
await ConfigModule.envVariablesLoaded;
16+
return condition(process.env)
17+
? {
18+
module: ConditionalModule,
19+
imports: [module],
20+
exports: [module],
21+
}
22+
: { module: ConditionalModule, imports: [] };
23+
}
24+
}

lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './conditional.module';
12
export * from './config.module';
23
export * from './config.service';
34
export * from './types';

tests/e2e/.env.conditional

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FOO="use it"
2+
FOO_FALSE="false"
3+
FOO_DYNAMIC="yes"
4+
FOO_CUSTOM="yeah!"
5+
BAR="yay"
6+
FOOBAR="do it"
7+
QUU="nested!"

tests/e2e/conditional.module.spec.ts

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { Injectable, Module } from '@nestjs/common';
2+
import { Test } from '@nestjs/testing';
3+
import { ConfigModule, ConditionalModule } from '../../lib';
4+
import { join } from 'path';
5+
6+
@Injectable()
7+
class FooService {}
8+
9+
@Injectable()
10+
class FooDynamicService {}
11+
12+
@Module({
13+
providers: [FooService],
14+
exports: [FooService],
15+
})
16+
class FooModule {
17+
static forRoot() {
18+
return {
19+
module: FooModule,
20+
providers: [FooDynamicService],
21+
exports: [FooDynamicService],
22+
};
23+
}
24+
}
25+
26+
@Injectable()
27+
class BarService {}
28+
29+
@Module({
30+
providers: [BarService],
31+
exports: [BarService],
32+
})
33+
class BarModule {}
34+
35+
@Module({
36+
providers: [
37+
{
38+
provide: 'quu',
39+
useValue: 42,
40+
},
41+
],
42+
exports: ['quu'],
43+
})
44+
class QuuModule {}
45+
46+
@Module({
47+
imports: [ConditionalModule.registerWhen(QuuModule, 'QUU')],
48+
exports: [ConditionalModule],
49+
})
50+
class FooBarModule {}
51+
52+
describe('ConditionalModule', () => {
53+
it('it should work for a regular module', async () => {
54+
const modRef = await Test.createTestingModule({
55+
imports: [
56+
ConfigModule.forRoot({
57+
envFilePath: join(process.cwd(), 'tests', 'e2e', '.env.conditional'),
58+
}),
59+
ConditionalModule.registerWhen(FooModule, 'FOO'),
60+
],
61+
}).compile();
62+
expect(modRef.get(FooService, { strict: false })).toBeDefined();
63+
await modRef.close();
64+
});
65+
it('should work for a dynamic module', async () => {
66+
const modRef = await Test.createTestingModule({
67+
imports: [
68+
ConfigModule.forRoot({
69+
envFilePath: join(process.cwd(), 'tests', 'e2e', '.env.conditional'),
70+
}),
71+
ConditionalModule.registerWhen(FooModule.forRoot(), 'FOO_DYNAMIC'),
72+
],
73+
}).compile();
74+
expect(modRef.get(FooDynamicService, { strict: false })).toBeDefined();
75+
await modRef.close();
76+
});
77+
it('should not register when the value is false', async () => {
78+
const modRef = await Test.createTestingModule({
79+
imports: [
80+
ConfigModule.forRoot({
81+
envFilePath: join(process.cwd(), 'tests', 'e2e', '.env.conditional'),
82+
}),
83+
ConditionalModule.registerWhen(FooModule, 'FOO_FALSE'),
84+
],
85+
}).compile();
86+
expect(() => modRef.get(FooService, { strict: false })).toThrow();
87+
await modRef.close();
88+
});
89+
it('should work for a custom condition', async () => {
90+
const modRef = await Test.createTestingModule({
91+
imports: [
92+
ConfigModule.forRoot({
93+
envFilePath: join(process.cwd(), 'tests', 'e2e', '.env.conditional'),
94+
}),
95+
ConditionalModule.registerWhen(FooModule, env => {
96+
return env.FOO_CUSTOM === 'yeah!';
97+
}),
98+
],
99+
}).compile();
100+
expect(modRef.get(FooService, { strict: false })).toBeDefined();
101+
await modRef.close();
102+
});
103+
it('should handle two conditional modules', async () => {
104+
const modRef = await Test.createTestingModule({
105+
imports: [
106+
ConfigModule.forRoot({
107+
envFilePath: join(process.cwd(), 'tests', 'e2e', '.env.conditional'),
108+
}),
109+
ConditionalModule.registerWhen(FooModule, 'FOO'),
110+
ConditionalModule.registerWhen(BarModule, 'BAR'),
111+
],
112+
}).compile();
113+
expect(modRef.get(FooService, { strict: false })).toBeDefined();
114+
expect(modRef.get(BarService, { strict: false })).toBeDefined();
115+
await modRef.close();
116+
});
117+
it('should handle nested conditional module', async () => {
118+
const modRef = await Test.createTestingModule({
119+
imports: [
120+
ConfigModule.forRoot({
121+
envFilePath: join(process.cwd(), 'tests', 'e2e', '.env.conditional'),
122+
}),
123+
ConditionalModule.registerWhen(FooBarModule, 'FOOBAR'),
124+
],
125+
}).compile();
126+
expect(modRef.get('quu', { strict: false })).toBeDefined();
127+
});
128+
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy