question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Automatically pascal case model names during introspection

See original GitHub issue

Problem

Having to manually update model names to conform to Prisma’s naming conventions using the introspection tool is 1) annoying to do for many snake cased table names, and 2) prevents an introspection-only schema approach to using prisma2.

Solution

Automatically change model names from this format:

model my_table {
  id   String @id @default(cuid())
  name String
}

To this:

model MyTable {
  id   String @id @default(cuid())
  name String
  @@map("my_table")
}

Alternatives

I could write my own script.

Additional context

Started in discussion at:

https://github.com/prisma/prisma2/discussions/1925

Just going to dump some regex here that might help write that script:

model ([a-z_]+) {
const result = `
model ${pascalCase(name)} {
  @@map(${name})
}
`

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:41
  • Comments:11 (2 by maintainers)

github_iconTop GitHub Comments

35reactions
TLaddcommented, Apr 23, 2020

I wanted to follow standard postgres naming practices (using snake case for column and field names so I don’t have to quote everything when using raw sql), but still end up with a generated client that follows normal Typescript/prisma conventions (Pascal case for models, camelcase for field names, plurals for array relationships).

I wrote a script that handles all of my personal naming issues. Might be easier with access to the AST and there could be cases I’m missing since I’m starting on a new app and so my schema is fairly simple, but works for my use case and shouldn’t be too hard to add additional cases as needed. Didn’t bother trying to handle spacing. Saving the file in vscode with the prisma plugin fixes it. It handles:

  • change model name to pascal case in both the model declaration and when used as types for relation fields. Adds the appropriate @@map annotation.
  • change field names from snake case to pascal case and updates references to those fields in relations and indexes. Add @map annotation to end of field line.
  • Add and s to field names for one to many and many to many relationships.
import fs from "fs";
import path from "path";

const PRISMA_FILE_PATH = path.join(__dirname, "..", "prisma", "schema.prisma");

function snakeToCamel(str: string) {
  return str.replace(/([-_]\w)/g, (g) => g[1].toUpperCase());
}
function snakeToPascal(str: string) {
  let camelCase = snakeToCamel(str);
  return `${camelCase[0].toUpperCase()}${camelCase.substr(1)}`;
}

const PRISMA_PRIMITIVES = ["String", "Boolean", "Int", "Float", "DateTime"];

function isPrimitiveType(typeName: string) {
  return PRISMA_PRIMITIVES.includes(typeName);
}

function fixFieldsArrayString(fields: string) {
  return fields
    .split(", ")
    .map((field) => snakeToCamel(field))
    .join(", ");
}

async function fixPrismaFile() {
  const text = fs.readFileSync(path.join(PRISMA_FILE_PATH), "utf8");

  const textAsArray = text.split("\n");

  const fixedText = [];
  let currentModelName: string | null = null;
  let hasAddedModelMap = false;

  for (let line of textAsArray) {
    // Are we at the start of a model definition
    const modelMatch = line.match(/^model (\w+) {$/);
    if (modelMatch) {
      currentModelName = modelMatch[1];
      hasAddedModelMap = false;
      const pascalModelName = snakeToPascal(currentModelName);
      fixedText.push(`model ${pascalModelName} {`);
      continue;
    }

    // We don't need to change anything if we aren't in a model body
    if (!currentModelName) {
      fixedText.push(line);
      continue;
    }

    // Add the @@map to the table name for the model
    if (!hasAddedModelMap && (line.match(/\s+@@/) || line === "}")) {
      if (line === "}") {
        fixedText.push("");
      }
      fixedText.push(`  @@map("${currentModelName}")`);
      hasAddedModelMap = true;
    }

    // Renames field and applies a @map to the field name if it is snake case
    // Adds an s to the field name if the type is an array relation
    const fieldMatch = line.match(/\s\s(\w+)\s+(\w+)(\[\])?/);
    let fixedLine = line;
    if (fieldMatch) {
      const [, currentFieldName, currentFieldType, isArrayType] = fieldMatch;

      let fixedFieldName = snakeToCamel(currentFieldName);
      if (isArrayType && !fixedFieldName.endsWith("s")) {
        fixedFieldName = `${fixedFieldName}s`;
      }

      fixedLine = fixedLine.replace(currentFieldName, fixedFieldName);

      // Add map if we needed to convert the field name and the field is not a relational type
      // If it's relational, the field type will be a non-primitive, hence the isPrimitiveType check
      if (currentFieldName.includes("_") && isPrimitiveType(currentFieldType)) {
        fixedLine = `${fixedLine} @map("${currentFieldName}")`;
      }
    }

    // Capitalizes model names in field types
    const fieldTypeMatch = fixedLine.match(/\s\s\w+\s+(\w+)/);
    if (fieldTypeMatch) {
      const currentFieldType = fieldTypeMatch[1];
      const fieldTypeIndex = fieldTypeMatch[0].lastIndexOf(currentFieldType);
      const fixedFieldType = snakeToPascal(currentFieldType);
      const startOfLine = fixedLine.substr(0, fieldTypeIndex);
      const restOfLine = fixedLine.substr(
        fieldTypeIndex + currentFieldType.length
      );
      fixedLine = `${startOfLine}${fixedFieldType}${restOfLine}`;
    }

    // Changes `fields: [relation_id]` in @relation to camel case
    const relationFieldsMatch = fixedLine.match(/fields:\s\[([\w,\s]+)\]/);
    if (relationFieldsMatch) {
      const fields = relationFieldsMatch[1];
      fixedLine = fixedLine.replace(fields, fixFieldsArrayString(fields));
    }

    // Changes fields listed in @@index or @@unique to camel case
    const indexUniqueFieldsMatch = fixedLine.match(/@@\w+\(\[([\w,\s]+)\]/);
    if (indexUniqueFieldsMatch) {
      const fields = indexUniqueFieldsMatch[1];
      fixedLine = fixedLine.replace(fields, fixFieldsArrayString(fields));
    }

    fixedText.push(fixedLine);
  }

  fs.writeFileSync(PRISMA_FILE_PATH, fixedText.join("\n"));
}

fixPrismaFile();

Edit: Added check to avoid adding @map to relation fields.

22reactions
michaellzccommented, May 26, 2020

For those looking for fixing the naming convention automatically, I wrote a utility program that utilizes the Prisma schema AST, provided by the internal API getDMMF from @prisma/sdk, to automatically transform the naming of model and fields while keeping the mapping correct.

https://github.com/IBM/prisma-schema-transformer

Read more comments on GitHub >

github_iconTop Results From Across the Web

Names in the underlying database - Prisma
Mapping names in the Prisma schema allows you to influence the naming in your ... hand has recommended model naming conventions (singular form,...
Read more >
Methods and properties should be named in PascalCase
Shared naming conventions allow teams to collaborate efficiently. This rule checks whether or not method and property names are PascalCased. To reduce noise, ......
Read more >
What is pascal case? - TheServerSide.com
Pascal case requires that the first letter of a variable be in upper case. In contrast, camel case -- also known as CamelCase...
Read more >
Can I tell MVC to separate pascal-cased properties in to words ...
1. According to this answer, you can only apply constants, stackoverflow.com/questions/11654740/… · 2 · Use reflection to get property name from class definition, ......
Read more >
Ditching Gson's Field Naming Strategy | by Sebastiano Gottardo
Anyway, back to the codebase. The members of the model classes followed the standard camel case Java/Kotlin convention. There were no ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found