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

feat: custom eslint rule infra #38345

Merged
merged 79 commits into from
Jan 2, 2025

Conversation

ayushpahwa
Copy link
Contributor

@ayushpahwa ayushpahwa commented Dec 24, 2024

Description

This PR introduces the custom ESLint rule to handle floating promises in our JS Objects (using async function calls without await inside async functions). Along with this, some refactors were needed

  1. Function to dynamically enable/disable the rules based on context ( using editor type as the context for now, can be updated as per requirements).
  2. Generate a list of async functions from the global data input for evaluation.
  3. Package added for estree types.

Note: This custom rule is only used in our EE codebase for now. We can enable it in CE in case the requirement arises.

Fixes #37255

Automation

/test sanity js

🔍 Cypress test results

Tip

🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/12558146123
Commit: 36b6610
Cypress dashboard.
Tags: @tag.Sanity, @tag.JS
Spec:


Tue, 31 Dec 2024 10:54:23 UTC

Communication

Should the DevRel and Marketing teams inform users about this change?

  • Yes
  • No

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a custom ESLint rule to enforce proper Promise handling
    • Enhanced linting capabilities with more granular error detection
  • Improvements

    • Updated linting utility functions to support more flexible configuration
    • Introduced more detailed context-aware linting options
  • Development

    • Added TypeScript type definitions for ESTree
    • Expanded test coverage for linting utilities
  • Chores

    • Refactored linting-related utility functions and constants

@github-actions github-actions bot added the Enhancement New feature or request label Dec 24, 2024
Copy link

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/12479987595.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 38345.
recreate: .

Copy link

Deploy-Preview-URL: https://ce-38345.dp.appsmith.com

@ayushpahwa
Copy link
Contributor Author

/build-deploy-preview skip-tests=true

Copy link

Deploying Your Preview: https://github.com/appsmithorg/appsmith/actions/runs/12484477041.
Workflow: On demand build Docker image and deploy preview.
skip-tests: true.
env: ``.
PR: 38345.
recreate: .

Copy link

Deploy-Preview-URL: https://ce-38345.dp.appsmith.com

@github-actions github-actions bot added Task A simple Todo Workflows Pod Issues that the workflows team owns Workflows Product Issues related to the workflows product labels Dec 31, 2024
@ayushpahwa ayushpahwa added the ok-to-test Required label for CI label Dec 31, 2024
@ayushpahwa ayushpahwa marked this pull request as ready for review December 31, 2024 09:53
@ayushpahwa ayushpahwa requested a review from KelvinOm as a code owner December 31, 2024 09:53
@ayushpahwa ayushpahwa requested review from ankitakinger, KelvinOm, AmanAgarwal041 and sneha122 and removed request for KelvinOm December 31, 2024 09:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (7)
app/client/src/plugins/Linting/customRules/no-floating-promises.ts (1)

80-119: Enhance Handling of Async Calls

It might be worthwhile to also enforce calling .catch() on async calls to avoid unhandled errors, especially if an error is not handled by .then().

app/client/src/plugins/Linting/utils/getLintingErrors.ts (2)

98-101: Avoid Assignments Within Array Callbacks

Using assignments directly in arrow function expressions can be confusing. Consider using a for-loop or separate statements for better readability and maintainability.

- libAccessors.forEach((accessor) => (globalData[accessor] = "readonly"));
- objectKeys(SUPPORTED_WEB_APIS).forEach((apiName) => (globalData[apiName] = "readonly"));
+ for (const accessor of libAccessors) {
+   globalData[accessor] = "readonly";
+ }
+ for (const apiName of objectKeys(SUPPORTED_WEB_APIS)) {
+   globalData[apiName] = "readonly";
+ }
🧰 Tools
🪛 Biome (1.9.4)

[error] 98-98: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 101-101: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


152-155: Refrain From Assignments in Expressions

Reiterating the suggestion above: consider rewriting these array callbacks with simpler loops.

🧰 Tools
🪛 Biome (1.9.4)

[error] 152-152: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 155-155: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

app/client/src/ce/utils/lintRulesHelpers.ts (1)

10-10: Refactor the Empty Object Parameter

If you’re not using properties from ContextType, remove or rename the destructured parameter for clarity.

-export const getLintRulesBasedOnContext = ({}: ContextType): Record<string, "off" | "error"> => {
+export const getLintRulesBasedOnContext = (_: ContextType): Record<string, "off" | "error"> => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 10-10: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

app/client/src/ce/hooks/index.ts (1)

21-23: Optional Handling for Unmatched Paths

The usage of matchPath is straightforward. Consider handling logging or a default case if no routes match.

app/client/src/plugins/Linting/tests/generateLintingGlobalData.test.ts (1)

14-99: Good Coverage, Consider Edge Cases

The tests validate common flows (normal, empty, unsupported). You could add negative or malformed data scenarios for deeper validation.

app/client/src/plugins/Linting/constants.ts (1)

13-15: Signature expansion looks appropriate.
Accepting asyncFunctions and editorType supports more dynamic context for linting.

To enhance clarity, update the function’s docstring (if available) to describe both new parameters.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de2d5a4 and 36b6610.

⛔ Files ignored due to path filters (1)
  • app/client/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • app/client/package.json (1 hunks)
  • app/client/src/ce/hooks/index.ts (2 hunks)
  • app/client/src/ce/utils/lintRulesHelpers.ts (1 hunks)
  • app/client/src/ee/utils/lintRulesHelpers.ts (1 hunks)
  • app/client/src/plugins/Linting/constants.ts (3 hunks)
  • app/client/src/plugins/Linting/customRules/no-floating-promises.ts (1 hunks)
  • app/client/src/plugins/Linting/tests/generateLintingGlobalData.test.ts (1 hunks)
  • app/client/src/plugins/Linting/tests/getLintingErrors.test.ts (2 hunks)
  • app/client/src/plugins/Linting/utils/getLintingErrors.ts (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • app/client/src/ee/utils/lintRulesHelpers.ts
🧰 Additional context used
🪛 Biome (1.9.4)
app/client/src/ce/utils/lintRulesHelpers.ts

[error] 10-10: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

app/client/src/plugins/Linting/utils/getLintingErrors.ts

[error] 98-98: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 101-101: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 117-117: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)


[error] 152-152: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)


[error] 155-155: The assignment should not be in an expression.

The use of assignments in expressions is confusing.
Expressions are often considered as side-effect free.

(lint/suspicious/noAssignInExpressions)

🔇 Additional comments (15)
app/client/src/plugins/Linting/customRules/no-floating-promises.ts (1)

4-17: Rule Metadata Is Well-Defined

The metadata and documentation fields look clear and properly structured.

app/client/src/plugins/Linting/utils/getLintingErrors.ts (2)

45-48: No Issues Found

These imports appear necessary and properly placed for the new logic.


372-382: No Issues Found

Integration with generateLintingGlobalData works as intended.

app/client/src/ce/hooks/index.ts (1)

33-36: No Issues Found

The custom hook’s simplicity helps keep logic clear by delegating to getEditorType.

app/client/src/plugins/Linting/tests/generateLintingGlobalData.test.ts (1)

5-12: Mocks Are Well-Implemented

Mocking getEditorType keeps these tests self-contained and ensures consistent behavior.

app/client/src/plugins/Linting/constants.ts (5)

4-5: Good separation of concerns.
The imports for noFloatingPromisesLintRule and getLintRulesBasedOnContext keep the custom logic modular.


45-45: Context-driven rules.
Using getLintRulesBasedOnContext to load rules for the given editorType is flexible and future-proof.


50-57: Great approach to customizing ESLint via settings.
Providing both globalData and asyncFunctions here allows your custom rules to reference them easily.


58-64: Kudos for defining customRules plugin.
Registering "no-floating-promises" under a dedicated plugin helps keep rules organized.


66-66: Ensure consistent overrides.
Merging extraRules with existing ESLint rules is correct. Confirm that none of the existing rules conflict with your custom rules.

app/client/src/plugins/Linting/tests/getLintingErrors.test.ts (4)

1-4: Neat import for accurate script type detection.
EvaluationScriptType and getScriptType unify the test logic with production usage.


9-15: Context-based mocking.
Mocking getLintRulesBasedOnContext confirms your new rule loading approach without interfering with other tests.


612-617: Focused test suite.
The new "Custom lint checks" section clarifies your custom ESLint rule coverage.


617-678: Test ensures unhandled Promise detection.
Validates that the custom rule flags an async call without await. This is a key feature for Promise safety.

app/client/package.json (1)

264-264: Type definitions added.
Including @types/estree supports AST-based lint rules effectively. Great addition for type safety.


if (
!!dataValue &&
dataValue.hasOwnProperty("ENTITY_TYPE") &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use Object.hasOwn Instead of hasOwnProperty

Avoid calling hasOwnProperty directly from the object to prevent prototype pollution issues.

- dataValue.hasOwnProperty("ENTITY_TYPE")
+ Object.hasOwn(dataValue, "ENTITY_TYPE")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dataValue.hasOwnProperty("ENTITY_TYPE") &&
Object.hasOwn(dataValue, "ENTITY_TYPE") &&
🧰 Tools
🪛 Biome (1.9.4)

[error] 117-117: Do not access Object.prototype method 'hasOwnProperty' from target object.

It's recommended using Object.hasOwn() instead of using Object.hasOwnProperty().
See MDN web docs for more details.

(lint/suspicious/noPrototypeBuiltins)

import type { Rule } from "eslint";
import type * as ESTree from "estree";

export const noFloatingPromisesLintRule: Rule.RuleModule = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a package for custom rules. However, I'm not entirely sure that these rules are necessary at all. Why can't we just use this plugin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @KelvinOm thanks for pointing out the eslint plugin, I'll try them out. As for the package for custom rules, these are for the developers building the TS code of appsmith. The eslint rule that I added is for the appsmith users who write JS code inside the JSObjects of apps, packages and workflows. If we mix them, there might be confusion related to how the rules are being applied. However we do need to communicate this properly, would updates to readme help here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to see if the plugins can work. The issue seems to be in the way we lint our code in appsmith. Each function is linted one at a time with limited scope of the rest of the world. Also most of the async functions like Query.run etc are not part of the defined codebase that gets linted, it needs to be passed down as global context. Hence these out of the box plugins don't fit in this particular case.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! LGTM!

@ayushpahwa ayushpahwa requested a review from KelvinOm January 2, 2025 06:32
@ayushpahwa ayushpahwa merged commit 9c81647 into release Jan 2, 2025
56 checks passed
@ayushpahwa ayushpahwa deleted the feat/eslint/custom-rule-floating-promises branch January 2, 2025 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature or request ok-to-test Required label for CI Task A simple Todo Workflows Pod Issues that the workflows team owns Workflows Product Issues related to the workflows product
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Task]: Add custom rule for to lint floating promises
2 participants