Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[M] Add requireUnderscorePrefixForIds and remove requirePrefix to actually support Mongo naming conventions #362

Merged
merged 6 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

## 0.3.0 (2024-04-03)

- [#330](https://github.com/loop-payments/prisma-lint/issues/330) Remove `requirePrefix` option and add `requireUnderscorePrefixForIds` to `field-name-mapping-snake-case` to actually support MongoDB naming conventions.

## 0.2.0 (2024-03-29)

- [#354](https://github.com/loop-payments/prisma-lint/issues/354) Add `allowlist` to `model-name-grammatical-number`.
Expand Down
10 changes: 6 additions & 4 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Checks that the mapped name of a field is the expected snake case.
```ts
z.object({
compoundWords: z.array(z.string()).optional(),
requirePrefix: z.string().optional(),
requireUnderscorePrefixForIds: z.boolean().optional(),
})
.strict()
.optional();
Expand Down Expand Up @@ -70,17 +70,19 @@ model PersistedQuery {
}
```

#### With `{ requirePrefix: ["_"] }`
#### With `{ requireUnderscorePrefixForIds: true }`

```prisma
// good
model PersistedQuery {
fooId String @map(name: "_foo_id")
id String @id @map(name: "_id")
otherField String @map(name: "other_field")
}

// bad
model PersistedQuery {
fooId String @map(name: "foo_id")
id String @id @map(name: "id")
otherField String @map(name: "other_field")
}
```

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prisma-lint",
"version": "0.2.0",
"version": "0.3.0",
"description": "A linter for Prisma schema files.",
"repository": {
"type": "git",
Expand Down
28 changes: 21 additions & 7 deletions src/rules/field-name-mapping-snake-case.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,50 @@ describe('field-name-mapping-snake-case', () => {
ruleDefinitions: [fieldNameMappingSnakeCase],
});

describe('with requirePrefix', () => {
const run = getRunner({ requirePrefix: '_' });
describe('with requireUnderscorePrefixForIds', () => {
const run = getRunner({ requireUnderscorePrefixForIds: true });

describe('valid with mapping', () => {
it('returns no violations', async () => {
const violations = await run(`
model User {
fooBar String @map(name: "_foo_bar")
id String @id @map(name: "_id")
otherField String @map(name: "other_field")
}
`);
expect(violations.length).toEqual(0);
expect(violations.map((v) => v.message)).toEqual([]);
});
});

describe('missing @map', () => {
it('returns violation', async () => {
const violations = await run(`
model User {
fooBar String
idFoo String @id
}
`);
expect(violations.length).toEqual(1);
expect(violations).toHaveLength(1);
});
});

describe('without prefix', () => {
it('returns violation', async () => {
const violations = await run(`
model user {
fooBar String @map(name: "foo_bar")
id String @id @map(name: "id")
}
`);
expect(violations.map((v) => v.message)).toEqual([
'Field name must be mapped to "_id".',
]);
});
});

describe('with wrong mapping', () => {
it('returns violation', async () => {
const violations = await run(`
model user {
idTwo String @id @map(name: "_id_one")
}
`);
expect(violations.length).toEqual(1);
Expand Down
28 changes: 17 additions & 11 deletions src/rules/field-name-mapping-snake-case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const RULE_NAME = 'field-name-mapping-snake-case';
const Config = z
.object({
compoundWords: z.array(z.string()).optional(),
requirePrefix: z.string().optional(),
requireUnderscorePrefixForIds: z.boolean().optional(),
})
.strict()
.optional();
Expand Down Expand Up @@ -51,23 +51,25 @@ const Config = z
* graphQLId String @map(name: "graph_q_l_id")
* }
*
* @example { requirePrefix: ["_"] }
* @example { requireUnderscorePrefixForIds: true }
* // good
* model PersistedQuery {
* fooId String @map(name: "_foo_id")
* id String @id @map(name: "_id")
* otherField String @map(name: "other_field")
* }
*
* // bad
* model PersistedQuery {
* fooId String @map(name: "foo_id")
* id String @id @map(name: "id")
* otherField String @map(name: "other_field")
* }
*
*/
export default {
ruleName: RULE_NAME,
configSchema: Config,
create: (config, context) => {
const { compoundWords, requirePrefix } = config ?? {};
const { compoundWords, requireUnderscorePrefixForIds } = config ?? {};
return {
Field: (model, field) => {
if (isAssociation(field.fieldType)) {
Expand All @@ -78,12 +80,12 @@ export default {
const report = (message: string) =>
context.report({ model, field, message });

const { name, attributes } = field;
const expectedSnakeCase = toSnakeCase(name, {
compoundWords,
requirePrefix,
});
const isMappingRequired = !isAllLowerCase(name);
const { attributes, name: fieldName } = field;
const isIdWithRequiredPrefix =
requireUnderscorePrefixForIds &&
attributes?.find((a) => a.name === 'id');
const isMappingRequired =
!isAllLowerCase(fieldName) || isIdWithRequiredPrefix;

if (!attributes) {
if (isMappingRequired) {
Expand All @@ -105,6 +107,10 @@ export default {
}
return;
}
let expectedSnakeCase = toSnakeCase(fieldName, { compoundWords });
if (isIdWithRequiredPrefix) {
expectedSnakeCase = `_${expectedSnakeCase}`;
}
if (mappedName !== expectedSnakeCase) {
report(`Field name must be mapped to "${expectedSnakeCase}".`);
}
Expand Down
Loading