-
Notifications
You must be signed in to change notification settings - Fork 41
Add new FF ESLint rules #2981
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
Merged
Merged
Add new FF ESLint rules #2981
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
5b84916
format: run lint:fix
shervElmi 7d48853
Add ESLint rule to disallow unnecessary optional chaining on querySel…
shervElmi 43e2591
Add ESLint rule to detect and warn about repeated DOM queries with th…
shervElmi dbc1f29
Add ESLint rule to detect appendChild calls in loops and suggest usin…
shervElmi 04d0653
Add ESLint rules for DOM optimization and enforce JavaScript coding s…
shervElmi cdc6685
Update ESLint rules documentation with new DOM optimization rules and…
shervElmi befa405
Refactor: convert var declarations to const/let following ESLint pref…
shervElmi fd693fc
Add id prop to BaseControl in RadioControl component
shervElmi d0e07b5
Add eslint-disable-line comment for no-unused-expressions in Cypress …
shervElmi 9642772
Remove unused sourceCode variable from no-repeated-selector ESLint rule
shervElmi cf8960a
Initialize variables at declaration to prevent undefined values and i…
shervElmi 4de4244
Refactor addon-state.js to use array destructuring for cleaner elemen…
shervElmi d5257f8
Refactor code to use destructuring, optional chaining, and improve sw…
shervElmi bc28623
Disable ESLint rules to suppress DeepSource false positives for legac…
shervElmi a90c155
Replace double negation operator with explicit Boolean() conversion f…
shervElmi 2c7c958
Remove unused global comments and fix escaped forward slash in URL path
shervElmi ebc5d89
Remove unused global comment and consolidate multi-line template lite…
shervElmi f56c108
revert: undo manual JS-0119 init-declarations fixes
shervElmi f089f93
fix: suppress DeepSource JS-0085 and webpack resolver errors
shervElmi 8caed08
Add rule to prevent automatic commits in code change principles
shervElmi 6071ded
Suppress ESLint prefer-const warning for intentional let declaration …
shervElmi 85b3274
Add hasOwnProperty checks to for-in loops and optional chaining for o…
shervElmi 56ed772
Move fieldID declaration to top of checkValidity function and use des…
shervElmi 68994cc
Disable DeepSource short variable name rules to allow project convent…
shervElmi 4f731f5
Remove DeepSource short variable name rules and webpack resolver, add…
shervElmi 9ecd0e5
Remove eslint-import-resolver-webpack dependency and add trailing new…
shervElmi 4e2902c
Merge remote-tracking branch 'origin/HEAD' into feature/add-new-ff-es…
shervElmi fc73039
Replace Object.prototype.hasOwnProperty.call with Object.hasOwn and i…
shervElmi 2d78a29
Improve variable naming in scroll position calculation by replacing s…
shervElmi af49f23
npm run build
shervElmi 00cd712
Improve code formatting by adding const to imageSize declaration and …
shervElmi f2bca79
Move variable declarations to point of use and convert var to const/l…
shervElmi 8e98b19
Move variable declarations to point of use and convert var to const/l…
shervElmi e5f860c
Merge branch 'master' into feature/add-new-ff-eslint-rules
Crabcyborg b512e0a
Remove prefer template oxlint rule exception
Crabcyborg 4d12b4e
Prefer const
Crabcyborg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or 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 |
|---|---|---|
| @@ -1,28 +1,113 @@ | ||
| version = 1 | ||
|
|
||
| # 1. Global Exclude Patterns | ||
| # Tell DeepSource which files to completely ignore (e.g., external libraries). | ||
| exclude_patterns = [ | ||
| "**/node_modules/**", | ||
| "**/vendor/**", | ||
| "**/venv/**", | ||
| "**/dist/**", | ||
| "**/build/**" | ||
| "**/build/**", | ||
| "**/coverage/**", | ||
| "**/*.min.js", | ||
| "**/*.js.map", | ||
| "js/formidable_blocks.js", | ||
| "js/formidable_overlay.js", | ||
| "js/form-templates.js", | ||
| "js/formidable_dashboard.js", | ||
| "js/onboarding-wizard.js", | ||
| "js/addons-page.js", | ||
| "js/formidable_styles.js", | ||
| "js/formidable_admin.js", | ||
| "js/bootstrap-multiselect.js", | ||
| "js/formidable-settings-components.js", | ||
| "js/formidable-web-components.js", | ||
| "js/frm_testing_mode.js", | ||
| "js/welcome-tour.js", | ||
| "eslint-rules/**" | ||
| ] | ||
|
|
||
| # 2. Test Patterns | ||
| # Tell DeepSource which files are tests. This prevents false positives | ||
| # (e.g., allowing "assert" statements in test files). | ||
| test_patterns = [ | ||
| "**/tests/**" | ||
| "**/tests/**", | ||
| "**/phpunit/**", | ||
| "**/cypress/**" | ||
| ] | ||
|
|
||
| # 3. Analyzers | ||
| # Add a block like this for every language you use. | ||
| [[analyzers]] | ||
| name = "php" | ||
| enabled = true | ||
|
|
||
| [[analyzers]] | ||
| name = "javascript" | ||
| enabled = true | ||
|
|
||
| [analyzers.meta] | ||
| plugins = ["react"] | ||
| dependency_file_paths = ["package.json"] | ||
| environment = [ | ||
| "browser", | ||
| "jquery", | ||
| "cypress", | ||
| "mocha" | ||
| ] | ||
| module_system = "es-modules" | ||
| globals = [ | ||
| # WordPress core globals | ||
| "ajaxurl", | ||
| "wp", | ||
| "wpApiSettings", | ||
| "tinyMCE", | ||
| "tb_remove", | ||
| "adminpage", | ||
| "pagenow", | ||
| # Google reCAPTCHA / hCaptcha / Turnstile | ||
| "grecaptcha", | ||
| "hcaptcha", | ||
| "turnstile", | ||
| # Formidable globals (via wp_localize_script) | ||
| "frmGlobal", | ||
| "frm_js", | ||
| "frm_admin_js", | ||
| "frmDom", | ||
| "frmFormTemplatesVars", | ||
| "FrmDeactivationFeedbackI18n", | ||
| "formidable_form_selector", | ||
| "frmSettings", | ||
| "s11FloatingLinksData", | ||
| "frmPlugSearch", | ||
| "frmAddonsVars", | ||
| "frmOnboardingWizardVars", | ||
| "frmApplicationsVars", | ||
| "frmWelcomeTourVars", | ||
| "frm_stripe_vars", | ||
| "frm_trans_vars", | ||
| "frmSquareVars", | ||
| # Formidable window globals | ||
| "frmFrontForm", | ||
| "frmAdminBuild", | ||
| "frmAdminBuildJS", | ||
| "FrmFormsConnect", | ||
| "frmAdminPopup", | ||
| "frmOverlay", | ||
| "frmDropdownComponent", | ||
| "frmStylerFunctions", | ||
| "frmProForm", | ||
| "frmThemeOverride_jsErrors", | ||
| "frmThemeOverride_frmPlaceError", | ||
| "frmThemeOverride_frmAfterSubmit", | ||
| "frm_add_logic_row", | ||
| "frm_remove_tag", | ||
| "frm_show_div", | ||
| "frmCheckAll", | ||
| "frmCheckAllLevel", | ||
| "frmGetFieldValues", | ||
| "frmImportCsv", | ||
| "FormidablePSH", | ||
| "__FRMURLVARS", | ||
| "frm_password_checks", | ||
| "formidable_block_calculator", | ||
| "formidable_view_selector", | ||
| "frmdates_admin_js", | ||
| "frm_abdn" | ||
| ] |
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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
115 changes: 115 additions & 0 deletions
115
eslint-rules/rules/no-optional-chaining-queryselectorall.js
This file contains hidden or 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,115 @@ | ||
| 'use strict'; | ||
|
|
||
| /** | ||
| * Detects unnecessary optional chaining on querySelectorAll results. | ||
| * Since querySelectorAll always returns a NodeList (never null/undefined), | ||
| * and NodeList.forEach handles empty lists safely, the ?. is redundant. | ||
| */ | ||
| module.exports = { | ||
| meta: { | ||
| type: 'suggestion', | ||
| docs: { | ||
| description: 'Disallow unnecessary optional chaining on querySelectorAll and similar DOM methods that never return null.', | ||
| }, | ||
| fixable: 'code', | ||
| schema: [], | ||
| messages: { | ||
| unnecessaryChaining: 'Unnecessary optional chaining on {{method}}. {{reason}}', | ||
| }, | ||
| }, | ||
|
|
||
| create( context ) { | ||
| const sourceCode = context.sourceCode; | ||
|
|
||
| // Methods that always return a value (never null/undefined) | ||
| const alwaysReturns = new Set([ | ||
| 'querySelectorAll', | ||
| 'getElementsByClassName', | ||
| 'getElementsByTagName', | ||
| 'getElementsByName', | ||
| 'children', | ||
| ]); | ||
|
|
||
| return { | ||
| ChainExpression( node ) { | ||
| const { expression } = node; | ||
|
|
||
| // Check if this is a call expression with optional chaining | ||
| if ( expression.type !== 'CallExpression' || ! expression.optional ) { | ||
| return; | ||
| } | ||
|
|
||
| const { callee } = expression; | ||
|
|
||
| // Check if callee is a MemberExpression (e.g., document.querySelectorAll) | ||
| if ( callee.type !== 'MemberExpression' ) { | ||
| return; | ||
| } | ||
|
|
||
| const methodName = callee.property.name; | ||
|
|
||
| if ( ! alwaysReturns.has( methodName ) ) { | ||
| return; | ||
| } | ||
|
|
||
| let reason; | ||
| if ( methodName === 'querySelectorAll' ) { | ||
| reason = 'querySelectorAll always returns a NodeList. Use .querySelectorAll() without ?.'; | ||
| } else if ( methodName === 'children' ) { | ||
| reason = 'children always returns an HTMLCollection. Use .children without ?.'; | ||
| } else { | ||
| reason = `${ methodName } always returns a collection. Remove the ?.`; | ||
| } | ||
|
|
||
| context.report({ | ||
| node, | ||
| messageId: 'unnecessaryChaining', | ||
| data: { | ||
| method: methodName, | ||
| reason, | ||
| }, | ||
| fix( fixer ) { | ||
| // Remove the ?. by replacing the CallExpression with a non-optional version | ||
| const callText = sourceCode.getText( expression ); | ||
| const fixedText = callText.replace( /\?\.([([])/g, '$1' ); | ||
|
|
||
| return fixer.replaceText( node, fixedText ); | ||
| }, | ||
| }); | ||
| }, | ||
|
|
||
| // Also catch cases like: elements?.forEach where elements is from querySelectorAll | ||
| MemberExpression( node ) { | ||
| if ( ! node.optional ) { | ||
| return; | ||
| } | ||
|
|
||
| const { object } = node; | ||
|
|
||
| // Check if object is a direct call to querySelectorAll or similar | ||
| if ( object.type === 'CallExpression' && object.callee.type === 'MemberExpression' ) { | ||
| const methodName = object.callee.property.name; | ||
|
|
||
| if ( alwaysReturns.has( methodName ) ) { | ||
| context.report({ | ||
| node, | ||
| messageId: 'unnecessaryChaining', | ||
| data: { | ||
| method: methodName, | ||
| reason: `${ methodName } always returns a collection, so ${ node.property.name } will not be null.`, | ||
| }, | ||
| fix( fixer ) { | ||
| // Replace ?. with . | ||
| const objectText = sourceCode.getText( object ); | ||
| const propertyText = sourceCode.getText( node.property ); | ||
| const fixedText = `${ objectText }.${ propertyText }`; | ||
|
|
||
| return fixer.replaceText( node, fixedText ); | ||
| }, | ||
| }); | ||
| } | ||
| } | ||
| }, | ||
| }; | ||
| }, | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix may produce invalid code for computed properties.
If
node.computedis true (e.g.,result?.[0]), the fix outputsresult.0instead ofresult[0]. Consider preserving bracket notation for computed accesses.🐛 Proposed fix for computed property handling
fix( fixer ) { // Replace ?. with . const objectText = sourceCode.getText( object ); const propertyText = sourceCode.getText( node.property ); - const fixedText = `${ objectText }.${ propertyText }`; + const fixedText = node.computed + ? `${ objectText }[${ propertyText }]` + : `${ objectText }.${ propertyText }`; return fixer.replaceText( node, fixedText ); },📝 Committable suggestion
🤖 Prompt for AI Agents