From bb915d081241fcfafd918be65b7c8f114b6c75f7 Mon Sep 17 00:00:00 2001 From: Victor Korzunin Date: Wed, 15 Sep 2021 22:06:57 +0200 Subject: [PATCH] feat: support js code models generation --- plop/js/index.hbs | 42 ++++++++++++++++++ plop/js/models/Model.hbs | 44 +++++++++++++++++++ plop/js/models/index.hbs | 9 ++++ plop/plopfile.js | 24 +++++----- plop/{index.ts.hbs => ts/index.hbs} | 0 .../Model.ts.hbs => ts/models/Model.hbs} | 0 .../index.ts.hbs => ts/models/index.hbs} | 0 plop/{utils/find.ts.hbs => ts/utils/find.hbs} | 0 .../index.ts.hbs => ts/utils/index.hbs} | 0 prisma/schema.prisma | 3 +- prisma/sequelize/config.json | 4 +- src/index.ts | 26 +++++++++-- 12 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 plop/js/index.hbs create mode 100644 plop/js/models/Model.hbs create mode 100644 plop/js/models/index.hbs rename plop/{index.ts.hbs => ts/index.hbs} (100%) rename plop/{models/Model.ts.hbs => ts/models/Model.hbs} (100%) rename plop/{models/index.ts.hbs => ts/models/index.hbs} (100%) rename plop/{utils/find.ts.hbs => ts/utils/find.hbs} (100%) rename plop/{utils/index.ts.hbs => ts/utils/index.hbs} (100%) diff --git a/plop/js/index.hbs b/plop/js/index.hbs new file mode 100644 index 0000000..c252848 --- /dev/null +++ b/plop/js/index.hbs @@ -0,0 +1,42 @@ +const { Sequelize } = require('sequelize'); +const { tryLoadEnvs } = require('@prisma/sdk'); +const { mergeDeepRight } = require('ramda'); +const path = require('path'); + +const config = require('./config.json'); +const models = require('./models'); + +const loadedEnv = tryLoadEnvs({ + rootEnvPath: config.relativeEnvPaths.rootEnvPath && path.resolve(__dirname, config.relativeEnvPaths.rootEnvPath), + schemaEnvPath: config.relativeEnvPaths.schemaEnvPath && path.resolve(__dirname, config.relativeEnvPaths.schemaEnvPath), +}); +const env = { ...(loadedEnv ? loadedEnv.parsed : {}), ...process.env }; +const databaseUrl = config.datasource.url.fromEnvVar + ? env[config.datasource.url.fromEnvVar] + : config.datasource.url.value; + +module.exports.createSequelizeInstance = (options) => { + const withDefaults = mergeDeepRight({ + define: { + freezeTableName: true, + }, + }); + + const sequelize = new Sequelize(databaseUrl, withDefaults(options ?? {})); + + // First initialize all models + Object.keys(models).forEach((model) => { + models[model].initialize?.(sequelize); + }); + + // Then apply associations + Object.keys(models).forEach((model) => { + models[model].associate?.(models); + models[model].hooks?.(models); + }); + + return { + sequelize, + models, + }; +}; diff --git a/plop/js/models/Model.hbs b/plop/js/models/Model.hbs new file mode 100644 index 0000000..586fe86 --- /dev/null +++ b/plop/js/models/Model.hbs @@ -0,0 +1,44 @@ +const { Model, DataTypes } = require('sequelize'); + +module.exports.{{modelName}} = class extends Model { + static initialize(sequelize) { + this.init( + { + {{#each scalarFields}} + {{fieldName}}: { + type: {{#if isList}}DataTypes.ARRAY(DataTypes.{{type}}){{else}}DataTypes.{{{type}}}{{/if}},{{#if (eq allowNull false)}} + allowNull: {{allowNull}},{{/if}}{{#if (and hasDefaultValue (eq isAutoincrement false))}} + defaultValue: '{{default}}',{{/if}}{{#if isId}} + primaryKey: {{isId}},{{/if}}{{#if isAutoincrement}} + autoIncrement: {{isAutoincrement}},{{/if}}{{#if isUnique}} + unique: {{isUnique}},{{/if}} + }, + {{/each}} + }, + { + sequelize, + modelName: '{{modelName}}', + tableName: '{{#if dbName}}{{dbName}}{{else}}{{modelName}}{{/if}}', + timestamps: {{or (or hasCreatedAt hasUpdatedAt) hasDeletedAt}},{{#if (or (or hasCreatedAt hasUpdatedAt) hasDeletedAt)}}{{#if (eq hasCreatedAt false)}} + createdAt: false,{{/if}}{{#if (eq hasUpdatedAt false)}} + updatedAt: false,{{/if}}{{!-- {{#if (eq hasDeletedAt false)}} + deletedAt: false,{{/if}} --}}{{#if hasDeletedAt}} + paranoid: true,{{/if}}{{/if}} + } + ); + } + + {{#if (or belongsToFields (or hasManyFields hasOneFields))}} + static associate(models) { + {{#each belongsToFields}} + this.belongsTo(models.{{name}}, { as: '{{as}}', targetKey: '{{targetKey}}', foreignKey: '{{foreignKey}}' }); + {{/each}} + {{#each hasManyFields}} + this.hasMany(models.{{name}}, { as: '{{as}}' }); + {{/each}} + {{#each hasOneFields}} + this.hasOne(models.{{name}}, { as: '{{as}}' }); + {{/each}} + } + {{/if}} +} diff --git a/plop/js/models/index.hbs b/plop/js/models/index.hbs new file mode 100644 index 0000000..26e13d0 --- /dev/null +++ b/plop/js/models/index.hbs @@ -0,0 +1,9 @@ +{{#each models}} +const { {{modelName}} } = require('./{{modelName}}'); +{{/each}} + +module.exports = { + {{#each models}} + {{modelName}}, + {{/each}} +}; diff --git a/plop/plopfile.js b/plop/plopfile.js index 855babc..95a022c 100644 --- a/plop/plopfile.js +++ b/plop/plopfile.js @@ -18,28 +18,28 @@ module.exports = function (plop) { actions: () => [ { type: 'add', - path: 'utils/find.ts', - templateFile: path.join(__dirname, './utils/find.ts.hbs'), + path: 'utils/find.{{outputFormat}}', + templateFile: path.join(__dirname, './{{outputFormat}}/utils/find.hbs'), }, { type: 'add', - path: 'utils/index.ts', - templateFile: path.join(__dirname, './utils/index.ts.hbs'), + path: 'utils/index.{{outputFormat}}', + templateFile: path.join(__dirname, './{{outputFormat}}/utils/index.hbs'), }, ], }); - plop.setGenerator('index.ts', { + plop.setGenerator('index', { actions: () => [ { type: 'add', - path: 'models/index.ts', - templateFile: path.join(__dirname, './models/index.ts.hbs'), + path: 'models/index.{{outputFormat}}', + templateFile: path.join(__dirname, './{{outputFormat}}/models/index.hbs'), }, { type: 'add', - path: 'index.ts', - templateFile: path.join(__dirname, './index.ts.hbs'), + path: 'index.{{outputFormat}}', + templateFile: path.join(__dirname, './{{outputFormat}}/index.hbs'), }, { type: 'add', @@ -49,12 +49,12 @@ module.exports = function (plop) { ], }); - plop.setGenerator('Model.ts', { + plop.setGenerator('Model', { actions: () => [ { type: 'add', - path: 'models/{{modelName}}.ts', - templateFile: path.join(__dirname, './models/Model.ts.hbs'), + path: 'models/{{modelName}}.{{outputFormat}}', + templateFile: path.join(__dirname, './{{outputFormat}}/models/Model.hbs'), }, ], }); diff --git a/plop/index.ts.hbs b/plop/ts/index.hbs similarity index 100% rename from plop/index.ts.hbs rename to plop/ts/index.hbs diff --git a/plop/models/Model.ts.hbs b/plop/ts/models/Model.hbs similarity index 100% rename from plop/models/Model.ts.hbs rename to plop/ts/models/Model.hbs diff --git a/plop/models/index.ts.hbs b/plop/ts/models/index.hbs similarity index 100% rename from plop/models/index.ts.hbs rename to plop/ts/models/index.hbs diff --git a/plop/utils/find.ts.hbs b/plop/ts/utils/find.hbs similarity index 100% rename from plop/utils/find.ts.hbs rename to plop/ts/utils/find.hbs diff --git a/plop/utils/index.ts.hbs b/plop/ts/utils/index.hbs similarity index 100% rename from plop/utils/index.ts.hbs rename to plop/ts/utils/index.hbs diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 78a3679..19784e3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,5 +1,6 @@ generator models { - provider = "node ./dist/cli.js" + provider = "node ./dist/cli.js" + outputFormat = "typescript" } datasource db { diff --git a/prisma/sequelize/config.json b/prisma/sequelize/config.json index d88f2fb..766dc70 100644 --- a/prisma/sequelize/config.json +++ b/prisma/sequelize/config.json @@ -9,7 +9,9 @@ "value": "/Users/victor/Projects/_own/prisma-sequelize-generator/prisma/sequelize", "fromEnvVar": "null" }, - "config": {}, + "config": { + "outputFormat": "typescript" + }, "binaryTargets": [], "previewFeatures": [] }, diff --git a/src/index.ts b/src/index.ts index 77a42f4..76324b9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,12 +2,23 @@ import { generatorHandler } from '@prisma/generator-helper'; import { getEnvPaths, parseEnvValue } from '@prisma/sdk'; import nodePlop from 'node-plop'; import * as path from 'path'; +import { mergeRight, prop } from 'ramda'; import { transformDMMF } from './generator/transformDMMF'; // eslint-disable-next-line @typescript-eslint/no-var-requires // const pkg = require('../package.json'); +const formatAliases = { + javascript: 'js', + typescript: 'ts', + js: 'js', + ts: 'ts', +}; +const withDefaults = mergeRight({ + outputFormat: formatAliases.js, +}); + generatorHandler({ onManifest() { return { @@ -20,6 +31,13 @@ generatorHandler({ throw new Error('No output was specified for Prisma Sequelize Generator'); } + const generatorConfig = withDefaults(options.generator.config); + + if (!Object.keys(formatAliases).includes(generatorConfig.outputFormat)) { + throw new Error(`Incorrect output format was specified. Allowed formats are: ${Object.keys(formatAliases)}`); + } + const outputFormat = prop(generatorConfig.outputFormat as keyof typeof formatAliases, formatAliases); + const outputDir = // This ensures previous version of prisma are still supported typeof options.generator.output === 'string' @@ -29,8 +47,8 @@ generatorHandler({ try { const plop = nodePlop(path.join(__dirname, '../plop/plopfile.js'), { destBasePath: outputDir, force: true }); // const utilsGenerator = plop.getGenerator('utils'); - const indexGenerator = plop.getGenerator('index.ts'); - const modelGenerator = plop.getGenerator('Model.ts'); + const indexGenerator = plop.getGenerator('index'); + const modelGenerator = plop.getGenerator('Model'); const schemaDir = options.schemaPath ? path.dirname(options.schemaPath) : process.cwd(); const schemaPath = path.join(schemaDir, 'prisma.schema'); @@ -55,8 +73,8 @@ generatorHandler({ await Promise.all([ // utilsGenerator.runActions({}), - indexGenerator.runActions({ models, config: JSON.stringify(config, null, 2) }), - ...models.map((model) => modelGenerator.runActions(model)), + indexGenerator.runActions({ models, config: JSON.stringify(config, null, 2), outputFormat }), + ...models.map((model) => modelGenerator.runActions({ ...model, outputFormat })), ]); } catch (e) { console.error('Error: unable to write files for Prisma Sequelize Generator');