TWNTY-2244 - ESLint rule: enforce usage of .getLoadable() + .getValue() to get atoms (#4143)

* ESLint rule: enforce usage of .getLoadable() + .getValue() to get atoms

Co-authored-by: Matheus <matheus_benini@hotmail.com>

* Merge main

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>

* Fix

* Refactor according to review

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>

* Fix linter issue

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>

* Fix linter

Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: Matheus <matheus_benini@hotmail.com>
Co-authored-by: v1b3m <vibenjamin6@gmail.com>
Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
gitstart-app[bot]
2024-03-06 00:24:20 +01:00
committed by GitHub
parent 706b5d3cf1
commit b2210bd418
17 changed files with 180 additions and 24 deletions

View File

@@ -30,6 +30,11 @@ import {
rule as styledComponentsPrefixedWithStyled,
RULE_NAME as styledComponentsPrefixedWithStyledName,
} from './rules/styled-components-prefixed-with-styled';
import {
rule as useGetLoadableAndGetValueToGetAtoms,
RULE_NAME as useGetLoadableAndGetValueToGetAtomsName,
} from './rules/use-getLoadable-and-getValue-to-get-atoms';
/**
* Import your custom workspace rules at the top of this file.
*
@@ -64,6 +69,8 @@ module.exports = {
[sortCssPropertiesAlphabeticallyName]: sortCssPropertiesAlphabetically,
[styledComponentsPrefixedWithStyledName]:
styledComponentsPrefixedWithStyled,
[useGetLoadableAndGetValueToGetAtomsName]:
useGetLoadableAndGetValueToGetAtoms,
[maxConstsPerFileName]: maxConstsPerFile,
},
};

View File

@@ -0,0 +1,53 @@
import { TSESLint } from '@typescript-eslint/utils';
import { rule, RULE_NAME } from './use-getLoadable-and-getValue-to-get-atoms';
const ruleTester = new TSESLint.RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
});
ruleTester.run(RULE_NAME, rule, {
valid: [
{
code: 'const atoms = snapshot.getLoadable(someState).getValue();',
},
{
code: 'const atoms = snapshot.getLoadable(someState(viewId)).getValue();',
},
],
invalid: [
{
code: 'const atoms = await snapshot.getPromise(someState);',
errors: [
{
messageId: 'invalidWayToGetAtoms',
},
],
output: 'const atoms = snapshot.getLoadable(someState).getValue();',
},
{
code: 'const atoms = await snapshot.getPromise(someState(viewId));',
errors: [
{
messageId: 'invalidWayToGetAtoms',
},
],
output:
'const atoms = snapshot.getLoadable(someState(viewId)).getValue();',
},
{
code: 'const atoms = snapshot.getLoadable(someState).anotherMethod();',
errors: [
{
messageId: 'invalidWayToGetAtoms',
},
],
output: 'const atoms = snapshot.getLoadable(someState).getValue();',
},
],
});

View File

@@ -0,0 +1,88 @@
import { ESLintUtils } from '@typescript-eslint/utils';
// NOTE: The rule will be available in ESLint configs as "@nx/workspace-usage-getLoadable-and-getValue-to-get-atoms"
export const RULE_NAME = 'use-getLoadable-and-getValue-to-get-atoms';
export const rule = ESLintUtils.RuleCreator(() => __filename)({
name: RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Ensure you are using getLoadable and getValue',
recommended: 'recommended',
},
fixable: 'code',
schema: [],
messages: {
redundantAwait: 'Redundant await on non-promise',
invalidAccessorOnSnapshot:
"Expected to use method 'getLoadable()' on 'snapshot' but instead found '{{ propertyName }}'",
invalidWayToGetAtoms:
"Expected to use method 'getValue()' with 'getLoadable()' but instead found '{{ propertyName }}'",
},
},
defaultOptions: [],
create: (context) => ({
AwaitExpression: (node) => {
const { argument, range }: any = node;
if (
(argument.callee?.object?.callee?.object?.name === 'snapshot' &&
argument?.callee?.object?.callee?.property?.name === 'getLoadable') ||
(argument.callee?.object?.name === 'snapshot' &&
argument?.callee?.property?.name === 'getLoadable')
) {
// remove await
context.report({
node,
messageId: 'redundantAwait',
data: {
propertyName: argument.callee.property.name,
},
fix: (fixer) => fixer.removeRange([range[0], range[0] + 5]),
});
}
},
MemberExpression: (node) => {
const { object, property }: any = node;
if (
object.callee?.type === 'MemberExpression' &&
object.callee.object?.name === 'snapshot' &&
object.callee.property?.name === 'getLoadable'
) {
const propertyName = property.name;
if (propertyName !== 'getValue') {
context.report({
node: property,
messageId: 'invalidWayToGetAtoms',
data: {
propertyName,
},
// replace the property with `getValue`
fix: (fixer) => fixer.replaceText(property, 'getValue'),
});
}
}
},
CallExpression: (node) => {
const { callee }: any = node;
if (
callee.type === 'MemberExpression' &&
callee.object?.name === 'snapshot' &&
callee.property?.name === 'getPromise'
) {
context.report({
node: callee.property,
messageId: 'invalidAccessorOnSnapshot',
data: {
propertyName: callee.property.name,
},
// Replace `getPromise` with `getLoadable`
fix: (fixer) => fixer.replaceText(callee.property, 'getLoadable'),
});
}
},
}),
});