@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
JavaScript
"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 .Uuid
externalUserId String .Uuid
createdAt DateTime .Timestamp(6)
updatedAt DateTime .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
? `/${(0, case_anything_1.kebabCase)(this.prismaConfiguration.featureName)}-client`
: `/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