create-forgeon 0.1.33 → 0.1.35
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/db-prisma.mjs +401 -0
- package/src/modules/executor.mjs +2 -0
- package/src/modules/executor.test.mjs +414 -6
- package/src/modules/i18n.mjs +244 -22
- package/src/modules/logger.mjs +76 -27
- package/src/modules/registry.mjs +8 -0
- package/src/modules/swagger.mjs +14 -4
- package/templates/module-fragments/db-prisma/00_title.md +6 -0
- package/templates/module-fragments/db-prisma/10_overview.md +10 -0
- package/templates/module-fragments/db-prisma/20_scope.md +14 -0
- package/templates/module-fragments/db-prisma/90_status_implemented.md +4 -0
- package/templates/module-presets/swagger/packages/swagger/src/setup-swagger.ts +1 -1
|
@@ -11,11 +11,136 @@ function mkTmp(prefix) {
|
|
|
11
11
|
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
function createMinimalForgeonProject(targetRoot) {
|
|
15
|
-
fs.mkdirSync(path.join(targetRoot, 'apps', 'api'), { recursive: true });
|
|
16
|
-
fs.writeFileSync(path.join(targetRoot, 'package.json'), '{"name":"demo"}\n', 'utf8');
|
|
17
|
-
fs.writeFileSync(path.join(targetRoot, 'pnpm-workspace.yaml'), 'packages:\n - apps/*\n', 'utf8');
|
|
18
|
-
}
|
|
14
|
+
function createMinimalForgeonProject(targetRoot) {
|
|
15
|
+
fs.mkdirSync(path.join(targetRoot, 'apps', 'api'), { recursive: true });
|
|
16
|
+
fs.writeFileSync(path.join(targetRoot, 'package.json'), '{"name":"demo"}\n', 'utf8');
|
|
17
|
+
fs.writeFileSync(path.join(targetRoot, 'pnpm-workspace.yaml'), 'packages:\n - apps/*\n', 'utf8');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function assertDbPrismaWiring(projectRoot) {
|
|
21
|
+
const appModule = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts'), 'utf8');
|
|
22
|
+
assert.match(appModule, /dbPrismaConfig/);
|
|
23
|
+
assert.match(appModule, /dbPrismaEnvSchema/);
|
|
24
|
+
assert.match(appModule, /DbPrismaModule/);
|
|
25
|
+
|
|
26
|
+
const apiPackage = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'package.json'), 'utf8');
|
|
27
|
+
assert.match(apiPackage, /@forgeon\/db-prisma/);
|
|
28
|
+
|
|
29
|
+
const apiDockerfile = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'Dockerfile'), 'utf8');
|
|
30
|
+
assert.match(apiDockerfile, /COPY packages\/db-prisma\/package\.json packages\/db-prisma\/package\.json/);
|
|
31
|
+
assert.match(apiDockerfile, /COPY packages\/db-prisma packages\/db-prisma/);
|
|
32
|
+
assert.match(apiDockerfile, /RUN pnpm --filter @forgeon\/db-prisma build/);
|
|
33
|
+
|
|
34
|
+
const compose = fs.readFileSync(path.join(projectRoot, 'infra', 'docker', 'compose.yml'), 'utf8');
|
|
35
|
+
assert.match(compose, /DATABASE_URL: \$\{DATABASE_URL\}/);
|
|
36
|
+
|
|
37
|
+
const healthController = fs.readFileSync(
|
|
38
|
+
path.join(projectRoot, 'apps', 'api', 'src', 'health', 'health.controller.ts'),
|
|
39
|
+
'utf8',
|
|
40
|
+
);
|
|
41
|
+
assert.match(healthController, /PrismaService/);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function stripDbPrismaArtifacts(projectRoot) {
|
|
45
|
+
const dbPackageDir = path.join(projectRoot, 'packages', 'db-prisma');
|
|
46
|
+
if (fs.existsSync(dbPackageDir)) {
|
|
47
|
+
fs.rmSync(dbPackageDir, { recursive: true, force: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const prismaDir = path.join(projectRoot, 'apps', 'api', 'prisma');
|
|
51
|
+
if (fs.existsSync(prismaDir)) {
|
|
52
|
+
fs.rmSync(prismaDir, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const apiPackagePath = path.join(projectRoot, 'apps', 'api', 'package.json');
|
|
56
|
+
const apiPackage = JSON.parse(fs.readFileSync(apiPackagePath, 'utf8'));
|
|
57
|
+
if (apiPackage.dependencies) {
|
|
58
|
+
delete apiPackage.dependencies['@forgeon/db-prisma'];
|
|
59
|
+
delete apiPackage.dependencies['@prisma/client'];
|
|
60
|
+
}
|
|
61
|
+
if (apiPackage.devDependencies) {
|
|
62
|
+
delete apiPackage.devDependencies.prisma;
|
|
63
|
+
}
|
|
64
|
+
if (apiPackage.scripts) {
|
|
65
|
+
for (const key of Object.keys(apiPackage.scripts)) {
|
|
66
|
+
if (key.startsWith('prisma:')) {
|
|
67
|
+
delete apiPackage.scripts[key];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (typeof apiPackage.scripts.predev === 'string') {
|
|
71
|
+
apiPackage.scripts.predev = apiPackage.scripts.predev
|
|
72
|
+
.replace('pnpm --filter @forgeon/db-prisma build && ', '')
|
|
73
|
+
.replace(' && pnpm --filter @forgeon/db-prisma build', '')
|
|
74
|
+
.replace('pnpm --filter @forgeon/db-prisma build', '')
|
|
75
|
+
.trim();
|
|
76
|
+
if (apiPackage.scripts.predev.length === 0) {
|
|
77
|
+
delete apiPackage.scripts.predev;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
delete apiPackage.prisma;
|
|
82
|
+
fs.writeFileSync(apiPackagePath, `${JSON.stringify(apiPackage, null, 2)}\n`, 'utf8');
|
|
83
|
+
|
|
84
|
+
const rootPackagePath = path.join(projectRoot, 'package.json');
|
|
85
|
+
const rootPackage = JSON.parse(fs.readFileSync(rootPackagePath, 'utf8'));
|
|
86
|
+
if (rootPackage.scripts && typeof rootPackage.scripts.postinstall === 'string') {
|
|
87
|
+
rootPackage.scripts.postinstall = rootPackage.scripts.postinstall
|
|
88
|
+
.replace(/\s*&&\s*pnpm --filter @forgeon\/api prisma:generate/g, '')
|
|
89
|
+
.replace(/pnpm --filter @forgeon\/api prisma:generate\s*&&\s*/g, '')
|
|
90
|
+
.trim();
|
|
91
|
+
if (rootPackage.scripts.postinstall.length === 0) {
|
|
92
|
+
delete rootPackage.scripts.postinstall;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
fs.writeFileSync(rootPackagePath, `${JSON.stringify(rootPackage, null, 2)}\n`, 'utf8');
|
|
96
|
+
|
|
97
|
+
const appModulePath = path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts');
|
|
98
|
+
let appModule = fs.readFileSync(appModulePath, 'utf8');
|
|
99
|
+
appModule = appModule
|
|
100
|
+
.replace(/^import \{ dbPrismaConfig, dbPrismaEnvSchema, DbPrismaModule \} from '@forgeon\/db-prisma';\r?\n/m, '')
|
|
101
|
+
.replace(/,\s*dbPrismaConfig/g, '')
|
|
102
|
+
.replace(/dbPrismaConfig,\s*/g, '')
|
|
103
|
+
.replace(/,\s*dbPrismaEnvSchema/g, '')
|
|
104
|
+
.replace(/dbPrismaEnvSchema,\s*/g, '')
|
|
105
|
+
.replace(/^\s*DbPrismaModule,\r?\n/gm, '');
|
|
106
|
+
fs.writeFileSync(appModulePath, appModule, 'utf8');
|
|
107
|
+
|
|
108
|
+
const healthControllerPath = path.join(projectRoot, 'apps', 'api', 'src', 'health', 'health.controller.ts');
|
|
109
|
+
let healthController = fs.readFileSync(healthControllerPath, 'utf8');
|
|
110
|
+
healthController = healthController
|
|
111
|
+
.replace(/^import \{ PrismaService \} from '@forgeon\/db-prisma';\r?\n/m, '')
|
|
112
|
+
.replace(/\s*private readonly prisma: PrismaService,\r?\n/g, '\n')
|
|
113
|
+
.replace(
|
|
114
|
+
/\s*@Post\('db'\)\s*async getDbProbe\(\)\s*\{[\s\S]*?\n\s*\}\r?\n/g,
|
|
115
|
+
'\n',
|
|
116
|
+
);
|
|
117
|
+
fs.writeFileSync(healthControllerPath, healthController, 'utf8');
|
|
118
|
+
|
|
119
|
+
const apiDockerfilePath = path.join(projectRoot, 'apps', 'api', 'Dockerfile');
|
|
120
|
+
let apiDockerfile = fs.readFileSync(apiDockerfilePath, 'utf8');
|
|
121
|
+
apiDockerfile = apiDockerfile
|
|
122
|
+
.replace(/^COPY apps\/api\/prisma apps\/api\/prisma\r?\n/gm, '')
|
|
123
|
+
.replace(/^COPY packages\/db-prisma\/package\.json packages\/db-prisma\/package\.json\r?\n/gm, '')
|
|
124
|
+
.replace(/^COPY packages\/db-prisma packages\/db-prisma\r?\n/gm, '')
|
|
125
|
+
.replace(/^RUN pnpm --filter @forgeon\/db-prisma build\r?\n/gm, '')
|
|
126
|
+
.replace(/^RUN pnpm --filter @forgeon\/api prisma:generate\r?\n/gm, '');
|
|
127
|
+
fs.writeFileSync(apiDockerfilePath, apiDockerfile, 'utf8');
|
|
128
|
+
|
|
129
|
+
const composePath = path.join(projectRoot, 'infra', 'docker', 'compose.yml');
|
|
130
|
+
let compose = fs.readFileSync(composePath, 'utf8');
|
|
131
|
+
compose = compose.replace(/^\s+DATABASE_URL:.*\r?\n/gm, '');
|
|
132
|
+
fs.writeFileSync(composePath, compose, 'utf8');
|
|
133
|
+
|
|
134
|
+
const apiEnvExamplePath = path.join(projectRoot, 'apps', 'api', '.env.example');
|
|
135
|
+
let apiEnv = fs.readFileSync(apiEnvExamplePath, 'utf8');
|
|
136
|
+
apiEnv = apiEnv.replace(/^DATABASE_URL=.*\r?\n/gm, '');
|
|
137
|
+
fs.writeFileSync(apiEnvExamplePath, apiEnv, 'utf8');
|
|
138
|
+
|
|
139
|
+
const dockerEnvExamplePath = path.join(projectRoot, 'infra', 'docker', '.env.example');
|
|
140
|
+
let dockerEnv = fs.readFileSync(dockerEnvExamplePath, 'utf8');
|
|
141
|
+
dockerEnv = dockerEnv.replace(/^DATABASE_URL=.*\r?\n/gm, '');
|
|
142
|
+
fs.writeFileSync(dockerEnvExamplePath, dockerEnv, 'utf8');
|
|
143
|
+
}
|
|
19
144
|
|
|
20
145
|
describe('addModule', () => {
|
|
21
146
|
const modulesDir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -419,7 +544,7 @@ describe('addModule', () => {
|
|
|
419
544
|
const rootReadme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
420
545
|
assert.match(rootReadme, /## Swagger \/ OpenAPI Module/);
|
|
421
546
|
assert.match(rootReadme, /SWAGGER_ENABLED=false/);
|
|
422
|
-
assert.match(rootReadme, /localhost:3000\/docs/);
|
|
547
|
+
assert.match(rootReadme, /localhost:3000\/api\/docs/);
|
|
423
548
|
|
|
424
549
|
const moduleDoc = fs.readFileSync(result.docsPath, 'utf8');
|
|
425
550
|
assert.match(moduleDoc, /Swagger \/ OpenAPI/);
|
|
@@ -466,4 +591,287 @@ describe('addModule', () => {
|
|
|
466
591
|
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
467
592
|
}
|
|
468
593
|
});
|
|
594
|
+
|
|
595
|
+
it('applies logger after swagger without losing logger config keys', () => {
|
|
596
|
+
const targetRoot = mkTmp('forgeon-module-swagger-logger-');
|
|
597
|
+
const projectRoot = path.join(targetRoot, 'demo-swagger-logger');
|
|
598
|
+
const templateRoot = path.join(packageRoot, 'templates', 'base');
|
|
599
|
+
|
|
600
|
+
try {
|
|
601
|
+
scaffoldProject({
|
|
602
|
+
templateRoot,
|
|
603
|
+
packageRoot,
|
|
604
|
+
targetRoot: projectRoot,
|
|
605
|
+
projectName: 'demo-swagger-logger',
|
|
606
|
+
frontend: 'react',
|
|
607
|
+
db: 'prisma',
|
|
608
|
+
i18nEnabled: true,
|
|
609
|
+
proxy: 'caddy',
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
const swaggerResult = addModule({
|
|
613
|
+
moduleId: 'swagger',
|
|
614
|
+
targetRoot: projectRoot,
|
|
615
|
+
packageRoot,
|
|
616
|
+
});
|
|
617
|
+
assert.equal(swaggerResult.applied, true);
|
|
618
|
+
|
|
619
|
+
const loggerResult = addModule({
|
|
620
|
+
moduleId: 'logger',
|
|
621
|
+
targetRoot: projectRoot,
|
|
622
|
+
packageRoot,
|
|
623
|
+
});
|
|
624
|
+
assert.equal(loggerResult.applied, true);
|
|
625
|
+
|
|
626
|
+
const appModule = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts'), 'utf8');
|
|
627
|
+
assert.match(
|
|
628
|
+
appModule,
|
|
629
|
+
/load: \[coreConfig,\s*dbPrismaConfig,\s*i18nConfig,\s*swaggerConfig,\s*loggerConfig\]/,
|
|
630
|
+
);
|
|
631
|
+
assert.match(
|
|
632
|
+
appModule,
|
|
633
|
+
/validate: createEnvValidator\(\[coreEnvSchema,\s*dbPrismaEnvSchema,\s*i18nEnvSchema,\s*swaggerEnvSchema,\s*loggerEnvSchema\]\)/,
|
|
634
|
+
);
|
|
635
|
+
assert.match(appModule, /ForgeonSwaggerModule/);
|
|
636
|
+
assert.match(appModule, /ForgeonLoggerModule/);
|
|
637
|
+
} finally {
|
|
638
|
+
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('applies i18n after logger without losing logger config keys', () => {
|
|
643
|
+
const targetRoot = mkTmp('forgeon-module-logger-i18n-');
|
|
644
|
+
const projectRoot = path.join(targetRoot, 'demo-logger-i18n');
|
|
645
|
+
const templateRoot = path.join(packageRoot, 'templates', 'base');
|
|
646
|
+
|
|
647
|
+
try {
|
|
648
|
+
scaffoldProject({
|
|
649
|
+
templateRoot,
|
|
650
|
+
packageRoot,
|
|
651
|
+
targetRoot: projectRoot,
|
|
652
|
+
projectName: 'demo-logger-i18n',
|
|
653
|
+
frontend: 'react',
|
|
654
|
+
db: 'prisma',
|
|
655
|
+
i18nEnabled: false,
|
|
656
|
+
proxy: 'caddy',
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
addModule({
|
|
660
|
+
moduleId: 'logger',
|
|
661
|
+
targetRoot: projectRoot,
|
|
662
|
+
packageRoot,
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
const i18nResult = addModule({
|
|
666
|
+
moduleId: 'i18n',
|
|
667
|
+
targetRoot: projectRoot,
|
|
668
|
+
packageRoot,
|
|
669
|
+
});
|
|
670
|
+
assert.equal(i18nResult.applied, true);
|
|
671
|
+
|
|
672
|
+
const appModule = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts'), 'utf8');
|
|
673
|
+
assert.match(
|
|
674
|
+
appModule,
|
|
675
|
+
/load: \[coreConfig,\s*dbPrismaConfig,\s*loggerConfig,\s*i18nConfig\]/,
|
|
676
|
+
);
|
|
677
|
+
assert.match(
|
|
678
|
+
appModule,
|
|
679
|
+
/validate: createEnvValidator\(\[coreEnvSchema,\s*dbPrismaEnvSchema,\s*loggerEnvSchema,\s*i18nEnvSchema\]\)/,
|
|
680
|
+
);
|
|
681
|
+
assert.match(appModule, /ForgeonLoggerModule/);
|
|
682
|
+
assert.match(appModule, /ForgeonI18nModule/);
|
|
683
|
+
|
|
684
|
+
const apiPackage = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'package.json'), 'utf8');
|
|
685
|
+
assert.match(apiPackage, /@forgeon\/logger/);
|
|
686
|
+
assert.match(apiPackage, /@forgeon\/i18n/);
|
|
687
|
+
assert.match(apiPackage, /pnpm --filter @forgeon\/logger build/);
|
|
688
|
+
assert.match(apiPackage, /pnpm --filter @forgeon\/i18n build/);
|
|
689
|
+
|
|
690
|
+
const mainTs = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'main.ts'), 'utf8');
|
|
691
|
+
assert.match(mainTs, /ForgeonLoggerService/);
|
|
692
|
+
assert.match(mainTs, /ForgeonHttpLoggingInterceptor/);
|
|
693
|
+
} finally {
|
|
694
|
+
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
it('applies i18n after swagger without losing swagger config keys', () => {
|
|
699
|
+
const targetRoot = mkTmp('forgeon-module-swagger-i18n-order-');
|
|
700
|
+
const projectRoot = path.join(targetRoot, 'demo-swagger-i18n-order');
|
|
701
|
+
const templateRoot = path.join(packageRoot, 'templates', 'base');
|
|
702
|
+
|
|
703
|
+
try {
|
|
704
|
+
scaffoldProject({
|
|
705
|
+
templateRoot,
|
|
706
|
+
packageRoot,
|
|
707
|
+
targetRoot: projectRoot,
|
|
708
|
+
projectName: 'demo-swagger-i18n-order',
|
|
709
|
+
frontend: 'react',
|
|
710
|
+
db: 'prisma',
|
|
711
|
+
i18nEnabled: false,
|
|
712
|
+
proxy: 'caddy',
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
addModule({
|
|
716
|
+
moduleId: 'swagger',
|
|
717
|
+
targetRoot: projectRoot,
|
|
718
|
+
packageRoot,
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
const i18nResult = addModule({
|
|
722
|
+
moduleId: 'i18n',
|
|
723
|
+
targetRoot: projectRoot,
|
|
724
|
+
packageRoot,
|
|
725
|
+
});
|
|
726
|
+
assert.equal(i18nResult.applied, true);
|
|
727
|
+
|
|
728
|
+
const appModule = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts'), 'utf8');
|
|
729
|
+
assert.match(
|
|
730
|
+
appModule,
|
|
731
|
+
/load: \[coreConfig,\s*dbPrismaConfig,\s*swaggerConfig,\s*i18nConfig\]/,
|
|
732
|
+
);
|
|
733
|
+
assert.match(
|
|
734
|
+
appModule,
|
|
735
|
+
/validate: createEnvValidator\(\[coreEnvSchema,\s*dbPrismaEnvSchema,\s*swaggerEnvSchema,\s*i18nEnvSchema\]\)/,
|
|
736
|
+
);
|
|
737
|
+
assert.match(appModule, /ForgeonSwaggerModule/);
|
|
738
|
+
assert.match(appModule, /ForgeonI18nModule/);
|
|
739
|
+
|
|
740
|
+
const apiPackage = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'package.json'), 'utf8');
|
|
741
|
+
assert.match(apiPackage, /@forgeon\/swagger/);
|
|
742
|
+
assert.match(apiPackage, /@forgeon\/i18n/);
|
|
743
|
+
assert.match(apiPackage, /pnpm --filter @forgeon\/swagger build/);
|
|
744
|
+
assert.match(apiPackage, /pnpm --filter @forgeon\/i18n build/);
|
|
745
|
+
|
|
746
|
+
const mainTs = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'main.ts'), 'utf8');
|
|
747
|
+
assert.match(mainTs, /setupSwagger/);
|
|
748
|
+
assert.match(mainTs, /SwaggerConfigService/);
|
|
749
|
+
} finally {
|
|
750
|
+
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
751
|
+
}
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
it('applies swagger -> logger -> i18n and keeps all module wiring', () => {
|
|
755
|
+
const targetRoot = mkTmp('forgeon-module-mixed-order-');
|
|
756
|
+
const projectRoot = path.join(targetRoot, 'demo-mixed-order');
|
|
757
|
+
const templateRoot = path.join(packageRoot, 'templates', 'base');
|
|
758
|
+
|
|
759
|
+
try {
|
|
760
|
+
scaffoldProject({
|
|
761
|
+
templateRoot,
|
|
762
|
+
packageRoot,
|
|
763
|
+
targetRoot: projectRoot,
|
|
764
|
+
projectName: 'demo-mixed-order',
|
|
765
|
+
frontend: 'react',
|
|
766
|
+
db: 'prisma',
|
|
767
|
+
i18nEnabled: false,
|
|
768
|
+
proxy: 'caddy',
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
addModule({ moduleId: 'swagger', targetRoot: projectRoot, packageRoot });
|
|
772
|
+
addModule({ moduleId: 'logger', targetRoot: projectRoot, packageRoot });
|
|
773
|
+
addModule({ moduleId: 'i18n', targetRoot: projectRoot, packageRoot });
|
|
774
|
+
|
|
775
|
+
const appModule = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts'), 'utf8');
|
|
776
|
+
assert.match(
|
|
777
|
+
appModule,
|
|
778
|
+
/load: \[coreConfig,\s*dbPrismaConfig,\s*swaggerConfig,\s*loggerConfig,\s*i18nConfig\]/,
|
|
779
|
+
);
|
|
780
|
+
assert.match(
|
|
781
|
+
appModule,
|
|
782
|
+
/validate: createEnvValidator\(\[coreEnvSchema,\s*dbPrismaEnvSchema,\s*swaggerEnvSchema,\s*loggerEnvSchema,\s*i18nEnvSchema\]\)/,
|
|
783
|
+
);
|
|
784
|
+
assert.match(appModule, /ForgeonSwaggerModule/);
|
|
785
|
+
assert.match(appModule, /ForgeonLoggerModule/);
|
|
786
|
+
assert.match(appModule, /ForgeonI18nModule/);
|
|
787
|
+
|
|
788
|
+
const mainTs = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'main.ts'), 'utf8');
|
|
789
|
+
assert.match(mainTs, /setupSwagger\(app,\s*swaggerConfigService\)/);
|
|
790
|
+
assert.match(mainTs, /app\.useLogger\(app\.get\(ForgeonLoggerService\)\);/);
|
|
791
|
+
assert.match(mainTs, /app\.useGlobalInterceptors\(app\.get\(ForgeonHttpLoggingInterceptor\)\);/);
|
|
792
|
+
|
|
793
|
+
const apiPackage = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'package.json'), 'utf8');
|
|
794
|
+
assert.match(apiPackage, /@forgeon\/swagger/);
|
|
795
|
+
assert.match(apiPackage, /@forgeon\/logger/);
|
|
796
|
+
assert.match(apiPackage, /@forgeon\/i18n/);
|
|
797
|
+
assert.match(apiPackage, /pnpm --filter @forgeon\/swagger build/);
|
|
798
|
+
assert.match(apiPackage, /pnpm --filter @forgeon\/logger build/);
|
|
799
|
+
assert.match(apiPackage, /pnpm --filter @forgeon\/i18n build/);
|
|
800
|
+
|
|
801
|
+
assertDbPrismaWiring(projectRoot);
|
|
802
|
+
} finally {
|
|
803
|
+
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
it('keeps db-prisma wiring across module installation orders', () => {
|
|
808
|
+
const sequences = [
|
|
809
|
+
['logger', 'swagger', 'i18n'],
|
|
810
|
+
['swagger', 'i18n', 'logger'],
|
|
811
|
+
['i18n', 'logger', 'swagger'],
|
|
812
|
+
];
|
|
813
|
+
|
|
814
|
+
for (const sequence of sequences) {
|
|
815
|
+
const targetRoot = mkTmp(`forgeon-module-db-order-${sequence.join('-')}-`);
|
|
816
|
+
const projectRoot = path.join(targetRoot, `demo-db-${sequence.join('-')}`);
|
|
817
|
+
const templateRoot = path.join(packageRoot, 'templates', 'base');
|
|
818
|
+
|
|
819
|
+
try {
|
|
820
|
+
scaffoldProject({
|
|
821
|
+
templateRoot,
|
|
822
|
+
packageRoot,
|
|
823
|
+
targetRoot: projectRoot,
|
|
824
|
+
projectName: `demo-db-${sequence.join('-')}`,
|
|
825
|
+
frontend: 'react',
|
|
826
|
+
db: 'prisma',
|
|
827
|
+
i18nEnabled: false,
|
|
828
|
+
proxy: 'caddy',
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
for (const moduleId of sequence) {
|
|
832
|
+
addModule({ moduleId, targetRoot: projectRoot, packageRoot });
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
assertDbPrismaWiring(projectRoot);
|
|
836
|
+
} finally {
|
|
837
|
+
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
it('applies db-prisma as final module after other modules', () => {
|
|
843
|
+
const targetRoot = mkTmp('forgeon-module-db-last-');
|
|
844
|
+
const projectRoot = path.join(targetRoot, 'demo-db-last');
|
|
845
|
+
const templateRoot = path.join(packageRoot, 'templates', 'base');
|
|
846
|
+
|
|
847
|
+
try {
|
|
848
|
+
scaffoldProject({
|
|
849
|
+
templateRoot,
|
|
850
|
+
packageRoot,
|
|
851
|
+
targetRoot: projectRoot,
|
|
852
|
+
projectName: 'demo-db-last',
|
|
853
|
+
frontend: 'react',
|
|
854
|
+
db: 'prisma',
|
|
855
|
+
i18nEnabled: false,
|
|
856
|
+
proxy: 'caddy',
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
stripDbPrismaArtifacts(projectRoot);
|
|
860
|
+
|
|
861
|
+
addModule({ moduleId: 'logger', targetRoot: projectRoot, packageRoot });
|
|
862
|
+
addModule({ moduleId: 'swagger', targetRoot: projectRoot, packageRoot });
|
|
863
|
+
addModule({ moduleId: 'i18n', targetRoot: projectRoot, packageRoot });
|
|
864
|
+
const dbResult = addModule({ moduleId: 'db-prisma', targetRoot: projectRoot, packageRoot });
|
|
865
|
+
assert.equal(dbResult.applied, true);
|
|
866
|
+
|
|
867
|
+
assertDbPrismaWiring(projectRoot);
|
|
868
|
+
|
|
869
|
+
const appModule = fs.readFileSync(path.join(projectRoot, 'apps', 'api', 'src', 'app.module.ts'), 'utf8');
|
|
870
|
+
assert.match(appModule, /ForgeonLoggerModule/);
|
|
871
|
+
assert.match(appModule, /ForgeonSwaggerModule/);
|
|
872
|
+
assert.match(appModule, /ForgeonI18nModule/);
|
|
873
|
+
} finally {
|
|
874
|
+
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
875
|
+
}
|
|
876
|
+
});
|
|
469
877
|
});
|