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 +1 -1
- package/src/modules/executor.test.mjs +15 -1
- package/templates/base/apps/api/src/app.module.ts +10 -10
- package/templates/base/packages/i18n/package.json +6 -4
- package/templates/base/packages/i18n/src/forgeon-i18n.module.ts +46 -46
- package/templates/base/packages/i18n/src/i18n-config.loader.ts +20 -0
- package/templates/base/packages/i18n/src/i18n-config.module.ts +10 -0
- package/templates/base/packages/i18n/src/i18n-config.service.ts +20 -0
- package/templates/base/packages/i18n/src/i18n-env.schema.ts +14 -0
- package/templates/base/packages/i18n/src/index.ts +5 -1
package/package.json
CHANGED
|
@@ -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, /
|
|
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 {
|
|
4
|
-
|
|
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:
|
|
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/
|
|
12
|
-
"nestjs
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
+
}
|