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

ci: add require formly documentation rule #1019

Merged
merged 5 commits into from
Feb 21, 2022
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
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