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

Add S6957 (eslint-plugin-react/no-deprecated) extracted from S1874 (deprecation) #4619

Merged
merged 1 commit into from Mar 27, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
513 changes: 0 additions & 513 deletions its/ruling/src/test/expected/jsts/sonar-web/javascript-S1874.json

Large diffs are not rendered by default.

515 changes: 515 additions & 0 deletions its/ruling/src/test/expected/jsts/sonar-web/javascript-S6957.json

Large diffs are not rendered by default.

7 changes: 0 additions & 7 deletions packages/jsts/src/rules/S1874/cb.test.ts
Expand Up @@ -20,16 +20,9 @@
import { check } from '../tools';
import { rule } from './';
import path from 'path';
import { clearPackageJsons, loadPackageJsons } from '@sonar/jsts';

const sonarId = path.basename(__dirname);

describe('Rule S1874', () => {
beforeEach(() => {
loadPackageJsons(__dirname, []);
});
afterAll(() => {
clearPackageJsons();
});
check(sonarId, rule, __dirname);
});
63 changes: 0 additions & 63 deletions packages/jsts/src/rules/S1874/rule.diagnostics.ts

This file was deleted.

62 changes: 30 additions & 32 deletions packages/jsts/src/rules/S1874/rule.ts
Expand Up @@ -20,46 +20,44 @@
// https://sonarsource.github.io/rspec/#/rspec/S1874/javascript

import { Rule } from 'eslint';
import { rule as diagnosticsRule } from './rule.diagnostics';
import { rules } from 'eslint-plugin-react';
import { mergeRules } from '../helpers';
import { getNearestPackageJsons } from '@sonar/jsts';

const reactNoDeprecated = rules['no-deprecated'];
import { isRequiredParserServices } from '../helpers';
import * as ts from 'typescript';

export const rule: Rule.RuleModule = {
meta: {
messages: {
deprecated: '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}',
...diagnosticsRule.meta!.messages,
deprecation: '{{deprecation}}',
},
},
create(context: Rule.RuleContext) {
function getVersionFromOptions() {
return context.options?.[0]?.['react-version'];
const services = context.sourceCode.parserServices;
if (!isRequiredParserServices(services)) {
return {};
}
function getVersionFromPackageJson() {
for (const { contents: packageJson } of getNearestPackageJsons(context.filename)) {
if (packageJson.dependencies?.react) {
return packageJson.dependencies.react;
}
if (packageJson.devDependencies?.react) {
return packageJson.devDependencies.react;
return {
Program: () => {
const program = services.program;
const checker = program.getTypeChecker();
const sourceFile = program.getSourceFile(context.filename);
const diagnostics: ts.DiagnosticWithLocation[] =
// @ts-ignore: TypeChecker#getSuggestionDiagnostics is not publicly exposed
checker.getSuggestionDiagnostics(sourceFile);
for (const diagnostic of diagnostics) {
if (diagnostic.reportsDeprecated === true) {
const sourceCode = context.sourceCode;
const start = sourceCode.getLocFromIndex(diagnostic.start);
const end = sourceCode.getLocFromIndex(diagnostic.start + diagnostic.length);
const loc = { start, end };
context.report({
loc,
messageId: 'deprecation',
data: {
deprecation: diagnostic.messageText as string,
},
});
}
}
}
return null;
}

const reactVersion = getVersionFromOptions() || getVersionFromPackageJson();

const patchedContext = reactVersion
? Object.create(context, {
settings: {
value: { react: { version: reactVersion } },
writable: false,
},
})
: context;
return mergeRules(reactNoDeprecated.create(patchedContext), diagnosticsRule.create(context));
},
};
},
};
35 changes: 35 additions & 0 deletions packages/jsts/src/rules/S6957/cb.test.ts
@@ -0,0 +1,35 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { check } from '../tools';
import { rule } from './';
import path from 'path';
import { clearPackageJsons, loadPackageJsons } from '@sonar/jsts';

const sonarId = path.basename(__dirname);

describe('Rule S6957', () => {
beforeEach(() => {
loadPackageJsons(__dirname, []);
});
afterAll(() => {
clearPackageJsons();
});
check(sonarId, rule, __dirname);
});
20 changes: 20 additions & 0 deletions packages/jsts/src/rules/S6957/index.ts
@@ -0,0 +1,20 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
export { rule } from './rule';
62 changes: 62 additions & 0 deletions packages/jsts/src/rules/S6957/rule.ts
@@ -0,0 +1,62 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// https://sonarsource.github.io/rspec/#/rspec/S6957/javascript

import { Rule } from 'eslint';
import { rules } from 'eslint-plugin-react';
import { getNearestPackageJsons } from '@sonar/jsts';

const reactNoDeprecated = rules['no-deprecated'];

export const rule: Rule.RuleModule = {
meta: {
messages: {
deprecated: '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}',
},
},
create(context: Rule.RuleContext) {
function getVersionFromOptions() {
return context.options?.[0]?.['react-version'];
}
function getVersionFromPackageJson() {
for (const { contents: packageJson } of getNearestPackageJsons(context.filename)) {
if (packageJson.dependencies?.react) {
return packageJson.dependencies.react;
}
if (packageJson.devDependencies?.react) {
return packageJson.devDependencies.react;
}
}
return null;
}

const reactVersion = getVersionFromOptions() || getVersionFromPackageJson();

const patchedContext = reactVersion
? Object.create(context, {
settings: {
value: { react: { version: reactVersion } },
writable: false,
},
})
: context;
return reactNoDeprecated.create(patchedContext);
},
};
2 changes: 2 additions & 0 deletions packages/jsts/src/rules/index.ts
Expand Up @@ -137,6 +137,7 @@ import { rule as S6079 } from './S6079'; // no-code-after-done
import { rule as S125 } from './S125'; // no-commented-code
import { rule as S1854 } from './S1854'; // no-dead-store
import { rule as S3001 } from './S3001'; // no-delete-var
import { rule as S6957 } from './S6957'; // no-deprecated-react
import { rule as S4621 } from './S4621'; // no-duplicate-in-composite
import { rule as S108 } from './S108'; // no-empty
import { rule as S6019 } from './S6019'; // no-empty-after-reluctant
Expand Down Expand Up @@ -427,6 +428,7 @@ rules['no-code-after-done'] = S6079;
rules['no-commented-code'] = S125;
rules['no-dead-store'] = S1854;
rules['no-delete-var'] = S3001;
rules['no-deprecated-react'] = S6957;
rules['no-duplicate-in-composite'] = S4621;
rules['no-empty'] = S108;
rules['no-empty-after-reluctant'] = S6019;
Expand Down
Expand Up @@ -257,6 +257,7 @@ public static List<Class<? extends JavaScriptCheck>> getAllChecks() {
NoConstructorReturnCheck.class,
NoControlRegexCheck.class,
NoDangerWithChildrenCheck.class,
NoDeprecatedReactCheck.class,
NoDirectMutationStateCheck.class,
NoDuplicateEnumValuesCheck.class,
NoDuplicateImportsCheck.class,
Expand Down
@@ -0,0 +1,36 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.javascript.checks;

import org.sonar.check.Rule;
import org.sonar.plugins.javascript.api.EslintBasedCheck;
import org.sonar.plugins.javascript.api.JavaScriptRule;
import org.sonar.plugins.javascript.api.TypeScriptRule;

@JavaScriptRule
@TypeScriptRule
@Rule(key = "S6957")
public class NoDeprecatedReactCheck implements EslintBasedCheck {

@Override
public String eslintKey() {
return "no-deprecated-react";
}
}
@@ -0,0 +1,52 @@
<h2>Why is this an issue?</h2>
<p>Deprecated methods are functions or properties that are no longer recommended and are likely to be removed in future updates of the library. They
are often replaced with newer methods that offer better performance, security, or usability.</p>
<p>Using deprecated methods in React can lead to the following issues:</p>
<ul>
<li> <strong>Code Maintainability</strong>: As deprecated methods are removed in future versions, the code will break if not updated. This can lead
to increased time and effort in code maintenance. </li>
<li> <strong>Performance</strong>: Newer methods often come with performance improvements. Using deprecated methods can lead to slower app
performance. </li>
<li> <strong>Security</strong>: Deprecated methods may have known security issues that are fixed in newer methods. </li>
</ul>
<pre data-diff-id="1" data-diff-type="noncompliant">
import React, { useState, useEffect } from 'react';

function MyComponent(props) {
const [state, setState] = useState(initialState);

componentWillReceiveProps(nextProps) { // Noncompliant: deprecated lifecycle method
// Some code here...
}

componentWillUpdate(nextProps, nextState) { // Noncompliant: deprecated lifecycle method
// Some code here...
}

render() {
return &lt;div&gt;Hello World&lt;/div&gt;;
}
}
</pre>
<p>To fix this issue, check React’s upgrading guide, replace the deprecated methods with their newer counterparts, and adapt your component’s
implementation accordingly.</p>
<pre data-diff-id="1" data-diff-type="compliant">
import React, { useState, useEffect } from 'react';

function MyComponent(props) {
const [state, setState] = useState(initialState);

// Using useEffect to replace deprecated lifecycle methods
useEffect(() =&gt; {
// Code to run on component update
}, [props]); // This will run when `props` changes

return &lt;div&gt;Hello World&lt;/div&gt;;
}
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
<li> React Documentation - <a href="https://react.dev/reference/react">React Reference Overview</a> </li>
</ul>