diff --git a/packages/generator/package.json b/packages/generator/package.json index 3422f18923..1f3514712a 100644 --- a/packages/generator/package.json +++ b/packages/generator/package.json @@ -34,6 +34,7 @@ "@babel/core": "7.12.10", "@babel/plugin-transform-typescript": "7.12.1", "@blitzjs/display": "0.35.0-canary.2", + "@mrleebo/prisma-ast": "^0.2.3", "@types/jscodeshift": "0.7.2", "chalk": "^4.1.0", "cross-spawn": "7.0.3", diff --git a/packages/generator/src/generators/model-generator.ts b/packages/generator/src/generators/model-generator.ts index 6da9933d46..fd90ddcf98 100644 --- a/packages/generator/src/generators/model-generator.ts +++ b/packages/generator/src/generators/model-generator.ts @@ -1,11 +1,11 @@ import {log} from "@blitzjs/display" +import * as ast from "@mrleebo/prisma-ast" import {spawn} from "cross-spawn" import which from "npm-which" import path from "path" import {Generator, GeneratorOptions, SourceRootType} from "../generator" import {Field} from "../prisma/field" import {Model} from "../prisma/model" -import {matchBetween} from "../utils/match-between" export interface ModelGeneratorOptions extends GeneratorOptions { modelName: string @@ -36,55 +36,60 @@ export class ModelGenerator extends Generator { // eslint-disable-next-line require-await async write() { + const schemaPath = path.resolve("db/schema.prisma") + if (!this.fs.exists(schemaPath)) { + throw new Error("Prisma schema file was not found") + } + + let schema: ast.Schema try { - if (!this.fs.exists(path.resolve("db/schema.prisma"))) { - throw new Error("Prisma schema file was not found") - } - let updatedOrCreated = "created" + schema = ast.getSchema(this.fs.read(schemaPath)) + } catch (err) { + log.error("Failed to parse db/schema.prisma file") + throw err + } + const {modelName, extraArgs, dryRun} = this.options + let updatedOrCreated = "created" - const extraArgs = - this.options.extraArgs.length === 1 && this.options.extraArgs[0].includes(" ") - ? this.options.extraArgs[0].split(" ") - : this.options.extraArgs - const modelDefinition = new Model( - this.options.modelName, - extraArgs.flatMap((def) => Field.parse(def)), - ) - if (!this.options.dryRun) { - // wrap in newlines to put a space below the previously generated model and - // to preserve the EOF newline - const schema = this.fs.read(path.resolve("db/schema.prisma")) + let fields = (extraArgs.length === 1 && extraArgs[0].includes(" ") + ? extraArgs[0].split(" ") + : extraArgs + ).flatMap((input) => Field.parse(input, schema)) - if (schema.indexOf(`model ${modelDefinition.name}`) === -1) { - //model does not exist in schema - just add it - this.fs.append(path.resolve("db/schema.prisma"), `\n${modelDefinition.toString()}\n`) - } else { - const model = matchBetween(schema, `model ${modelDefinition.name}`, "}") - if (model) { - //filter out all fields that are already defined - modelDefinition.fields = modelDefinition.fields.filter((field) => { - return model.indexOf(field.name) === -1 - }) + const modelDefinition = new Model(modelName, fields) - //add new fields to the selected model - const newModel = model.replace("}", `${modelDefinition.getNewFields()}}`) - - //replace all content with the newly added fields for the already existing model - this.fs.write(path.resolve("db/schema.prisma"), schema.replace(model, newModel)) - updatedOrCreated = "updated" - } + let model: ast.Model | undefined + if (!dryRun) { + model = schema.list.find(function (component): component is ast.Model { + return component.type === "model" && component.name === modelDefinition.name + }) + try { + if (model) { + for (const field of fields) field.appendTo(model) + this.fs.write(schemaPath, ast.printSchema(schema)) + updatedOrCreated = "updated" + } else { + model = modelDefinition.appendTo(schema) + this.fs.write(schemaPath, ast.printSchema(schema)) } + } catch (err) { + console.error(`Failed to apply changes to model '${modelDefinition.name}'`) + throw err } + } + + if (model) { log.newline() log.success( - `Model for '${this.options.modelName}'${ - this.options.dryRun ? "" : ` ${updatedOrCreated} in schema.prisma` + `Model '${modelDefinition.name}'${ + dryRun ? "" : ` ${updatedOrCreated} in schema.prisma` }:\n`, ) - modelDefinition.toString().split("\n").map(log.progress) + ast + .printSchema({type: "schema", list: [model]}) + .split("\n") + .map(log.progress) log.newline() - } catch (error) { - throw error } } diff --git a/packages/generator/src/prisma/field.ts b/packages/generator/src/prisma/field.ts index 9686b79c98..fbb417a4dc 100644 --- a/packages/generator/src/prisma/field.ts +++ b/packages/generator/src/prisma/field.ts @@ -1,4 +1,5 @@ import {log} from "@blitzjs/display" +import * as ast from "@mrleebo/prisma-ast" import {capitalize, singlePascal, uncapitalize} from "../utils/inflector" export enum FieldType { @@ -53,10 +54,11 @@ export class Field { relationToFields?: string[] // 'name:type?[]:attribute' => Field - static parse(input: string): Field[] { + static parse(input: string, schema?: ast.Schema): Field[] { const [_fieldName, _fieldType = "String", attribute] = input.split(":") let fieldName = uncapitalize(_fieldName) let fieldType = capitalize(_fieldType) + let isId = fieldName === "id" let isRequired = true let isList = false let isUpdatedAt = false @@ -91,7 +93,21 @@ export class Field { const idFieldName = `${fieldName}Id` relationFromFields = [idFieldName] relationToFields = ["id"] - maybeIdField = new Field(idFieldName, {type: FieldType.Int, isRequired}) + + const relationModel = schema?.list.find(function (component): component is ast.Model { + return component.type === "model" && component.name === fieldType + }) + const relationField = + relationModel && + relationModel.properties.find(function (prop): prop is ast.Field { + return prop.type === "field" && prop.name === "id" + }) + + maybeIdField = new Field(idFieldName, { + // find the matching field based on the relation and, if found, match its field type + type: relationField ? (relationField.fieldType as FieldType) : FieldType.Int, + isRequired, + }) isList = false break } @@ -109,14 +125,14 @@ export class Field { if (defaultValueTest.test(attribute)) { const [, _defaultValue] = attribute.match(defaultValueTest)! defaultValue = builtInGenerators.includes(_defaultValue) - ? {name: _defaultValue} + ? {type: "function", name: _defaultValue, params: []} : _defaultValue } } try { const parseResult = new Field(fieldName, { default: defaultValue, - isId: false, + isId, isList, isRequired, isUnique, @@ -168,72 +184,88 @@ export class Field { } } - private getDefault() { - if (this.default === undefined) return "" - let defaultValue: string - if (typeof this.default === "object") { - // { name: 'fnname' } is based off of the Prisma model definition - defaultValue = `${this.default.name}()` - } else { - defaultValue = String(this.default) - } - return `@default(${defaultValue})` - } + appendTo(model: ast.Model) { + if (model.properties.some((prop) => prop.type === "field" && prop.name === this.name)) return - private getId() { - return this.isId ? "@id" : "" - } + const attributes = [ + this.getId(), + this.getIsUnique(), + this.getDefault(), + this.getIsUpdatedAt(), + this.getRelation(), + ].filter(Boolean) as ast.Attribute[] - private getIsUnique() { - return this.isUnique ? "@unique" : "" + model.properties.push({ + type: "field", + name: this.name, + fieldType: this.type, + optional: !this.isRequired, + array: this.isList, + attributes, + }) } - private getIsUpdatedAt() { - return this.isUpdatedAt ? "@updatedAt" : "" + private getDefault(): ast.Attribute | undefined { + if (this.default == null) return + + return { + type: "attribute", + kind: "field", + name: "default", + args: [ + { + type: "attributeArgument", + value: typeof this.default === "object" ? `${this.default.name}()` : String(this.default), + }, + ], + } } - private getRelation() { - if (this.relationFromFields === undefined || this.relationToFields === undefined) return "" - const separator = - this.relationToFields && - this.relationToFields.length > 0 && - this.relationFromFields && - this.relationFromFields.length - ? ", " - : "" - const fromFields = - this.relationFromFields && this.relationFromFields.length > 0 - ? `fields: [${this.relationFromFields.toString()}]` - : "" - - const toFields = - this.relationToFields && this.relationToFields.length > 0 - ? `references: [${this.relationToFields.toString()}]` - : "" - return `@relation(${fromFields}${separator}${toFields})` + private getId(): ast.Attribute | undefined { + if (!this.isId) return + + return { + type: "attribute", + kind: "field", + name: "id", + } } - private getTypeModifiers() { - return `${this.isRequired ? "" : "?"}${this.isList ? "[]" : ""}` + private getIsUnique(): ast.Attribute | undefined { + if (!this.isUnique) return + return {type: "attribute", kind: "field", name: "unique"} } - private getAttributes() { - const possibleAttributes = [ - this.getDefault(), - this.getId(), - this.getIsUnique(), - this.getIsUpdatedAt(), - this.getRelation(), - ] - // filter out any attributes that return '' - const attrs = possibleAttributes.filter((attr) => attr) - if (attrs.length > 0) { - return ` ${attrs.join(" ")}` - } - return "" + private getIsUpdatedAt(): ast.Attribute | undefined { + if (!this.isUpdatedAt) return + return {type: "attribute", kind: "field", name: "updatedAt"} } - toString() { - return `${this.name} ${this.type}${this.getTypeModifiers()}${this.getAttributes()}` + private getRelation(): ast.Attribute | undefined { + if (this.relationFromFields == null || this.relationToFields == null) return + + return { + type: "attribute", + kind: "field", + name: "relation", + args: [ + { + type: "attributeArgument", + value: { + type: "keyValue", + key: "fields", + value: {type: "array", args: this.relationFromFields}, + }, + }, + { + type: "attributeArgument", + value: { + type: "keyValue", + key: "references", + value: {type: "array", args: this.relationToFields}, + }, + }, + ], + } } } diff --git a/packages/generator/src/prisma/model.ts b/packages/generator/src/prisma/model.ts index 4e534015f0..4e063c7fb5 100644 --- a/packages/generator/src/prisma/model.ts +++ b/packages/generator/src/prisma/model.ts @@ -1,25 +1,6 @@ +import * as ast from "@mrleebo/prisma-ast" import {singlePascal} from "../utils/inflector" -import {Field, FieldType} from "./field" - -function stringifyFieldsForPrinting(fields: Field[]) { - let maxNameLength = 1 - let maxTypeLength = 1 - for (let field of fields) { - const [name, type] = field.toString().split(/[\s]+/) - maxNameLength = Math.max(maxNameLength, name.length) - maxTypeLength = Math.max(maxTypeLength, type.length) - } - // add horizontal padding, otherwise there would be no space after the type - maxNameLength += 2 - maxTypeLength += 2 - return fields.map((field) => { - const [name, type, ...rest] = field.toString().split(/[\s]+/) - const attrs = rest.join(" ") - const namePad = maxNameLength - name.length - const typePad = maxTypeLength - type.length - return `${name}${Array(namePad).join(" ")}${type}${Array(typePad).join(" ")}${attrs}` - }) -} +import {Field} from "./field" export class Model { name: string @@ -30,54 +11,111 @@ export class Model { this.fields = fields } - private getIdField() { - return new Field("id", { - isRequired: true, - isList: false, - isId: true, - default: "autoincrement()", - type: FieldType.Int, - }) + appendTo(schema: ast.Schema): ast.Model { + const model = this.createModelAst() + schema.list.push(model) + return model } - private getCreatedAtField() { - return new Field("createdAt", { - isRequired: true, - isList: false, - isId: false, - default: "now()", - type: FieldType.DateTime, - }) + private createModelAst(): ast.Model { + const properties = [ + this.getIdField(), + this.getCreatedAtField(), + this.getUpdatedAtField(), + ].filter(Boolean) as ast.Field[] + + const model: ast.Model = { + type: "model", + name: this.name, + properties, + } + + for (const field of this.fields) field.appendTo(model) + + return model } - private getUpdatedAtField() { - return new Field("updatedAt", { - isRequired: true, - isList: false, - isId: false, - isUpdatedAt: true, - type: FieldType.DateTime, - }) + private getIdField(): ast.Field | undefined { + if (this.fieldExists("id")) return + return { + type: "field", + name: "id", + fieldType: "Int", + attributes: [ + { + type: "attribute", + kind: "field", + name: "id", + }, + { + type: "attribute", + kind: "field", + name: "default", + args: [ + { + type: "attributeArgument", + value: { + type: "function", + name: "autoincrement", + params: [], + }, + }, + ], + }, + ], + } } - private getFields() { - return stringifyFieldsForPrinting([ - this.getIdField(), - this.getCreatedAtField(), - this.getUpdatedAtField(), - ...this.fields, - ]) - .map((field) => `\n ${field}`) - .join("") + private getCreatedAtField(): ast.Field | undefined { + if (this.fieldExists("createdAt")) return + return { + type: "field", + name: "createdAt", + fieldType: "DateTime", + attributes: [ + { + type: "attribute", + kind: "field", + name: "default", + args: [ + { + type: "attributeArgument", + value: { + type: "function", + name: "now", + params: [], + }, + }, + ], + }, + ], + } + } + + private getUpdatedAtField(): ast.Field | undefined { + if (this.fieldExists("updatedAt")) return + return { + type: "field", + name: "updatedAt", + fieldType: "DateTime", + attributes: [ + { + type: "attribute", + kind: "field", + name: "updatedAt", + }, + ], + } } - public getNewFields() { - return stringifyFieldsForPrinting([...this.fields]) - .map((field) => ` ${field}\n`) - .join("") + private fieldExists(name: string): boolean { + return this.fields.some((field) => field.name === name) } - toString() { - return `model ${this.name} {${this.getFields()}\n}` + toString(): string { + return ast.printSchema({ + type: "schema", + list: [this.createModelAst()], + }) } } diff --git a/packages/generator/test/prisma/__snapshots__/model.test.ts.snap b/packages/generator/test/prisma/__snapshots__/model.test.ts.snap index 228c17a193..334cb1f379 100644 --- a/packages/generator/test/prisma/__snapshots__/model.test.ts.snap +++ b/packages/generator/test/prisma/__snapshots__/model.test.ts.snap @@ -1,14 +1,16 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Prisma Model generates a proper model 1`] = ` -"model User { - id Int @default(autoincrement()) @id +" +model User { + id Int @id @default(autoincrement()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt email String @unique updated DateTime @updatedAt - recentLogins DateTime[] - twoFactorEnabled Boolean - twoFactorMethod String? -}" + recentLogins DateTime[] + twoFactorEnabled Boolean + twoFactorMethod String? +} +" `; diff --git a/packages/generator/test/prisma/field.test.ts b/packages/generator/test/prisma/field.test.ts index d9704e6e7d..15b05de426 100644 --- a/packages/generator/test/prisma/field.test.ts +++ b/packages/generator/test/prisma/field.test.ts @@ -1,65 +1,58 @@ +import {Schema} from "@mrleebo/prisma-ast" import {Field} from "../../src/prisma/field" -describe("Field model", () => { - it("generates models for simple scalar types, making fields singular", () => { - expect(Field.parse("name:string").toString()).toMatchInlineSnapshot(`"name String"`) - expect(Field.parse("userId: int").toString()).toMatchInlineSnapshot(`"userId int"`) +describe("Field", () => { + it("parses optional types", () => { + const [field] = Field.parse("name:string?") + expect(field.isRequired).toBe(false) }) - it("generates models for pluralized property, making pluralized name fields", () => { - expect(Field.parse("names:string").toString()).toMatchInlineSnapshot(`"names String"`) - expect(Field.parse("userIds: int").toString()).toMatchInlineSnapshot(`"userIds int"`) + it("appends unique attribute", () => { + const [field] = Field.parse("email:string?:unique") + expect(field.isUnique).toBe(true) }) - it("serializes optional types", () => { - expect(Field.parse("name:string?").toString()).toMatchInlineSnapshot(`"name String?"`) + it("appends updatedAt attribute", () => { + const [field] = Field.parse("updatedAt:DateTime:updatedAt") + expect(field.isUpdatedAt).toBe(true) }) - it("serializes list types, pluralizing fields", () => { - expect(Field.parse("users:int[]").toString()).toMatchInlineSnapshot(`"users Int[]"`) - expect(Field.parse("user:int[]").toString()).toMatchInlineSnapshot(`"user Int[]"`) + it("handles default simple attribute", () => { + const [field] = Field.parse("isActive:boolean:default=true") + expect(field.default).toBe("true") }) - it("appends simple attributes", () => { - expect(Field.parse("email:string?:unique").toString()).toMatchInlineSnapshot( - `"email String? @unique"`, - ) - expect(Field.parse("updatedAt:DateTime:updatedAt").toString()).toMatchInlineSnapshot( - `"updatedAt DateTime @updatedAt"`, - ) + it("handles default uuid attribute", () => { + const [field] = Field.parse("id:string:default=uuid") + expect(field.default).toMatchObject({name: "uuid"}) }) - it("handles single default attribute", () => { - expect(Field.parse("isActive:boolean:default=true").toString()).toMatchInlineSnapshot( - `"isActive Boolean @default(true)"`, - ) - }) - - it("handles built-in default attribute", () => { - expect(Field.parse("id:string:default=uuid").toString()).toMatchInlineSnapshot( - `"id String @default(uuid())"`, - ) - expect(Field.parse("id:int:default=autoincrement")) + it("handles default autoincrement attribute", () => { + const [field] = Field.parse("id:int:default=autoincrement") + expect(field.default).toMatchObject({name: "autoincrement"}) }) it("has default field type", () => { - expect(Field.parse("name").toString()).toMatchInlineSnapshot(`"name String"`) + const [field] = Field.parse("name") + expect(field.type).toBe("String") }) it("allow number characters in model name", () => { - expect(Field.parse("name2").toString()).toMatchInlineSnapshot(`"name2 String"`) + const [field] = Field.parse("name2") + expect(field.name).toBe("name2") }) it("allow underscore characters in model name", () => { - expect(Field.parse("first_name").toString()).toMatchInlineSnapshot(`"first_name String"`) + const [field] = Field.parse("first_name") + expect(field.name).toBe("first_name") }) it("disallows number as a first character in model name", () => { - expect(() => Field.parse("2first").toString()).toThrow() + expect(() => Field.parse("2first")).toThrow() }) it("disallows underscore as a first character in model name", () => { - expect(() => Field.parse("_first").toString()).toThrow() + expect(() => Field.parse("_first")).toThrow() }) it("disallows special characters in model name", () => { @@ -74,27 +67,62 @@ describe("Field model", () => { expect(() => Field.parse(":int")).toThrow() }) - it("handles belongsTo relations", () => { - expect(Field.parse("belongsTo:task").join("\n")).toMatchInlineSnapshot(` - "task Task @relation(fields: [taskId], references: [id]) - taskId Int" - `) - expect(Field.parse("belongsTo:tasks").join("\n")).toMatchInlineSnapshot(` - "tasks Task @relation(fields: [tasksId], references: [id]) - tasksId Int" - `) - expect(Field.parse("belongsTo:task?").join("\n")).toMatchInlineSnapshot(` - "task Task? @relation(fields: [taskId], references: [id]) - taskId Int?" - `) - expect(Field.parse("belongsTo:tasks?").join("\n")).toMatchInlineSnapshot(` - "tasks Task? @relation(fields: [tasksId], references: [id]) - tasksId Int?" - `) - // ignore list directives, not a valid relation type - expect(Field.parse("belongsTo:tasks[]").join("\n")).toMatchInlineSnapshot(` - "tasks Task @relation(fields: [tasksId], references: [id]) - tasksId Int" - `) + describe("belongsTo", () => { + const schema: Schema = { + type: "schema", + list: [ + { + type: "model", + name: "Task", + properties: [ + { + type: "field", + name: "id", + fieldType: "Int", + }, + ], + }, + { + type: "model", + name: "Project", + properties: [ + { + type: "field", + name: "id", + fieldType: "String", + }, + ], + }, + ], + } + + it("simple relation", () => { + const [relation, foreignKey] = Field.parse("belongsTo:task") + expect(relation).toMatchObject({ + name: "task", + type: "Task", + relationFromFields: ["taskId"], + relationToFields: ["id"], + }) + expect(foreignKey).toMatchObject({name: "taskId", type: "Int"}) + }) + + it("relation with schema", () => { + const [relation, foreignKey] = Field.parse("belongsTo:project?", schema) + expect(relation).toMatchObject({ + name: "project", + type: "Project", + isRequired: false, + relationFromFields: ["projectId"], + relationToFields: ["id"], + }) + expect(foreignKey).toMatchObject({name: "projectId", type: "String", isRequired: false}) + }) + + it("relation with list directive", () => { + const [relation, foreignKey] = Field.parse("belongsTo:tasks[]", schema) + expect(relation).toMatchObject({name: "tasks", type: "Task", isList: false}) + expect(foreignKey).toMatchObject({name: "tasksId", type: "Int", isList: false}) + }) }) }) diff --git a/packages/installer/package.json b/packages/installer/package.json index 5c5ee7aa14..f0bb6b1aeb 100644 --- a/packages/installer/package.json +++ b/packages/installer/package.json @@ -33,7 +33,7 @@ "@blitzjs/config": "0.35.0-canary.2", "@blitzjs/display": "0.35.0-canary.2", "@blitzjs/generator": "0.35.0-canary.2", - "@mrleebo/prisma-ast": "^0.2.0", + "@mrleebo/prisma-ast": "^0.2.3", "@prisma/sdk": "2.19.0", "@types/jscodeshift": "0.7.2", "cross-spawn": "7.0.3", diff --git a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-enum.test.ts.snap b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-enum.test.ts.snap index e1b685a4c0..2769ceda81 100644 --- a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-enum.test.ts.snap +++ b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-enum.test.ts.snap @@ -10,5 +10,6 @@ datasource db { enum Role { USER ADMIN -}" +} +" `; diff --git a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-field.test.ts.snap b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-field.test.ts.snap index 2f777382df..379fdc0f49 100644 --- a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-field.test.ts.snap +++ b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-field.test.ts.snap @@ -10,7 +10,8 @@ datasource db { model Project { id Int @id @default(autoincrement()) name String @unique -}" +} +" `; exports[`addPrismaField skips if model is missing 1`] = ` @@ -18,5 +19,6 @@ exports[`addPrismaField skips if model is missing 1`] = ` datasource db { provider = \\"sqlite\\" url = \\"file:./db.sqlite\\" -}" +} +" `; diff --git a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-generator.test.ts.snap b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-generator.test.ts.snap index e85c7ac2b9..12f46582be 100644 --- a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-generator.test.ts.snap +++ b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-generator.test.ts.snap @@ -13,7 +13,8 @@ generator client { generator nexusPrisma { provider = \\"nexus-prisma\\" -}" +} +" `; exports[`addPrismaGenerator adds generator to file 1`] = ` @@ -25,7 +26,8 @@ datasource db { generator nexusPrisma { provider = \\"nexus-prisma\\" -}" +} +" `; exports[`addPrismaGenerator overwrites same generator 1`] = ` @@ -37,5 +39,6 @@ datasource db { generator nexusPrisma { provider = \\"nexus-prisma\\" -}" +} +" `; diff --git a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model-attribute.test.ts.snap b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model-attribute.test.ts.snap index aa78a556f9..66462f7c1b 100644 --- a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model-attribute.test.ts.snap +++ b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model-attribute.test.ts.snap @@ -11,7 +11,8 @@ model Project { id Int @id @default(autoincrement()) name String @unique @@index([name]) -}" +} +" `; exports[`addPrismaModelAttribute skips if model is missing 1`] = ` @@ -19,5 +20,6 @@ exports[`addPrismaModelAttribute skips if model is missing 1`] = ` datasource db { provider = \\"sqlite\\" url = \\"file:./db.sqlite\\" -}" +} +" `; diff --git a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model.test.ts.snap b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model.test.ts.snap index 5240ddb14c..4e061de550 100644 --- a/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model.test.ts.snap +++ b/packages/installer/test/transforms/prisma/__snapshots__/add-prisma-model.test.ts.snap @@ -9,5 +9,6 @@ datasource db { model Project { id String -}" +} +" `; diff --git a/packages/installer/test/transforms/prisma/__snapshots__/produce-schema.test.ts.snap b/packages/installer/test/transforms/prisma/__snapshots__/produce-schema.test.ts.snap index 7a3c4544b4..59da1be2af 100644 --- a/packages/installer/test/transforms/prisma/__snapshots__/produce-schema.test.ts.snap +++ b/packages/installer/test/transforms/prisma/__snapshots__/produce-schema.test.ts.snap @@ -40,5 +40,6 @@ model User { email String @unique hasAdministrativeAccess Boolean @default(false) -}" +} +" `; diff --git a/packages/installer/test/transforms/prisma/__snapshots__/set-prisma-data-source.test.ts.snap b/packages/installer/test/transforms/prisma/__snapshots__/set-prisma-data-source.test.ts.snap index cb4e501b4f..02c115249a 100644 --- a/packages/installer/test/transforms/prisma/__snapshots__/set-prisma-data-source.test.ts.snap +++ b/packages/installer/test/transforms/prisma/__snapshots__/set-prisma-data-source.test.ts.snap @@ -6,7 +6,8 @@ exports[`setPrismaDataSource adds datasource if missing 1`] = ` datasource db { provider = \\"postgresql\\" url = env(\\"DATABASE_URL\\") -}" +} +" `; exports[`setPrismaDataSource sets datasource 1`] = ` @@ -17,5 +18,6 @@ datasource db { url = env(\\"DATABASE_URL\\") } -// comment down here" +// comment down here +" `; diff --git a/yarn.lock b/yarn.lock index ce6c4b13c7..c4f7aae85d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2711,10 +2711,10 @@ resolved "https://registry.yarnpkg.com/@mdx-js/tag/-/tag-0.18.0.tgz#386d687deaed7133699a420d74612acdd1ed1c26" integrity sha512-3g1NOnbw+sJZohNOEN9NlaYYDdzq1y34S7PDimSn3zLV8etCu7pTCMFbnFHMSe6mMmm4yJ1gfbS3QiE7t+WMGA== -"@mrleebo/prisma-ast@^0.2.0": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@mrleebo/prisma-ast/-/prisma-ast-0.2.1.tgz#f883308083f313f61252b3de862b9c0125b68a82" - integrity sha512-YOsN0PylFLTnFfD+rmSLLwHinhbkNPhdSoYBLWmGs8IBxaPqC3RrG2PAqqgDmT6Nzz4q9x9EuuFVbTArOjGp2Q== +"@mrleebo/prisma-ast@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@mrleebo/prisma-ast/-/prisma-ast-0.2.3.tgz#d3b3c7051e897d2e478dca8cddd08104a9906b7d" + integrity sha512-coY/5Ae1JlKsjJkfOt5jV2QCWjSllbweOy+x8M2gBUHpFaQJQZTuYpGh9ymBhTU01lb9JgwIpkUMyNrRdZHlMw== dependencies: chevrotain "^9.0.1"