.windsurf/skills/eslint-rules/SKILL.md
Creating and maintaining custom ESLint rules for Formidable Forms. Use when adding new custom ESLint rules, modifying existing ones, or debugging rule behavior.
npx skillsauth add strategy11/formidable-forms eslint-rulesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Workflow for creating and maintaining custom ESLint rules in the Formidable Forms plugin.
Custom ESLint rules live in /eslint-rules/ at the project root.
eslint-rules/
├── index.js # Plugin entry point, exports all rules
└── rules/ # Individual rule files
├── prefer-strict-comparison.js
├── no-redundant-undefined-check.js
├── prefer-includes.js
├── no-typeof-undefined.js
├── no-optional-chaining-queryselectorall.js
├── no-repeated-selector.js
└── prefer-document-fragment.js
The plugin is imported in eslint.config.mjs as formidable and rules are referenced as formidable/<rule-name>.
The /eslint-rules/ directory is excluded from releases via:
.gitattributes: export-ignorebin/zip-plugin.sh: -x "*/eslint-rules/*"This mirrors the pattern used for /phpcs-sniffs/.
Enforces === and !== instead of == and != when comparing against non-empty, non-numeric string literals. Mirrors the PHP sniff PreferStrictComparisonSniff.
'string', 'post')'', '0', '123', '1.5'Detects x !== undefined && x patterns where the undefined check is redundant because the truthy check already covers it.
expr !== undefined && expr becomes just exprDetects .indexOf() comparisons with -1 and suggests .includes() instead. Catches yoda-style patterns that unicorn/prefer-includes misses (e.g., -1 !== [].indexOf(x)).
arr.indexOf(x) !== -1 and yoda -1 !== arr.indexOf(x)arr.indexOf(x) === -1 and yoda -1 === arr.indexOf(x)arr.indexOf(x) > -1 and yoda -1 < arr.indexOf(x)arr.indexOf(x) >= 0Detects typeof x === 'undefined' and yoda 'undefined' === typeof x patterns. Replaces with direct x === undefined comparison. Catches yoda-style patterns that unicorn/no-typeof-undefined misses.
typeof x === 'undefined' / typeof x == 'undefined''undefined' === typeof x / 'undefined' == typeof x (yoda)===/!== and ==/!= variantsPrevents unnecessary optional chaining (?.) on DOM methods that always return a value (never null/undefined). Methods like querySelectorAll, getElementsByClassName, and children always return a collection, so optional chaining is redundant.
document.querySelectorAll(...)?.forEachelement.getElementsByClassName(...)?.[0]element.children?.lengthDetects repeated calls to querySelector or querySelectorAll with the same selector string in the same function scope. Suggests caching the result in a variable.
document.querySelector('.item') calls should be cachedDetects appendChild, append, or prepend calls inside loops. Suggests using DocumentFragment to batch DOM operations and prevent multiple reflows.
for, for...of, for...in, while, forEach, mapCreate a new file in eslint-rules/rules/<rule-name>.js. Follow the existing pattern:
'use strict';
module.exports = {
meta: {
type: 'suggestion', // 'suggestion', 'problem', or 'layout'
docs: {
description: 'Description of what the rule enforces.',
},
fixable: 'code', // 'code' if auto-fixable, null otherwise
schema: [], // JSON Schema for rule options
messages: {
messageId: 'Error message with {{placeholder}}.',
},
},
create( context ) {
const sourceCode = context.sourceCode;
return {
// AST node visitor(s)
BinaryExpression( node ) {
// Rule logic
context.report({
node,
messageId: 'messageId',
data: { placeholder: 'value' },
fix( fixer ) {
return fixer.replaceText( node, 'replacement' );
},
});
},
};
},
};
Add the rule to eslint-rules/index.js:
const newRule = require( './rules/new-rule' );
module.exports = {
rules: {
// ... existing rules
'new-rule': newRule,
},
};
Add to the rules section in eslint.config.mjs:
'formidable/new-rule': 'error',
# Check for violations (requires nvm for node)
export PATH="$HOME/.nvm/versions/node/v20.19.2/bin:$PATH"
./node_modules/.bin/eslint .
# Auto-fix all violations
./node_modules/.bin/eslint . --fix
Or use the npm scripts:
npm run lint
npm run lint:fix
--fix so they can be applied to the existing codebase automatically/phpcs-sniffs/)Use https://astexplorer.net/ with the espree parser to inspect AST node types when developing rules. This helps identify the correct node visitors and property names.
Cascade automatically invokes this skill when your request involves custom ESLint rules.
To manually invoke:
@eslint-rules
development
Bug-fixing workflow for Formidable Forms. Use when fixing bugs, debugging unexpected behavior, investigating error logs, or resolving compatibility issues.
development
Maintainer-only workflow for handling GitHub Secret Scanning alerts on OpenClaw. Use when Codex needs to triage, redact, clean up, and resolve secret leakage found in issue comments, issue bodies, PR comments, or other GitHub content.
development
Maintainer workflow for OpenClaw releases, prereleases, changelog release notes, and publish validation. Use when Codex needs to prepare or verify stable or beta release steps, align version naming, assemble release notes, check release auth requirements, or validate publish-time commands and artifacts.
development
Run, watch, debug, and extend OpenClaw QA testing with qa-lab and qa-channel. Use when Codex needs to execute the repo-backed QA suite, inspect live QA artifacts, debug failing scenarios, add new QA scenarios, or explain the OpenClaw QA workflow. Prefer the live OpenAI lane with regular openai/gpt-5.4 in fast mode; do not use gpt-5.4-pro or gpt-5.4-mini unless the user explicitly overrides that policy.