create-forgeon 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-forgeon",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "Forgeon project generator CLI",
5
5
  "license": "MIT",
6
6
  "author": "Forgeon",
@@ -110,9 +110,23 @@ describe('addModule', () => {
110
110
 
111
111
  const appModule = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts'), 'utf8');
112
112
  assert.match(appModule, /coreConfig/);
113
- assert.match(appModule, /validateCoreEnv/);
113
+ assert.match(appModule, /createEnvValidator/);
114
+ assert.match(appModule, /coreEnvSchema/);
115
+ assert.match(appModule, /i18nConfig/);
116
+ assert.match(appModule, /i18nEnvSchema/);
114
117
  assert.match(appModule, /CoreConfigModule/);
115
118
 
119
+ const forgeonI18nModule = fs.readFileSync(
120
+ path.join(projectRoot, 'packages', 'i18n', 'src', 'forgeon-i18n.module.ts'),
121
+ 'utf8',
122
+ );
123
+ assert.match(forgeonI18nModule, /const resolvers = \[/);
124
+ assert.match(forgeonI18nModule, /I18nModule\.forRootAsync\([\s\S]*resolvers,/);
125
+ assert.doesNotMatch(
126
+ forgeonI18nModule,
127
+ /exports:\s*\[I18nModule,\s*I18nConfigModule,\s*I18nConfigService\]/,
128
+ );
129
+
116
130
  const appTsx = fs.readFileSync(path.join(projectRoot, 'apps', 'web', 'src', 'App.tsx'), 'utf8');
117
131
  assert.match(appTsx, /@forgeon\/i18n-web/);
118
132
  assert.match(appTsx, /react-i18next/);
@@ -1,31 +1,31 @@
1
1
  import { Module } from '@nestjs/common';
2
2
  import { ConfigModule } from '@nestjs/config';
3
- import { CoreConfigModule, coreConfig, validateCoreEnv } from '@forgeon/core';
4
- import { ForgeonI18nModule } from '@forgeon/i18n';
3
+ import {
4
+ CoreConfigModule,
5
+ coreConfig,
6
+ coreEnvSchema,
7
+ createEnvValidator,
8
+ } from '@forgeon/core';
9
+ import { ForgeonI18nModule, i18nConfig, i18nEnvSchema } from '@forgeon/i18n';
5
10
  import { join } from 'path';
6
11
  import { HealthController } from './health/health.controller';
7
12
  import { PrismaModule } from './prisma/prisma.module';
8
13
  import { AppExceptionFilter } from './common/filters/app-exception.filter';
9
14
 
10
- const i18nDefaultLang = process.env.I18N_DEFAULT_LANG ?? 'en';
11
- const i18nFallbackLang = process.env.I18N_FALLBACK_LANG ?? 'en';
12
15
  const i18nPath = join(__dirname, '..', '..', '..', 'resources', 'i18n');
13
16
 
14
17
  @Module({
15
18
  imports: [
16
19
  ConfigModule.forRoot({
17
20
  isGlobal: true,
18
- load: [coreConfig],
19
- validate: validateCoreEnv,
21
+ load: [coreConfig, i18nConfig],
22
+ validate: createEnvValidator([coreEnvSchema, i18nEnvSchema]),
20
23
  envFilePath: '.env',
21
24
  }),
22
25
  CoreConfigModule,
23
26
  ForgeonI18nModule.register({
24
- enabled: true,
25
- defaultLang: i18nDefaultLang,
26
- fallbackLang: i18nFallbackLang,
27
27
  path: i18nPath,
28
- }),
28
+ }),
29
29
  PrismaModule,
30
30
  ],
31
31
  controllers: [HealthController],
@@ -7,10 +7,12 @@
7
7
  "scripts": {
8
8
  "build": "tsc -p tsconfig.json"
9
9
  },
10
- "dependencies": {
11
- "@nestjs/common": "^11.0.1",
12
- "nestjs-i18n": "^10.5.1"
13
- },
10
+ "dependencies": {
11
+ "@nestjs/config": "^4.0.2",
12
+ "@nestjs/common": "^11.0.1",
13
+ "nestjs-i18n": "^10.5.1",
14
+ "zod": "^3.23.8"
15
+ },
14
16
  "devDependencies": {
15
17
  "@types/node": "^22.10.7",
16
18
  "typescript": "^5.7.3"
@@ -1,47 +1,47 @@
1
- import { DynamicModule, Module } from '@nestjs/common';
2
- import {
3
- AcceptLanguageResolver,
4
- I18nJsonLoader,
5
- I18nModule,
6
- QueryResolver,
7
- } from 'nestjs-i18n';
8
- import { join } from 'path';
9
-
10
- export interface ForgeonI18nOptions {
11
- enabled: boolean;
12
- defaultLang: string;
13
- fallbackLang: string;
14
- path?: string;
15
- }
16
-
17
- @Module({})
18
- export class ForgeonI18nModule {
19
- static register(options: ForgeonI18nOptions): DynamicModule {
20
- if (!options.enabled) {
21
- return { module: ForgeonI18nModule };
22
- }
23
-
24
- const translationsPath =
25
- options.path ?? join(process.cwd(), 'resources', 'i18n');
26
-
27
- return {
28
- module: ForgeonI18nModule,
29
- imports: [
30
- I18nModule.forRoot({
31
- fallbackLanguage: options.fallbackLang,
32
- loader: I18nJsonLoader,
33
- loaderOptions: {
34
- path: translationsPath,
35
- watch: false,
36
- },
37
- resolvers: [
38
- { use: AcceptLanguageResolver, options: { matchType: 'strict-loose' } },
39
- { use: QueryResolver, options: ['lang'] },
40
- ],
41
- }),
42
- ],
43
- exports: [I18nModule],
44
- };
45
- }
46
- }
1
+ import { DynamicModule, Module } from '@nestjs/common';
2
+ import {
3
+ AcceptLanguageResolver,
4
+ I18nJsonLoader,
5
+ I18nModule,
6
+ QueryResolver,
7
+ } from 'nestjs-i18n';
8
+ import { join } from 'path';
9
+ import { I18nConfigModule } from './i18n-config.module';
10
+ import { I18nConfigService } from './i18n-config.service';
11
+
12
+ export interface ForgeonI18nOptions {
13
+ path?: string;
14
+ }
15
+
16
+ @Module({})
17
+ export class ForgeonI18nModule {
18
+ static register(options: ForgeonI18nOptions = {}): DynamicModule {
19
+ const translationsPath = options.path ?? join(process.cwd(), 'resources', 'i18n');
20
+ const resolvers = [
21
+ { use: AcceptLanguageResolver, options: { matchType: 'strict-loose' } },
22
+ { use: QueryResolver, options: ['lang'] },
23
+ ];
24
+
25
+ return {
26
+ module: ForgeonI18nModule,
27
+ imports: [
28
+ I18nConfigModule,
29
+ I18nModule.forRootAsync({
30
+ imports: [I18nConfigModule],
31
+ inject: [I18nConfigService],
32
+ resolvers,
33
+ useFactory: (config: I18nConfigService) => ({
34
+ fallbackLanguage: config.fallbackLang,
35
+ loader: I18nJsonLoader,
36
+ loaderOptions: {
37
+ path: translationsPath,
38
+ watch: false,
39
+ },
40
+ }),
41
+ }),
42
+ ],
43
+ exports: [I18nModule, I18nConfigModule],
44
+ };
45
+ }
46
+ }
47
47
 
@@ -0,0 +1,20 @@
1
+ import { registerAs } from '@nestjs/config';
2
+ import { parseI18nEnv } from './i18n-env.schema';
3
+
4
+ export const I18N_CONFIG_NAMESPACE = 'i18n';
5
+
6
+ export interface I18nConfigValues {
7
+ defaultLang: string;
8
+ fallbackLang: string;
9
+ }
10
+
11
+ export const i18nConfig = registerAs(
12
+ I18N_CONFIG_NAMESPACE,
13
+ (): I18nConfigValues => {
14
+ const env = parseI18nEnv(process.env);
15
+ return {
16
+ defaultLang: env.I18N_DEFAULT_LANG,
17
+ fallbackLang: env.I18N_FALLBACK_LANG,
18
+ };
19
+ },
20
+ );
@@ -0,0 +1,10 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { ConfigModule } from '@nestjs/config';
3
+ import { I18nConfigService } from './i18n-config.service';
4
+
5
+ @Module({
6
+ imports: [ConfigModule],
7
+ providers: [I18nConfigService],
8
+ exports: [I18nConfigService],
9
+ })
10
+ export class I18nConfigModule {}
@@ -0,0 +1,20 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { ConfigService } from '@nestjs/config';
3
+ import { I18N_CONFIG_NAMESPACE } from './i18n-config.loader';
4
+
5
+ @Injectable()
6
+ export class I18nConfigService {
7
+ constructor(private readonly configService: ConfigService) {}
8
+
9
+ get defaultLang(): string {
10
+ return this.configService.getOrThrow<string>(
11
+ `${I18N_CONFIG_NAMESPACE}.defaultLang`,
12
+ );
13
+ }
14
+
15
+ get fallbackLang(): string {
16
+ return this.configService.getOrThrow<string>(
17
+ `${I18N_CONFIG_NAMESPACE}.fallbackLang`,
18
+ );
19
+ }
20
+ }
@@ -0,0 +1,14 @@
1
+ import { z } from 'zod';
2
+
3
+ export const i18nEnvSchema = z
4
+ .object({
5
+ I18N_DEFAULT_LANG: z.string().trim().min(1).default('en'),
6
+ I18N_FALLBACK_LANG: z.string().trim().min(1).default('en'),
7
+ })
8
+ .passthrough();
9
+
10
+ export type I18nEnv = z.infer<typeof i18nEnvSchema>;
11
+
12
+ export function parseI18nEnv(input: Record<string, unknown>): I18nEnv {
13
+ return i18nEnvSchema.parse(input);
14
+ }
@@ -1,2 +1,6 @@
1
- export * from './forgeon-i18n.module';
1
+ export * from './forgeon-i18n.module';
2
+ export * from './i18n-env.schema';
3
+ export * from './i18n-config.loader';
4
+ export * from './i18n-config.module';
5
+ export * from './i18n-config.service';
2
6