File size: 2,799 Bytes
048b1e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { createRule, paramMessage } from '@typespec/compiler'
import {
  isCamelCaseNoAcronyms,
  isPascalCaseNoAcronyms,
  isSnakeCase,
} from './utils.js'

export const casingRule = createRule({
  name: 'casing',
  severity: 'warning',
  description: 'Ensure proper casing style for AIP naming conventions.',
  messages: {
    name: paramMessage`The names of ${'type'} types must use ${'casing'}`,
  },
  create: (context) => ({
    model: (model) => {
      if (!isPascalCaseNoAcronyms(model.name)) {
        context.reportDiagnostic({
          format: { type: 'Model', casing: 'PascalCase' },
          target: model,
          messageId: 'name',
        })
      }
    },
    modelProperty: (property) => {
      const isPath = property.decorators.find(
        (d) => d.decorator.name === '$path',
      )

      if (isPath) {
        if (!isCamelCaseNoAcronyms(property.name)) {
          context.reportDiagnostic({
            format: { type: 'Model Property', casing: 'camelCase' },
            target: property,
            messageId: 'name',
          })
        }

        return
      }

      if (
        !['_', 'contentType'].includes(property.name) &&
        !isSnakeCase(property.name)
      ) {
        context.reportDiagnostic({
          format: { type: 'Model Property', casing: 'snake_case' },
          target: property,
          messageId: 'name',
        })
      }
    },
    enum: (model) => {
      // Check enum name is PascalCase
      if (!isPascalCaseNoAcronyms(model.name)) {
        context.reportDiagnostic({
          format: { type: 'Enum', casing: 'PascalCase' },
          target: model,
          messageId: 'name',
        })
      }

      // Check enum member names are PascalCase
      for (const member of model.members.values()) {
        if (!isPascalCaseNoAcronyms(member.name)) {
          context.reportDiagnostic({
            format: { type: 'Enum Member', casing: 'PascalCase' },
            target: member,
            messageId: 'name',
          })
        }
      }
    },
  }),
})

export const casingErrorsRule = createRule({
  name: 'casing-aip-errors',
  severity: 'error',
  description: 'Ensure proper casing style for AIP naming conventions.',
  messages: {
    value: paramMessage`The values of ${'type'} types must use ${'casing'}`,
  },
  create: (context) => ({
    enum: (model) => {
      // Check enum member values are snake_case
      for (const member of model.members.values()) {
        if (member.value && typeof member.value === 'string') {
          if (!isSnakeCase(member.value)) {
            context.reportDiagnostic({
              format: { type: 'Enum Value', casing: 'snake_case' },
              target: member,
              messageId: 'value',
            })
          }
        }
      }
    },
  }),
})