eslint-plugin-sting-react
v0.1.1-0
Published
ESLint plugin for validating @chargebee/sting-react component usage
Maintainers
Readme
eslint-plugin-sting-react
ESLint plugin for validating @chargebee/sting-react component usage.
Features
✅ Validate Component Names - Ensures only valid Sting React components are used
✅ Validate Props - Checks if props exist for each component
✅ Validate Prop Values - Validates enum values (variant, size, mode) against component definitions
✅ Enforce Sting Components - Prevents usage of native HTML elements when Sting alternatives exist
✅ Import Validation - Ensures components are imported from the correct package
✅ Design System Consistency - Warns against inline styles and encourages design tokens
✅ Accessibility Checks - Enforces accessibility best practices for all components
✅ Deprecation Warnings - Alerts about deprecated props with auto-fix support
✅ Auto-fix Support - Automatically fixes many common issues
✅ Smart Suggestions - Provides helpful suggestions for typos and invalid values
✅ Works in any editor - Runs during eslint / npm run lint
✅ CI/CD Ready - Catch errors before they reach production
Installation
npm install --save-dev eslint-plugin-sting-reactUsage
ESLint Configuration (Flat Config - ES Lint 9+)
// eslint.config.js
import stingReact from 'eslint-plugin-sting-react';
export default [
{
plugins: {
'sting-react': stingReact,
},
rules: {
'sting-react/valid-component-name': 'error',
'sting-react/valid-prop-name': 'error',
'sting-react/valid-prop-value': 'error',
'sting-react/prefer-sting-components': 'warn',
'sting-react/require-sting-import': 'error',
'sting-react/no-inline-styles': 'warn',
'sting-react/accessible-component-usage': 'warn',
'sting-react/no-deprecated-props': 'warn',
},
},
];ESLint Configuration (Legacy - .eslintrc)
// .eslintrc.js
module.exports = {
plugins: ['sting-react'],
rules: {
'sting-react/valid-component-name': 'error',
'sting-react/valid-prop-name': 'error',
'sting-react/valid-prop-value': 'error',
'sting-react/prefer-sting-components': 'warn',
'sting-react/require-sting-import': 'error',
'sting-react/no-inline-styles': 'warn',
'sting-react/accessible-component-usage': 'warn',
'sting-react/no-deprecated-props': 'warn',
},
};Or Use Recommended Config
// .eslintrc.js
module.exports = {
extends: ['plugin:sting-react/recommended'],
};Configurations
This plugin provides three preset configurations:
Recommended (Default)
Balanced configuration for teams adopting Sting design system:
{
"extends": ["plugin:sting-react/recommended"]
}Strict
Stricter rules for teams fully committed to the design system:
{
"extends": ["plugin:sting-react/strict"]
}Migration
Relaxed configuration for teams migrating from native HTML to Sting components:
{
"extends": ["plugin:sting-react/migration"]
}Rules
sting-react/valid-component-name
Ensures that only valid Sting React component names are used.
❌ Invalid
import { Sbutton } from '@chargebee/sting-react';
<Sbutton>Click</Sbutton> // Error: Invalid component name
<SUnknownComponent /> // Error: Unknown component. Did you mean: SButton?✅ Valid
import { SButton } from '@chargebee/sting-react';
<SButton>Click</SButton>sting-react/prefer-sting-components
Enforces the use of Sting React components instead of native HTML elements across all files in the project.
This rule ensures consistent use of the Sting Design System by flagging any usage of native HTML elements that have Sting component alternatives, regardless of whether @chargebee/sting-react is imported.
❌ Invalid
// Error: Use <SButton> from @chargebee/sting-react instead of native <button> element.
<button onClick={handleClick}>Click</button>
// Error: Use <SInput> from @chargebee/sting-react instead of native <input> element.
<input type="text" placeholder="Name" />
// Error: Use <SSelect> from @chargebee/sting-react instead of native <select> element.
<select>
<option value="1">Option 1</option>
</select>
// Error: Use <STextarea> from @chargebee/sting-react instead of native <textarea> element.
<textarea rows={4} />
// Error: Use <SLabel> from @chargebee/sting-react instead of native <label> element.
<label htmlFor="input">Name</label>✅ Valid
import { SButton, SInput, SSelect, STextarea, SLabel } from '@chargebee/sting-react';
<SButton onClick={handleClick}>Click</SButton>
<SInput placeholder="Name" />
<SSelect options={options} />
<STextarea rows={4} />
<SLabel>Name</SLabel>
// Other HTML elements without Sting alternatives are allowed
<div>
<span>Text</span>
<p>Paragraph</p>
<form>...</form>
</div>Covered Elements
This rule applies to the following HTML elements that have Sting component equivalents:
<button>→ Use<SButton><input>→ Use<SInput><select>→ Use<SSelect><textarea>→ Use<STextarea><label>→ Use<SLabel>
Why This Rule?
This rule enforces design system consistency by preventing the use of native HTML form elements anywhere in the codebase, ensuring all user interface elements follow the Sting Design System guidelines.
sting-react/require-sting-import
Ensures Sting components are imported from the correct package (@chargebee/sting-react).
Auto-fixable: ✅
❌ Invalid
// Wrong package
import { SButton } from '@chargebee/sting';
import { SButton } from 'sting-react';
// Missing import
<SButton>Click</SButton> // Used without import✅ Valid
import { SButton } from '@chargebee/sting-react';
<SButton>Click</SButton>sting-react/no-inline-styles
Discourages inline styles on Sting components to maintain design system consistency.
❌ Invalid
import { SButton, SInput } from '@chargebee/sting-react';
<SButton style={{ backgroundColor: 'red', padding: '10px' }}>Click</SButton>
<SInput style={{ width: '100%' }} />✅ Valid
import { SButton, SInput } from '@chargebee/sting-react';
<SButton variant="primary" className="custom-button">Click</SButton>
<SInput className="w-full" />Configuration
{
"sting-react/no-inline-styles": ["warn", {
"allowedComponents": ["SCard"],
"severity": "warn"
}]
}sting-react/accessible-component-usage
Ensures accessibility best practices when using Sting components.
❌ Invalid
import { SInput, SButton, SIcon, SModal } from '@chargebee/sting-react';
// Input without label
<SInput placeholder="Email" />
// Icon-only button without aria-label
<SButton><SIcon name="close" /></SButton>
// Modal without aria-label
<SModal>Content</SModal>✅ Valid
import { SInput, SButton, SIcon, SModal } from '@chargebee/sting-react';
<SInput aria-label="Email address" placeholder="Email" />
<SButton aria-label="Close"><SIcon name="close" /></SButton>
<SModal aria-label="Confirmation dialog">Content</SModal>Configuration
{
"sting-react/accessible-component-usage": ["warn", {
"strictMode": true
}]
}sting-react/no-deprecated-props
Warns about deprecated props and provides auto-fixes to migrate to new prop names.
Auto-fixable: ✅
❌ Invalid
import { SButton, SModal, SInput, SBadge } from '@chargebee/sting-react';
<SButton type="primary">Click</SButton> // Deprecated
<SModal visible={true}>Content</SModal> // Deprecated
<SInput error={true} /> // Deprecated
<SBadge color="success">New</SBadge> // Deprecated✅ Valid
import { SButton, SModal, SInput, SBadge } from '@chargebee/sting-react';
<SButton variant="primary">Click</SButton>
<SModal open={true}>Content</SModal>
<SInput isInvalid={true} />
<SBadge variant="success">New</SBadge>sting-react/valid-prop-name
Ensures that only valid props are used on Sting React components.
❌ Invalid
import { SButton } from '@chargebee/sting-react';
<SButton color="blue">Click</SButton>
// Error: Invalid prop "color" on component "SButton". Did you mean: icon?
<STooltip>
<span>Hover me</span>
</STooltip>
// Error: Missing required prop "content" on component "STooltip"✅ Valid
import { SButton, STooltip } from '@chargebee/sting-react';
<SButton variant="primary">Click</SButton>
<STooltip content="This is a tooltip">
<span>Hover me</span>
</STooltip>sting-react/valid-prop-value
Ensures that prop values match the allowed values for the component.
❌ Invalid
import { SButton, SBadge } from '@chargebee/sting-react';
<SButton variant="huge">Click</SButton>
// Error: Invalid value "huge" for prop "variant" on component "SButton".
// Expected one of: "primary", "primary-outline", "primary-ghost", "neutral",
// "neutral-ghost", "danger", "danger-outline", "danger-ghost"
<SBadge size="massive">Text</SBadge>
// Error: Invalid value "massive" for prop "size" on component "SBadge".
// Expected one of: "regular", "medium", "large"✅ Valid
import { SButton, SBadge } from '@chargebee/sting-react';
<SButton variant="primary">Click</SButton>
<SButton variant="danger">Delete</SButton>
<SBadge size="regular">Text</SBadge>
<SBadge size="large">Large Text</SBadge>Supported Components
The plugin validates all Sting React components including:
- Form Components: SButton, SInput, SCheckbox, SRadioGroup, SSelect, SSwitch, STextarea, SLabel, SFileUpload
- Layout Components: SCard, SModal, SDrawer, SAccordion, STabs, SContainedList
- Feedback Components: SBanner, SToast, SNotification, SSpinner, SModalLoader, SInlineError
- Navigation: SBreadcrumb, SPageHeader
- Data Display: STable, SBadge
- Overlays: STooltip, SPopover, SDropdown
- Progress: SProgressTracker
- Utilities: SIcon, SPlaceholder
Configuration Options
Rule Severity
You can configure the severity level for each rule:
{
rules: {
'sting-react/valid-component-name': 'error', // or 'warn', 'off'
'sting-react/valid-prop-name': 'warn',
'sting-react/valid-prop-value': 'error',
}
}Examples
Valid Code
import {
SButton,
SBadge,
SModal,
SInput,
STooltip
} from '@chargebee/sting-react';
function MyComponent() {
return (
<div>
{/* ✅ Valid: Correct component, props, and values */}
<SButton variant="primary" size="large">
Submit
</SButton>
{/* ✅ Valid: All prop values are correct */}
<SBadge variant="success" size="regular" mode="light">
Active
</SBadge>
{/* ✅ Valid: Required props provided */}
<STooltip content="Click to save">
<SButton variant="primary">Save</SButton>
</STooltip>
{/* ✅ Valid: Sub-components work */}
<SModal>
<SModal.Content>
<SModal.Header>Title</SModal.Header>
</SModal.Content>
</SModal>
</div>
);
}Invalid Code (Will Show Errors)
import { SButton, SBadge } from '@chargebee/sting-react';
function MyComponent() {
return (
<div>
{/* ❌ Error: Invalid component name */}
<Sbutton>Click</Sbutton>
{/* ❌ Error: Invalid prop "color" */}
<SButton color="blue">Click</SButton>
{/* ❌ Error: Invalid value "massive" for prop "size" */}
<SButton size="massive">Click</SButton>
{/* ❌ Error: Invalid value "purple" for prop "variant" */}
<SBadge variant="purple">Text</SBadge>
{/* ❌ Error: Missing required prop "content" */}
<STooltip>
<span>Hover me</span>
</STooltip>
</div>
);
}Running the Linter
In Development
npm run lintFix Auto-Fixable Issues
npm run lint -- --fixIn CI/CD
# In your CI/CD pipeline
npm run lint -- --max-warnings 0Integration with IDEs
VS Code
Errors will show up automatically in VS Code if you have the ESLint extension installed:
- Install the ESLint extension
- Errors will appear as red squiggly lines
- Hover over errors to see suggestions
Other Editors
Most modern editors support ESLint integration:
- WebStorm/IntelliJ: Built-in ES Lint support
- Sublime Text: via SublimeLinter-eslint
- Vim/Neovim: via ALE or coc-eslint
Benefits
1. Catch Errors Early
// This error is caught during linting, not at runtime!
<SButton varient="primary">Click</SButton>
// Error: Invalid prop "varient". Did you mean: variant?2. Enforced Standards
Ensure the entire team uses components correctly:
$ npm run lint
error Invalid value "huge" for prop "variant" sting-react/valid-prop-value
✖ 1 problem (1 error, 0 warnings)3. CI/CD Integration
Prevent invalid code from being merged:
# .github/workflows/ci.yml
- name: Lint
run: npm run lint4. Better DX
Get helpful suggestions for typos:
<SButton varaint="primary">
// Error: Invalid prop "varaint". Did you mean: variant?Updating Component Metadata
The component metadata is automatically extracted from @chargebee/sting-react component definitions. When component props change, simply run:
npm run generate-metadataThis will:
- Scan all components in
packages/sting-react/src/components/ - Extract TypeScript interface definitions
- Extract variant values from
tailwind-variantsconstants - Generate
src/utils/generatedComponentMetadata.tswith up-to-date metadata
The metadata generation is automatically run before each build via the prebuild script.
How Metadata Extraction Works
The plugin uses the TypeScript Compiler API to parse component files and extract:
- Props from TypeScript interfaces: Analyzes interfaces like
SButtonProps,BadgeProps, etc. - Type information: Captures the type of each prop (string, boolean, React.ReactNode, etc.)
- Required/optional status: Determines if props are required based on the
?token - Union type values: Extracts valid values from string literal unions (e.g.,
"primary" | "secondary") - Variant values from constants: Parses
tailwind-variantstv()calls to extract variant options
Development
Scripts
npm run build- Build the plugin (also runs metadata generation)npm run generate-metadata- Extract component metadata from Sting Reactnpm run watch- Watch mode for developmentnpm test- Run testsnpm run lint- Lint the codebase
Building
npm install
npm run buildTesting
npm testLocal Development
# In the plugin directory
npm link
# In your project
npm link eslint-plugin-sting-reactProject Structure
eslint-plugin-sting-react/
├── scripts/
│ ├── extractComponentMetadata.ts # Metadata extraction script
│ └── tsconfig.json # TypeScript config for scripts
├── src/
│ ├── rules/ # ESLint rule implementations
│ ├── utils/
│ │ ├── componentMetadata.ts # Main metadata utilities
│ │ └── generatedComponentMetadata.ts # Auto-generated (DO NOT EDIT)
│ └── index.ts # Plugin entry point
└── package.jsonTroubleshooting
Plugin Not Working
Check ESLint is installed
npm list eslintVerify plugin is in configuration
// .eslintrc.js plugins: ['sting-react']Check file is being linted
npm run lint -- path/to/file.tsx
Rules Not Triggering
Make sure you import from
@chargebee/sting-react- The plugin only validates components imported from this package
Check rule is enabled
rules: { 'sting-react/valid-component-name': 'error' // Not 'off' }
False Positives
If you're getting false positives, please open an issue with:
- The code that's causing the error
- Expected behavior
- Actual error message
License
ISC
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Made with ❤️ by Chargebee
