import { ESLintUtils } from '@typescript-eslint/utils'; import type { AST } from 'jsonc-eslint-parser'; // NOTE: The rule will be available in ESLint configs as "@nrwl/nx/workspace/valid-schema-description" export const RULE_NAME = 'valid-schema-description'; export const rule = ESLintUtils.RuleCreator(() => __filename)({ name: RULE_NAME, meta: { type: 'problem', docs: { description: `Ensures that nx schemas contain valid descriptions in order to provide consistent --help output for commands`, recommended: 'recommended', }, fixable: 'code', schema: [], messages: { requireSchemaDescriptionString: 'A schema description string is required in order to render --help output correctly', validSchemaDescription: 'A schema description should end with a . character for consistency', }, }, defaultOptions: [], create(context) { // jsonc-eslint-parser adds this property to parserServices where appropriate if (!(context.sourceCode.parserServices as any).isJSON) { return {}; } return { ['JSONExpressionStatement > JSONObjectExpression']( node: AST.JSONObjectExpression ) { const descriptionParentJSONPropertyNode = resolveDescriptionParentPropertyNode(node); if (!descriptionParentJSONPropertyNode) { context.report({ node: node as any, messageId: 'requireSchemaDescriptionString', }); return; } if (!descriptionParentJSONPropertyNode.value.value.endsWith('.')) { context.report({ node: descriptionParentJSONPropertyNode.value as any, messageId: 'validSchemaDescription', fix: (fixer) => { const [start, end] = descriptionParentJSONPropertyNode.value.range; return fixer.insertTextAfterRange( [start, end - 1], // -1 to account for the closing " of the string '.' ); }, }); } }, }; }, }); interface JSONPropertyWithStringLiteralValue extends AST.JSONProperty { key: AST.JSONStringLiteral; value: AST.JSONStringLiteral; } function resolveDescriptionParentPropertyNode( node: AST.JSONObjectExpression ): JSONPropertyWithStringLiteralValue | null { const descriptionParentJSONPropertyNode = node.properties.find((prop) => { return ( prop.key.type === 'JSONLiteral' && prop.key.value === 'description' && prop.value.type === 'JSONLiteral' && typeof prop.value.value === 'string' && prop.value.value.length > 0 ); }); return descriptionParentJSONPropertyNode as JSONPropertyWithStringLiteralValue; }