Skip to content

Commit

Permalink
ci: add require-formly-code-documentation rule (#1019)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxKless authored Feb 21, 2022
1 parent a40a6e4 commit c9dd6aa
Show file tree
Hide file tree
Showing 15 changed files with 123 additions and 4 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@
"ish-custom-rules/use-camel-case-environment-properties": "error",
"ish-custom-rules/use-component-change-detection": "warn",
"ish-custom-rules/use-jest-extended-matchers-in-tests": "warn",
"ish-custom-rules/require-formly-code-documentation": "warn",
"jest/no-commented-out-tests": "warn",
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "warn",
Expand Down
2 changes: 2 additions & 0 deletions eslint-rules/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { noVarBeforeReturnRule } from './rules/no-var-before-return';
import { orderedImportsRule } from './rules/ordered-imports';
import { privateDestroyFieldRule } from './rules/private-destroy-field';
import { projectStructureRule } from './rules/project-structure';
import { requireFormlyCodeDocumentationRule } from './rules/require-formly-code-documentation';
import { useAliasImportsRule } from './rules/use-alias-imports';
import { useAsyncSynchronizationInTestsRule } from './rules/use-async-synchronization-in-tests';
import { useCamelCaseEnvironmentPropertiesRule } from './rules/use-camel-case-environment-properties';
Expand Down Expand Up @@ -40,6 +41,7 @@ const rules = {
'project-structure': projectStructureRule,
'newline-before-root-members': newlineBeforeRootMembersRule,
'no-var-before-return': noVarBeforeReturnRule,
'require-formly-code-documentation': requireFormlyCodeDocumentationRule,
};

module.exports = {
Expand Down
66 changes: 66 additions & 0 deletions eslint-rules/src/rules/require-formly-code-documentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { AST_NODE_TYPES, TSESLint, TSESTree } from '@typescript-eslint/experimental-utils';

import { getClosestAncestorByKind } from '../helpers';

export const requireFormlyCodeDocumentationRule: TSESLint.RuleModule<string, []> = {
meta: {
messages: {
missingDocumentationError: `Missing documentation for {{ artifactName }}. \n Please provide documentation for all Formly types, wrappers and extensions.`,
},
type: 'problem',
schema: [],
},
create: context => {
function hasPrecedingComment(node: TSESTree.ClassDeclaration) {
return (
context.getSourceCode().getCommentsBefore(node)?.length > 0 ||
(node.decorators?.[0] && context.getSourceCode().getCommentsBefore(node.decorators[0])?.length > 0)
);
}
if (!context.getFilename().includes('formly')) {
return {};
}
return {
ClassDeclaration(node) {
if (isFormlyArtifactClass(node) && !hasPrecedingComment(node)) {
context.report({
node,
messageId: 'missingDocumentationError',
data: {
artifactName: node.id.name,
},
});
}
},
'ExportNamedDeclaration Identifier'(node: TSESTree.Identifier) {
if (
node.typeAnnotation?.typeAnnotation?.type === AST_NODE_TYPES.TSTypeReference &&
node.typeAnnotation.typeAnnotation.typeName.type === AST_NODE_TYPES.Identifier &&
node.typeAnnotation.typeAnnotation.typeName.name === 'FormlyExtension' &&
context
.getSourceCode()
.getCommentsBefore(getClosestAncestorByKind(context, AST_NODE_TYPES.ExportNamedDeclaration)).length === 0
) {
context.report({
node,
messageId: 'missingDocumentationError',
data: {
artifactName: node.name,
},
});
}
},
};
},
};

function isFormlyArtifactClass(node: TSESTree.ClassDeclaration): boolean {
return (
['FieldType', 'FieldWrapper'].some(
superClass => node.superClass?.type === AST_NODE_TYPES.Identifier && node.superClass?.name === superClass
) ||
node.implements?.some(
impl => impl.expression.type === AST_NODE_TYPES.Identifier && impl.expression.name === 'FormlyExtension'
)
);
}
14 changes: 14 additions & 0 deletions eslint-rules/tests/require-formly-code-documentation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { requireFormlyCodeDocumentationRule } from '../src/rules/require-formly-code-documentation';

import { RuleTestConfig } from './_execute-tests';

const config: RuleTestConfig = {
ruleName: 'require-formly-code-documentation',
rule: requireFormlyCodeDocumentationRule,
tests: {
valid: [],
invalid: [],
},
};

export default config;
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FieldType, FieldTypeConfig } from '@ngx-formly/core';

/**
* Type that renders a terms and conditions field, specific for the checkout review form.
*/
@Component({
selector: 'ish-checkout-review-tac-field',
templateUrl: './checkout-review-tac-field.component.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FieldWrapper } from '@ngx-formly/core';

/**
* Wrapper that handles checkout specific formatting and display of radio buttons.
*
* @templateOptions **shippingMethod** that will have its description displayed.
* @templateOptions **id** that will be used in the label.
* @tempalteOptions **labelClass* that will be applied to the label.
*
*/
@Component({
selector: 'ish-shipping-radio-wrapper',
templateUrl: './shipping-radio-wrapper.component.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { FormlyExtension, FormlyFieldConfig } from '@ngx-formly/core';

type FieldConfigWithDisabled = Omit<FormlyFieldConfig, 'options'> & { options: { formState: { disabled: string[] } } };

/**
* This extension disables all fields that have keys that match those
* Extension that disables all fields that have keys that match those
* specified in the formstate.disabled property
*/
type FieldConfigWithDisabled = Omit<FormlyFieldConfig, 'options'> & { options: { formState: { disabled: string[] } } };

export const disablePrefilledExtension: FormlyExtension = {
onPopulate(field) {
if (hasDisabled(field) && field.key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { FieldType } from '@ngx-formly/core';

/**
* Type that will render an <ish-formly-address-form> component
* and configure it to use the current form as its parent.
*
* @templateOption **businessCustomer** will be passed on to the component (see component documentation for infos).
*/
@Component({
selector: 'ish-registration-address-field',
templateUrl: './registration-address-field.component.html',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@ import { FieldType } from '@ngx-formly/core';

const sizes = ['h1', 'h2'];

/**
* Type that displays a section heading.
*
* @templateOption **heading** the primary heading text. Will be translated.
* @templateOption **subheading** the secondary heading text. Wil be translated.
* @templateOption **headingSize** determines whether the heading should be rendered with an <h1> or <h2> tag.
* @templateOption **showRequiredInfo** determines whether a message explaining the required star should be shown.
*/
@Component({
selector: 'ish-registration-heading-field',
templateUrl: './registration-heading-field.component.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FieldType, FieldTypeConfig } from '@ngx-formly/core';

/**
* Type that will render a terms and conditions field, specific for the registration form.
*/
@Component({
selector: 'ish-registration-tac-field',
templateUrl: './registration-tac-field.component.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FieldType } from '@ngx-formly/core';

// eslint-disable-next-line ish-custom-rules/require-formly-code-documentation
@Component({
selector: 'ish-formly-testing-example',
templateUrl: './formly-testing-example.component.html',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FieldType } from '@ngx-formly/core';

// eslint-disable-next-line ish-custom-rules/require-formly-code-documentation
@Component({
selector: 'ish-formly-fieldgroup-example',
templateUrl: './formly-testing-fieldgroup-example.component.html',
Expand Down
1 change: 1 addition & 0 deletions src/app/shared/formly/dev/testing/formly-testing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FieldType, FieldWrapper, FormlyFieldConfig, FormlyForm, FormlyModule }
import { FormlySelectModule } from '@ngx-formly/core/select';

/* eslint-disable ish-custom-rules/project-structure */
/* eslint-disable ish-custom-rules/require-formly-code-documentation */

@Component({ template: 'CaptchaFieldComponent: {{ field.key }} {{ to | json }}' })
class CaptchaFieldComponent extends FieldType {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FieldType } from '@ngx-formly/core';

/**
* Type to simply display a text value with optional styling
*
* @templateOption **inputClass** a class that will be used to style the div around the text
*/
@Component({
selector: 'ish-plain-text-field',
templateUrl: './plain-text-field.component.html',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FieldType, FieldTypeConfig, FormlyFieldConfig } from '@ngx-formly/core'
/**
* Type for a basic input field
*
* @templateOption type supports all text types; 'text' (default), 'email', 'password', 'tel'
* @templateOption **type** supports all text types; 'text' (default), 'email', 'password', 'tel'
*
* @defaultWrappers form-field-horizontal & validation
*/
Expand Down

0 comments on commit c9dd6aa

Please sign in to comment.