Skip to content

Commit

Permalink
fix(eslint-plugin): autofix no-public in explicit-member-accessibility
Browse files Browse the repository at this point in the history
  • Loading branch information
pablobirukov committed Jan 30, 2020
1 parent 569259e commit e119c20
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
18 changes: 18 additions & 0 deletions packages/eslint-plugin/.vscode/launch.json
@@ -0,0 +1,18 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Rule",
"program": "${workspaceFolder}/../../node_modules/jest/bin/jest.js",
"args": [
"--runInBand",
"tests/rules/explicit-member-accessibility.test"
]
}
]
}
60 changes: 60 additions & 0 deletions packages/eslint-plugin/src/rules/explicit-member-accessibility.ts
@@ -1,8 +1,15 @@
import {
AST_NODE_TYPES,
TSESTree,
AST_TOKEN_TYPES,
} from '@typescript-eslint/experimental-utils';
import * as util from '../util';
import {
ReportFixFunction,
RuleFixer,
RuleFix,
} from '@typescript-eslint/experimental-utils/dist/ts-eslint';
import { Range } from '../../../typescript-estree/dist/ts-estree/ts-estree';

type AccessibilityLevel =
| 'explicit' // require an accessor (including public)
Expand Down Expand Up @@ -38,6 +45,7 @@ export default util.createRule<Options, MessageIds>({
// too opinionated to be recommended
recommended: false,
},
fixable: 'code',
messages: {
missingAccessibility:
'Missing accessibility modifier on {{type}} {{name}}.',
Expand Down Expand Up @@ -91,6 +99,7 @@ export default util.createRule<Options, MessageIds>({
nodeType: string,
node: TSESTree.Node,
nodeName: string,
fix: ReportFixFunction | null = null,
): void {
context.report({
node: node,
Expand All @@ -99,6 +108,7 @@ export default util.createRule<Options, MessageIds>({
type: nodeType,
name: nodeName,
},
fix: fix,
});
}

Expand Down Expand Up @@ -140,6 +150,7 @@ export default util.createRule<Options, MessageIds>({
nodeType,
methodDefinition,
methodName,
getUnwantedPublicAccessibilityFixer(methodDefinition),
);
} else if (check === 'explicit' && !methodDefinition.accessibility) {
reportIssue(
Expand All @@ -151,6 +162,53 @@ export default util.createRule<Options, MessageIds>({
}
}

/**
* Creates a fixer that removes a "public" keyword with following spaces
*/
function getUnwantedPublicAccessibilityFixer(
node:
| TSESTree.MethodDefinition
| TSESTree.ClassProperty
| TSESTree.TSParameterProperty,
): ReportFixFunction {
return function(fixer: RuleFixer): RuleFix {
const tokens = sourceCode.getTokens(node);
let rangeToRemove: Range;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (
token.type === AST_TOKEN_TYPES.Keyword &&
token.value === 'public'
) {
const commensAfterPublicKeyword = sourceCode.getCommentsAfter(
token,
);
if (commensAfterPublicKeyword.length) {
// public /* Hi there! */ static foo()
// ^^^^^^^
rangeToRemove = [
token.range[0],
commensAfterPublicKeyword[0].range[0],
];
break;
} else {
// public static foo()
// ^^^^^^^
rangeToRemove = [token.range[0], tokens[i + 1].range[0]];
break;
}
}
}
if (typeof rangeToRemove! === 'undefined') {
throw new Error(
`Couldn't define a range for "public" keyword that is to be deleted`,
);
}

return fixer.removeRange(rangeToRemove);
};
}

/**
* Checks if property has an accessibility modifier.
* @param classProperty The node representing a ClassProperty.
Expand All @@ -170,6 +228,7 @@ export default util.createRule<Options, MessageIds>({
nodeType,
classProperty,
propertyName,
getUnwantedPublicAccessibilityFixer(classProperty),
);
} else if (propCheck === 'explicit' && !classProperty.accessibility) {
reportIssue(
Expand Down Expand Up @@ -217,6 +276,7 @@ export default util.createRule<Options, MessageIds>({
nodeType,
node,
nodeName,
getUnwantedPublicAccessibilityFixer(node),
);
}
break;
Expand Down
Expand Up @@ -706,5 +706,164 @@ class Test {
},
],
},
{
filename: 'test.ts',
code: `
class Test {
@public
public /*public*/constructor(private foo: string) {}
}
`,
options: [
{
accessibility: 'no-public',
},
],
errors: [
{
messageId: 'unwantedPublicAccessibility',
line: 3,
column: 3,
},
],
output: `
class Test {
@public
/*public*/constructor(private foo: string) {}
}
`,
},
{
filename: 'test.ts',
code: `
class Test {
@public
public foo() {}
}
`,
options: [
{
accessibility: 'no-public',
},
],
errors: [
{
messageId: 'unwantedPublicAccessibility',
line: 3,
column: 3,
},
],
output: `
class Test {
@public
foo() {}
}
`,
},

{
filename: 'test.ts',
code: `
class Test {
@public
public foo;
}
`,
options: [
{
accessibility: 'no-public',
},
],
errors: [
{
messageId: 'unwantedPublicAccessibility',
line: 3,
column: 3,
},
],
output: `
class Test {
@public
foo;
}
`,
},
{
filename: 'test.ts',
code: `
class Test {
public foo = "";
}
`,
options: [
{
accessibility: 'no-public',
},
],
errors: [
{
messageId: 'unwantedPublicAccessibility',
line: 3,
column: 3,
},
],
output: `
class Test {
foo = "";
}
`,
},

{
filename: 'test.ts',
code: `
class Test {
contructor(public /* Hi there */ readonly foo)
}
`,
options: [
{
accessibility: 'no-public',
overrides: { parameterProperties: 'no-public' },
},
],
errors: [
{
messageId: 'unwantedPublicAccessibility',
line: 3,
column: 14,
},
],
output: `
class Test {
contructor(/* Hi there */ readonly foo)
}
`,
},
{
filename: 'test.ts',
code: `
class Test {
contructor(public readonly foo: string)
}
`,
options: [
{
accessibility: 'no-public',
},
],
errors: [
{
messageId: 'unwantedPublicAccessibility',
line: 3,
column: 14,
},
],
output: `
class Test {
contructor(readonly foo: string)
}
`,
},
],
});

0 comments on commit e119c20

Please sign in to comment.