UNPKG

@nestjs-mod/prisma

Version:

Next-generation Node.js and TypeScript ORM for NestJS-mod (preview version only for Postgres)

283 lines (281 loc) 17.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PrismaInfrastructureUpdaterService = void 0; const tslib_1 = require("tslib"); const common_1 = require("@nestjs-mod/common"); const common_2 = require("@nestjs/common"); const case_anything_1 = require("case-anything"); const connection_string_1 = require("connection-string"); const path_1 = require("path"); const prisma_errors_1 = require("../prisma-errors"); const prisma_configuration_1 = require("../prisma.configuration"); const prisma_environments_1 = require("../prisma.environments"); const prisma_infrastructure_constants_1 = require("./prisma-infrastructure.constants"); const prisma_schema_file_service_1 = require("./prisma-schema-file.service"); let PrismaInfrastructureUpdaterService = class PrismaInfrastructureUpdaterService { constructor(packageJsonService, nxProjectJsonService, prismaConfiguration, prismaEnvironments, prismaSchemaFileService, wrapApplicationOptionsService, dotEnvService) { this.packageJsonService = packageJsonService; this.nxProjectJsonService = nxProjectJsonService; this.prismaConfiguration = prismaConfiguration; this.prismaEnvironments = prismaEnvironments; this.prismaSchemaFileService = prismaSchemaFileService; this.wrapApplicationOptionsService = wrapApplicationOptionsService; this.dotEnvService = dotEnvService; } async onModuleInit() { await this.update(); } async update() { this.updatePackageJsonFile(); this.updateProjectJsonFile(); this.updatePrismaSchemaFile(); await this.updateDotEnvFile(); } updatePackageJsonFile() { const projectJson = this.nxProjectJsonService.read(this.prismaConfiguration.nxProjectJsonFile); if (projectJson) { const projectName = projectJson.name; const packageJson = this.packageJsonService.read(); if (packageJson) { this.packageJsonService.addScripts(prisma_infrastructure_constants_1.PRISMA_SCRIPTS_CATEGORY_NAME, { [`prisma:pull:${projectName}`]: { commands: [`./node_modules/.bin/nx run ${projectName}:prisma-pull`], comments: [`Generating a prisma schema based on a database for ${projectName}`], }, 'prisma:pull': { commands: ['./node_modules/.bin/nx run-many -t=prisma-pull'], comments: ['Generating a prisma schema based on a database'], }, 'prisma:generate': { commands: ['./node_modules/.bin/nx run-many -t=prisma-generate'], comments: ['Generation of client prisma schema of all applications and modules'], }, }, packageJson); if (this.prismaConfiguration.addMigrationScripts) { this.packageJsonService.addScripts(prisma_infrastructure_constants_1.PRISMA_SCRIPTS_CATEGORY_NAME, { [`prisma:migrate-dev-new:${projectName}`]: { commands: [`./node_modules/.bin/nx run ${projectName}:prisma-migrate-dev --create-only --name=new`], comments: [`Command to create new empty migration for ${projectName}`], }, [`prisma:migrate-dev:${projectName}`]: { commands: [`./node_modules/.bin/nx run ${projectName}:prisma-migrate-dev --create-only`], comments: [ `Alias for create new migration for ${projectName} (example: \`npm run prisma:migrate-dev:${projectName} --name=new)\``, ], }, [`prisma:migrate-deploy:${projectName}`]: { commands: [`./node_modules/.bin/nx run ${projectName}:prisma-migrate-deploy`], comments: [`Applying migrations for ${projectName}`], }, 'prisma:migrate-deploy': { commands: ['./node_modules/.bin/nx run-many -t=prisma-migrate-deploy'], comments: ['Applying migrations of all applications and modules'], }, }, packageJson); } this.packageJsonService.write(packageJson); } } } updateProjectJsonFile() { if (!this.prismaConfiguration.schemaFile) { throw new prisma_errors_1.PrismaError('prismaSchemaFile not set'); } const projectJson = this.nxProjectJsonService.read(this.prismaConfiguration.nxProjectJsonFile); const packageJsonFilePath = this.packageJsonService.getPackageJsonFilePath(); if (projectJson && packageJsonFilePath) { const prismaSchemaFilePath = this.prismaConfiguration.schemaFile.replace((0, path_1.dirname)(packageJsonFilePath), ''); // generate this.nxProjectJsonService.addRunCommands([`./node_modules/.bin/prisma generate --schema=.${prismaSchemaFilePath}`], undefined, prismaSchemaFilePath, this.prismaConfiguration.nxProjectJsonFile); this.nxProjectJsonService.addRunCommands([`./node_modules/.bin/prisma generate --schema=.${prismaSchemaFilePath}`], 'prisma-generate', prismaSchemaFilePath, this.prismaConfiguration.nxProjectJsonFile); // pull this.nxProjectJsonService.addRunCommands([ `./node_modules/.bin/prisma db pull --schema=.${prismaSchemaFilePath}`, ...(this.prismaConfiguration.previewFeatures?.includes('typedSql') ? [`./node_modules/.bin/prisma generate --schema=.${prismaSchemaFilePath} --sql`] : []), ], 'prisma-pull', prismaSchemaFilePath, this.prismaConfiguration.nxProjectJsonFile); if (this.prismaConfiguration.addMigrationScripts) { // add const commands = (projectJson.targets?.['db-create']?.options?.['commands'] || []); for (const command of commands) { // "./node_modules/.bin/pg-create-db --force-change-username=true --force-change-password=true --root-database-url=${PRISMA_ROOT_DATABASE_URL} --app-database-url=${PRISMA_PRISMA_USER_DATABASE_URL}" const rootDatabaseName = command.split('--root-database-url=${')[1].split('}')[0]; const appDatabaseName = command.split('--app-database-url=${')[1].split('}')[0]; if (!appDatabaseName.includes('SHADOW_DATABASE_URL')) { // update db-create command const SHADOW_DATABASE_URL = appDatabaseName.replace('_DATABASE_URL', '_SHADOW_DATABASE_URL'); this.nxProjectJsonService.addRunCommands([ `./node_modules/.bin/pg-create-db --force-change-username=true --force-change-password=true --root-database-url=\${${rootDatabaseName}} --app-database-url=\${${SHADOW_DATABASE_URL}}`, ], 'db-create', undefined, this.prismaConfiguration.nxProjectJsonFile); } } // migrate-dev this.nxProjectJsonService.addRunCommands([`./node_modules/.bin/prisma migrate dev --schema=.${prismaSchemaFilePath}`], 'prisma-migrate-dev', prismaSchemaFilePath, this.prismaConfiguration.nxProjectJsonFile); // migrate-deploy this.nxProjectJsonService.addRunCommands([`./node_modules/.bin/prisma migrate deploy --schema=.${prismaSchemaFilePath}`], 'prisma-migrate-deploy', prismaSchemaFilePath, this.prismaConfiguration.nxProjectJsonFile); } } } async updateDotEnvFile() { if (this.prismaConfiguration.addMigrationScripts) { const { databaseName, shadowDatabaseName } = this.getDbConnectionEnvKeys(); // update env file const envs = this.dotEnvService.read(false, true); if (envs?.[databaseName] && !envs[shadowDatabaseName]) { const parsed = this.parseDatabaseUrl(envs[databaseName] || ''); delete envs['# shadow database, need for create migrations from diff of old and new schema content']; envs['# shadow database, need for create migrations from diff of old and new schema content'] = ''; envs[shadowDatabaseName] = envs[databaseName]?.replace(`/${parsed.DATABASE}?`, `/shadow_${parsed.DATABASE}?`); await this.dotEnvService.write(envs, true); } } } updatePrismaSchemaFile() { const packageJsonFilePath = this.packageJsonService.getPackageJsonFilePath(); let prismaSchema = this.prismaSchemaFileService.read(); if (!prismaSchema) { const { databaseName, shadowDatabaseName } = this.getDbConnectionEnvKeys(); const prismaFeatureName = this.prismaConfiguration.featureName ? (0, case_anything_1.upperCamelCase)(this.prismaConfiguration.featureName) : ''; const constantCasePrismaFeatureName = this.prismaConfiguration.featureName ? (0, case_anything_1.constantCase)(this.prismaConfiguration.featureName) : ''; prismaSchema = `generator client { provider = "${this.prismaConfiguration.provider}" ${this.prismaConfiguration.engineType ? `engineType = "${this.prismaConfiguration.engineType}"` : ''} output = "${this.prismaConfiguration.output ? this.prismaConfiguration.output.replace(packageJsonFilePath ? (0, path_1.dirname)(packageJsonFilePath) : '', this.getPathToRootFromPrismaSchemaFile()) : `${this.getPathToRootFromPrismaSchemaFile()}/node_modules/@prisma/client`}" ${(this.prismaConfiguration.binaryTargets || []).length > 0 ? `binaryTargets = ${JSON.stringify(this.prismaConfiguration.binaryTargets)}` : ''} ${(this.prismaConfiguration.previewFeatures || []).length > 0 ? `previewFeatures = ${JSON.stringify(this.prismaConfiguration.previewFeatures)}` : ''} ${this.prismaConfiguration.runtime ? `runtime = ${JSON.stringify(this.prismaConfiguration.runtime)}` : ''} ${this.prismaConfiguration.moduleFormat ? `moduleFormat = ${JSON.stringify(this.prismaConfiguration.moduleFormat)}` : ''} ${this.prismaConfiguration.generatedFileExtension ? `generatedFileExtension = ${JSON.stringify(this.prismaConfiguration.generatedFileExtension)}` : ''} ${this.prismaConfiguration.importFileExtension ? `importFileExtension = ${JSON.stringify(this.prismaConfiguration.importFileExtension)}` : ''} } datasource db { provider = "postgresql" url = env("${databaseName}") shadowDatabaseUrl = env("${shadowDatabaseName}") } model ${prismaFeatureName}User { id String @id(map: "PK_${constantCasePrismaFeatureName}_USER") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid externalUserId String @unique(map: "UQ_${constantCasePrismaFeatureName}_USER") @db.Uuid createdAt DateTime @default(now()) @db.Timestamp(6) updatedAt DateTime @default(now()) @db.Timestamp(6) } `; } if (this.prismaConfiguration.customSchemaContent) { const clean = (s) => s.split(' ').join('').split('\n').join(''); if (this.prismaConfiguration.customSchemaContent && !clean(prismaSchema).includes(clean(this.prismaConfiguration.customSchemaContent))) { prismaSchema = `${prismaSchema}\n${this.prismaConfiguration.customSchemaContent}`; } } const generatorArr = prismaSchema.split('generator client {'); const afterRemoveGenerator = (generatorArr[0] || '') + (generatorArr[1].split('}').slice(1).join('}') || ''); const datasourceArr = afterRemoveGenerator.split('datasource db {'); const afterRemoveDatasource = (datasourceArr[0] || '') + (datasourceArr[1].split('}').slice(1).join('}') || ''); const clientNodeJSModuleName = this.prismaConfiguration.featureName ? `@prisma/${(0, case_anything_1.kebabCase)(this.prismaConfiguration.featureName)}-client` : `@prisma/client`; const newGenerator = `generator client { provider = "${this.prismaConfiguration.provider}" ${this.prismaConfiguration.engineType ? `engineType = "${this.prismaConfiguration.engineType}"` : ''} output = "${this.prismaConfiguration.output ? this.prismaConfiguration.output.replace(packageJsonFilePath ? (0, path_1.dirname)(packageJsonFilePath) : '', this.getPathToRootFromPrismaSchemaFile()) : `${this.getPathToRootFromPrismaSchemaFile()}/node_modules/@prisma/client`}" ${(this.prismaConfiguration.binaryTargets || []).length > 0 ? `binaryTargets = ${JSON.stringify(this.prismaConfiguration.binaryTargets)}` : ''} ${(this.prismaConfiguration.previewFeatures || []).length > 0 ? `previewFeatures = ${JSON.stringify(this.prismaConfiguration.previewFeatures)}` : ''} ${this.prismaConfiguration.runtime ? `runtime = ${JSON.stringify(this.prismaConfiguration.runtime)}` : ''} ${this.prismaConfiguration.moduleFormat ? `moduleFormat = ${JSON.stringify(this.prismaConfiguration.moduleFormat)}` : ''} ${this.prismaConfiguration.generatedFileExtension ? `generatedFileExtension = ${JSON.stringify(this.prismaConfiguration.generatedFileExtension)}` : ''} ${this.prismaConfiguration.importFileExtension ? `importFileExtension = ${JSON.stringify(this.prismaConfiguration.importFileExtension)}` : ''} }`; const { databaseName, shadowDatabaseName } = this.getDbConnectionEnvKeys(); const connectionString = this.parseDatabaseUrl(this.prismaEnvironments.databaseUrl); const newDatasource = `datasource db { provider = "${connectionString.PROTOCOL || 'postgresql'}" url = env("${databaseName}")${this.prismaConfiguration.addMigrationScripts ? ` shadowDatabaseUrl = env("${shadowDatabaseName}")` : ''} }`; this.prismaSchemaFileService.write([newGenerator, newDatasource, afterRemoveDatasource].join('\n').split('\n\n\n').join('\n')); } getPathToRootFromPrismaSchemaFile() { const packageJsonFilePath = this.packageJsonService.getPackageJsonFilePath(); const prismaSchemaFilePath = this.prismaSchemaFileService.getPrismaSchemaFilePath(); return prismaSchemaFilePath .replace((0, path_1.dirname)(packageJsonFilePath || ''), '') .split('/') .slice(2) .map(() => '..') .join('/'); } getDbConnectionEnvKeys() { const concatedDatabaseName = [ this.wrapApplicationOptionsService.project?.name, this.prismaConfiguration.featureName, 'DATABASE_URL', ] .filter(Boolean) .join('_'); const concatedShadowDatabaseName = [ this.wrapApplicationOptionsService.project?.name, this.prismaConfiguration.featureName, 'SHADOW_DATABASE_URL', ] .filter(Boolean) .join('_'); const databaseName = this.prismaConfiguration.featureName ? `${(0, case_anything_1.constantCase)(concatedDatabaseName)}` : `DATABASE_URL`; const shadowDatabaseName = this.prismaConfiguration.featureName ? `${(0, case_anything_1.constantCase)(concatedShadowDatabaseName)}` : `SHADOW_DATABASE_URL`; return { databaseName, shadowDatabaseName }; } parseDatabaseUrl(databaseUrl) { if (!databaseUrl) { return {}; } try { const cs = new connection_string_1.ConnectionString(databaseUrl); const USERNAME = cs.user; const PASSWORD = cs.password; const PORT = cs.port; const HOST = cs.hosts && cs.hosts[0].toString(); const DATABASE = cs.path && cs.path[0]; const SCHEMA = cs.params && cs.params['schema']; const SCHEMAS = cs.params && cs.params['schemas']; return { USERNAME, PASSWORD, HOST, DATABASE, SCHEMA, SCHEMAS, PORT, PROTOCOL: cs.protocol }; } catch (error) { return {}; } } }; exports.PrismaInfrastructureUpdaterService = PrismaInfrastructureUpdaterService; exports.PrismaInfrastructureUpdaterService = PrismaInfrastructureUpdaterService = tslib_1.__decorate([ (0, common_2.Injectable)(), tslib_1.__metadata("design:paramtypes", [common_1.PackageJsonService, common_1.NxProjectJsonService, prisma_configuration_1.PrismaConfiguration, prisma_environments_1.PrismaEnvironments, prisma_schema_file_service_1.PrismaSchemaFileService, common_1.WrapApplicationOptionsService, common_1.DotEnvService]) ], PrismaInfrastructureUpdaterService); //# sourceMappingURL=prisma-infrastructure-updater.service.js.map