-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[eslint-plugin-azure-sdk] New rule to make sure TSdoc comments for pr…
…ivate members don't use internal tags (#18761) * worked on linter private member internal tag rule * fixed linter errors in packages and removed exportedSettings * fixed formatting in files with linter errors * removed excluding code * removed interfaces and modules check * checking for all syntax nodes with accessibility modifier * fixed sorted imports * style changes * fixed import sorting
- Loading branch information
Showing
10 changed files
with
355 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
93 changes: 93 additions & 0 deletions
93
common/tools/eslint-plugin-azure-sdk/docs/rules/ts-doc-internal-private-member.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# ts-doc-internal-private-member | ||
|
||
Prevents the usage of the `@internal` tag in TSDoc comments for private members. | ||
|
||
Internal objects are defined as classes, interfaces, or standalone functions that are not exported from the main entrypoint to the package and are not members of any exports from the main entrypoint (defined recursively). Because private members are already ignored by the docs generation tools, we do not need to use `@internal` in the TSDoc comments **Files that are specified as excluded in a `typedoc.json` file are ignored by this rule.** | ||
|
||
## Examples | ||
|
||
### Good | ||
|
||
```ts | ||
/** | ||
* Class documentation | ||
*/ | ||
class ExampleClass { | ||
private x = 0; | ||
private y: number; | ||
|
||
private [s: string]: boolean | ((s: string) => boolean); | ||
|
||
/** | ||
* Method documentation | ||
*/ | ||
private testMethod(private x: number, private y: number) {} | ||
} | ||
``` | ||
|
||
|
||
```ts | ||
/** | ||
* Class documentation | ||
*/ | ||
class ExampleClass { | ||
/** | ||
* @internal | ||
*/ | ||
x = 0; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
y: number; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
[s: string]: boolean | ((s: string) => boolean); | ||
|
||
/** | ||
* Method documentation | ||
* @internal | ||
*/ | ||
testMethod(private x: number, private y: number) {} | ||
} | ||
``` | ||
|
||
### Bad | ||
|
||
```ts | ||
/** | ||
* Class documentation | ||
*/ | ||
class ExampleClass { | ||
/** | ||
* @internal | ||
*/ | ||
private x = 0; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
private y: number; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
private [s: string]: boolean | ((s: string) => boolean); | ||
|
||
/** | ||
* Method documentation | ||
* @internal | ||
*/ | ||
private testMethod(private x: number, private y: number) {} | ||
} | ||
``` | ||
|
||
## When to turn off | ||
|
||
If you are not using TypeDoc. | ||
|
||
## Source | ||
|
||
Suggestion by [@deyaaeldeen](https://github.com/deyaaeldeen). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
common/tools/eslint-plugin-azure-sdk/src/rules/ts-doc-internal-private-member.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
/** | ||
* @file Rule to check if a private class member is tagged with @internal | ||
* @author Hamsa Shankar | ||
*/ | ||
|
||
import { ParserServices, TSESTree } from "@typescript-eslint/experimental-utils"; | ||
import { Node } from "estree"; | ||
import { ParserWeakMapESTreeToTSNode } from "@typescript-eslint/typescript-estree/dist/parser-options"; | ||
import { Rule } from "eslint"; | ||
import { SyntaxKind } from "typescript"; | ||
import { getRuleMetaData } from "../utils"; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Rule Definition | ||
//------------------------------------------------------------------------------ | ||
|
||
/** | ||
* Helper method for reporting on a node | ||
* @param node the Node being operated on | ||
* @param context the ESLint runtime context | ||
* @param converter a converter from TSESTree Nodes to TSNodes | ||
* @param typeChecker the TypeScript TypeChecker | ||
* @throws if the Node passes throught the initial checks and has an internal tag | ||
*/ | ||
const reportInternal = ( | ||
node: Node, | ||
context: Rule.RuleContext, | ||
converter: ParserWeakMapESTreeToTSNode, | ||
): void => { | ||
const tsNode = converter.get(node as TSESTree.Node); | ||
const modifiers = tsNode.modifiers; | ||
|
||
// if type is internal and has a TSDoc and is a private member | ||
if ((tsNode as any).jsDoc !== undefined | ||
&& modifiers?.some((modifier) => modifier.kind === SyntaxKind.PrivateKeyword)) { | ||
// fetch all tags | ||
for (const comment of (tsNode as any).jsDoc) { | ||
if (comment.tags !== undefined) { | ||
for (const tag of comment.tags) { | ||
if (tag.tagName.escapedText.match(/internal/)) { | ||
context.report({ | ||
node, | ||
message: "private class members should not include an @internal tag" | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
|
||
export = { | ||
meta: getRuleMetaData( | ||
"ts-doc-internal-private-member", | ||
"requires TSDoc comments to not include an '@internal' tag if the object is private" | ||
), | ||
create: (context: Rule.RuleContext): Rule.RuleListener => { | ||
|
||
const parserServices = context.parserServices as ParserServices; | ||
if ( | ||
parserServices.program === undefined || | ||
parserServices.esTreeNodeToTSNodeMap === undefined | ||
) { | ||
return {}; | ||
} | ||
|
||
const converter = parserServices.esTreeNodeToTSNodeMap; | ||
|
||
return { | ||
":matches(ClassProperty, MethodDefinition, TSMethodSignature, TSPropertySignature, TSIndexSignature, TSParameterProperty)": ( | ||
node: Node | ||
): void => reportInternal(node, context, converter) | ||
}; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
179 changes: 179 additions & 0 deletions
179
common/tools/eslint-plugin-azure-sdk/tests/rules/ts-doc-internal-private-member.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT license. | ||
|
||
/** | ||
* @file Testing the ts-doc-internal-private-member rule. | ||
* @author Hamsa Shankar | ||
*/ | ||
|
||
import { RuleTester } from "eslint"; | ||
import rule from "../../src/rules/ts-doc-internal-private-member"; | ||
|
||
//------------------------------------------------------------------------------ | ||
// Tests | ||
//------------------------------------------------------------------------------ | ||
|
||
const ruleTester = new RuleTester({ | ||
parser: require.resolve("@typescript-eslint/parser"), | ||
parserOptions: { | ||
createDefaultProgram: true, | ||
project: "./tsconfig.json" | ||
} | ||
}); | ||
|
||
ruleTester.run("ts-doc-internal-private-member", rule, { | ||
valid: [ | ||
// all private | ||
{ | ||
code: ` | ||
/** | ||
* Class documentation | ||
*/ | ||
class ExampleClass { | ||
/** | ||
* Class Property | ||
*/ | ||
private x = 0; | ||
/** | ||
* Property Signature | ||
*/ | ||
private y: number; | ||
/** | ||
* Index signature | ||
*/ | ||
private [s: string]: boolean | ((s: string) => boolean); | ||
/** | ||
* Parameter Property | ||
*/ | ||
private testMethod(private x: number, private y: number) {} | ||
/** | ||
* Method Definition | ||
*/ | ||
private get getter(): number { return 0 } | ||
/** | ||
* Method Signature | ||
*/ | ||
private method1(): any; | ||
}`, | ||
filename: "src/test.ts" | ||
}, | ||
// all internal | ||
{ | ||
code: ` | ||
/** | ||
* Class documentation | ||
*/ | ||
class ExampleClass { | ||
/** | ||
* Class Property | ||
* @internal | ||
*/ | ||
x = 0; | ||
/** | ||
* Property Signature | ||
* @internal | ||
*/ | ||
y: number; | ||
/** | ||
* Index signature | ||
* @internal | ||
*/ | ||
[s: string]: boolean | ((s: string) => boolean); | ||
/** | ||
* Parameter Property | ||
* @internal | ||
*/ | ||
testMethod(private x: number, private y: number) {} | ||
/** | ||
* Method Definition | ||
* @internal | ||
*/ | ||
get getter(): number { return 0 } | ||
/** | ||
* Method Signature | ||
* @internal | ||
*/ | ||
method1(): any; | ||
}`, | ||
filename: "src/test.ts" | ||
} | ||
], | ||
invalid: [ | ||
{ | ||
code: ` | ||
/** | ||
* Class documentation | ||
*/ | ||
class ExampleClass { | ||
/** | ||
* Class Property | ||
* @internal | ||
*/ | ||
private x = 0; | ||
/** | ||
* Property Signature | ||
* @internal | ||
*/ | ||
private y: number; | ||
/** | ||
* Index signature | ||
* @internal | ||
*/ | ||
private [s: string]: boolean | ((s: string) => boolean); | ||
/** | ||
* Parameter Property | ||
* @internal | ||
*/ | ||
private testMethod(private x: number, private y: number) {} | ||
/** | ||
* Method Definition | ||
* @internal | ||
*/ | ||
private get getter(): number { return 0 } | ||
/** | ||
* Method Signature | ||
* @internal | ||
*/ | ||
private method1(): any; | ||
}`, | ||
filename: "src/test.ts", | ||
errors: [ | ||
{ | ||
message: "private class members should not include an @internal tag" | ||
}, | ||
{ | ||
message: "private class members should not include an @internal tag" | ||
}, | ||
{ | ||
message: "private class members should not include an @internal tag" | ||
}, | ||
{ | ||
message: "private class members should not include an @internal tag" | ||
}, | ||
{ | ||
message: "private class members should not include an @internal tag" | ||
}, | ||
{ | ||
message: "private class members should not include an @internal tag" | ||
} | ||
] | ||
} | ||
] | ||
}); |
Oops, something went wrong.