| import { | |
| createRule, | |
| getDiscriminator, | |
| getTypeName, | |
| isTemplateInstance, | |
| paramMessage, | |
| } from '@typespec/compiler' | |
| import { SyntaxKind } from '@typespec/compiler/ast' | |
| export const compositionOverInheritanceRule = createRule({ | |
| name: 'composition-over-inheritance', | |
| description: | |
| 'Check that if a model is used in an operation and has derived models that it has a discriminator or recommend to use composition via spread or `is`.', | |
| severity: 'warning', | |
| messages: { | |
| default: paramMessage`Model '${'name'}' is extending '${'baseModel'}' that doesn't define a discriminator. If '${'baseModel'}' is meant to be used: | |
| - For composition consider using spread \`...\` or \`model is\` instead. | |
| - As a polymorphic relation, add the \`@discriminator\` decorator on the base model.`, | |
| instance: paramMessage`Model '${'name'}' is extending a template '${'baseModel'}'. Consider using composition with spread \`...\` or \`model is\` instead.`, | |
| }, | |
| create(context) { | |
| return { | |
| model: (model) => { | |
| if ( | |
| model.baseModel && | |
| model.node?.kind === SyntaxKind.ModelStatement && | |
| model.node.extends && | |
| getDiscriminator(context.program, model.baseModel) === undefined | |
| ) { | |
| context.reportDiagnostic({ | |
| messageId: isTemplateInstance(model.baseModel) | |
| ? 'instance' | |
| : 'default', | |
| format: { | |
| name: model.name, | |
| baseModel: getTypeName(model.baseModel), | |
| }, | |
| target: model.node.extends, | |
| }) | |
| } | |
| }, | |
| } | |
| }, | |
| }) | |