npx skills add ...
npx skills add sap/open-ux-tools --skill eslint-rule-development
Develop a new ESLint rule for @sap-ux/eslint-plugin-fiori-tools. Always use this skill when adding any rule to the plugin, implementing manifest.json/XML/CDS/flex-change validation, writing rule tests with project context, or creating rule documentation — even if the request sounds simple or the user doesn't mention 'ESLint' explicitly. Guides through the complete workflow: diagnostics constant, rule implementation, unit tests, docs, and registration.
npx skills add sap/open-ux-tools --skill eslint-rule-development
Add a new ESLint rule to @sap-ux/eslint-plugin-fiori-tools following the established patterns in the monorepo.
Read tool directly — never Bash cat or spawn Explore subagents for file reading| Type | Use when | Reference file |
|---|---|---|
| Annotation rule | Validates UI.* OData annotations in .xml / .cds files. Also, covers rules where text, that is, a label, title, or description, can be an i18n binding | references/annotation.md |
| Manifest JSON rule | Validates manifest.json properties | references/manifest-json.md |
| Flex change file rule | Validates webapp/changes/*.change (Applicable only to OData V2 flex change properties) | references/flex-change.md |
| JavaScript / TypeScript rule | Validates JS/TS application source code (UI5 patterns, global variables, deprecated APIs) | references/js-ts-rule.md |
Infer from the request:
sap-[kebab-case-name] patternerror or warn. Rules in recommended-for-s4hana MUST be warn.OData version determines the linker file — always follow the stated version:
| OData version | Linker file | Manifest root |
|---|---|---|
| V2 only | src/project-context/linker/fe-v2.ts | sap.ui.generic.app.pages.* |
| V4 only | src/project-context/linker/fe-v4.ts | sap.ui5.routing.targets.* |
| Both | both linker files | both roots |
Read the matching reference file immediately, then read all the files it lists in a single parallel batch. If the rule spans multiple types (e.g. annotations + manifest, or manifest + flex changes), read all matching reference files and combine their templates and access patterns.
In packages/eslint-plugin-fiori-tools/src/language/diagnostics.ts:
Use the template from the reference file for your rule type.
Code quality requirements for every new or modified function:
@param, @returns) to every new function. When modifying an existing function, update its JSDoc to reflect any signature or behaviour changes.sonarjs/cognitive-complexity. If a function exceeds 15, extract branches or loops into well-named helper functions until the complexity falls within the limit. Do not inline complex logic in a single function to avoid this.src/rules/index.ts — add import + entry (alphabetical order):
src/index.ts — add to fioriLanguageConfig rules (Fiori language rules) or baseFioriToolsRules (JS/TS rules):
If the rule should also apply to S/4HANA projects, add it to the recommended-for-s4hana config in src/index.ts as well. Rules in this config must use 'warn' severity:
Use the test template from the reference file; run only the new test file (see efficiency rules at the top).
Always check message, never messageId, in errors arrays. When a rule message contains interpolated data (e.g. {{tableType}}), checking only messageId would accept any value for that placeholder and miss regressions. Use the fully resolved string instead:
If tests show 0 errors when violations are expected, check the debug checklist at the bottom of the reference file.
Create packages/eslint-plugin-fiori-tools/docs/rules/sap-[rule-name].md.
Read packages/eslint-plugin-fiori-tools/docs/rules/TEMPLATE.md for structure. Key sections:
In packages/eslint-plugin-fiori-tools/README.md, do two things:
Add your new rule at the top of the rules table with new in the version column:
Backfill any pending new versions: if any rows still show new from prior rule additions that have since been released, look up each rule's release version in packages/eslint-plugin-fiori-tools/CHANGELOG.md and replace new with that version. After this cleanup, your newly added rule should be the only row showing new.
Run lint:fix to auto-fix ESLint errors and apply Prettier formatting across all modified files, then verify no issues remain:
If lint reports errors after lint:fix:
sonarjs/cognitive-complexity — resolve per the cognitive complexity guidance in Step 3.prettier/prettier — a formatting issue could not be auto-fixed. Apply the suggested change manually (usually a line that is too long or a multiline expression that needs restructuring).@typescript-eslint/no-unsafe-* — replace any casts or untyped values with proper interfaces or unknown + type guards.Do not proceed to Step 9 until pnpm lint exits with code 0.
Then confirm all tests still pass:
Select @sap-ux/eslint-plugin-fiori-tools, choose minor for new rules:
Summarize what was done:
minor), summary line| Purpose | Path |
|---|---|
| Rule implementation | packages/eslint-plugin-fiori-tools/src/rules/sap-[name].ts |
| Rule registry | packages/eslint-plugin-fiori-tools/src/rules/index.ts |
| Plugin config & exports | packages/eslint-plugin-fiori-tools/src/index.ts |
| Diagnostic constants | packages/eslint-plugin-fiori-tools/src/language/diagnostics.ts |
| Annotation helper utilities | packages/eslint-plugin-fiori-tools/src/project-context/linker/annotations.ts |
i18n helper utilities (extractI18nKey, getAttrOrChildText, collectSectionLabelKeys, for example) | packages/eslint-plugin-fiori-tools/src/project-context/linker/i18n.ts |
| Annotation index key format | packages/eslint-plugin-fiori-tools/src/project-context/parser/service.ts (lines 50-57) |
| Linker types | packages/eslint-plugin-fiori-tools/src/project-context/linker/types.ts |
| V2 linker | packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v2.ts |
| V4 linker | packages/eslint-plugin-fiori-tools/src/project-context/linker/fe-v4.ts |
| Rule factory | packages/eslint-plugin-fiori-tools/src/language/rule-factory.ts |
| Rule fixer | packages/eslint-plugin-fiori-tools/src/language/rule-fixer.ts |
| Test helper | packages/eslint-plugin-fiori-tools/test/test-helper.ts |
| Rule docs template | packages/eslint-plugin-fiori-tools/docs/rules/TEMPLATE.md |
| README rules table | packages/eslint-plugin-fiori-tools/README.md |
check() context access by rule type| Rule type | Use in check() | Why |
|---|---|---|
| Annotation | linkedModel.apps for page iteration; index.apps[appKey] for getIndexedServiceForMainService | Only check annotations referenced from pages — never scan all entity annotations. pageNames must list only pages that reference the specific annotation. See annotation.md for access patterns and the page-annotation-map template. When text can be an i18n binding, use the two-pass design in annotation.md#when-the-text-field-can-be-an-i18n-binding. |
| Manifest JSON | linkedModel.apps for page iteration; index.apps[appKey] for parsedApp (manifest URI, manifestObject) | Requires linked pages to find manifest config paths |
| Flex change | context.sourceCode.projectContext.linkedModel.apps | Guard on FioriChangeSourceCode first; linked model provides change file config via page.lookup['table'] |
| JavaScript / TypeScript | Standard ESLint context — no projectContext | JS/TS rules don't use the Fiori project model; use Rule.RuleModule, not createFioriRule |