lint-new

Create a new ESLint rule with tests for eslintPluginScraps. Use when asked to "create a lint rule", "add an eslint rule", "scaffold a rule", "write a new scraps rule", or "new design system lint rule". Covers rule creation, test authoring, registration, and autofix implementation.

getsentry/sentry13 installsApache-2.0Synced Aug 25

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: lint-new
description: Create a new ESLint rule with tests for eslintPluginScraps. Use when asked to "create a lint rule", "add an eslint rule", "scaffold a rule", "write a new scraps rule", or "new design system lint rule". Covers rule creation, test authoring, registration, and autofix implementation.
license: Apache-2.0
---

Create a new ESLint rule named `$ARGUMENTS` in the eslintPluginScraps plugin.

## Step 1: Choose Your Archetype

Read [references/rule-archetypes.md](references/rule-archetypes.md) and pick the archetype that matches your rule's intent:

| You want to...                              | Archetype              | Reference to load                                                |
| ------------------------------------------- | ---------------------- | ---------------------------------------------------------------- |
| Rewrite import paths                        | Import rewrite         | Inline — simple pattern                                          |
| Validate token/value usage per CSS property | Property validation    | [style-collector-guide.md](references/style-collector-guide.md)  |
| Restrict JSX elements in specific props     | JSX structural         | [rule-archetypes.md](references/rule-archetypes.md) §Archetype 3 |
| Detect patterns in static CSS text          | Template text analysis | [rule-archetypes.md](references/rule-archetypes.md) §Archetype 4 |

Read the relevant reference before writing code. The archetypes document which AST visitors to use, which shared utilities apply, and which patterns are NOT appropriate for each approach.

## Step 2: Check Shared Utilities

Before writing AST traversal logic, check `static/eslint/eslintPluginScraps/src/ast/` for reusable code:

| Utility                 | Location                                 | Use for                                                             |
| ----------------------- | ---------------------------------------- | ------------------------------------------------------------------- |
| `getStyledCallInfo`     | `src/ast/utils/styled.ts`                | Classifying styled/css calls as element, component, or css          |
| `createQuasiScanner`    | `src/ast/scanner/index.ts`               | Scanning static CSS text in template literals (Archetype 4)         |
| `createImportTracker`   | `src/ast/tracker/imports.ts`             | Resolving where a local name was imported from                      |
| `createStyleCollector`  | `src/ast/extractor/index.ts`             | Collecting CSS-in-JS _dynamic value_ declarations (NOT static text) |
| `shouldAnalyze`         | `src/ast/extractor/index.ts`             | Fast pre-scan to skip files without Emotion usage                   |
| `normalizePropertyName` | `src/ast/utils/normalizePropertyName.ts` | Normalizing CSS property names                                      |
| `decomposeValue`        | `src/ast/extractor/value-decomposer.ts`  | Breaking complex expressions into all possible values               |
| Theme tracker           | `src/ast/tracker/theme.ts`               | Tracking `useTheme()` and callback theme bindings                   |

If another rule already solves a similar problem, extract shared logic into `src/ast/utils/` and reuse it.

## Step 3: Create Files

1. **Rule**: `static/eslint/eslintPluginScraps/src/rules/$ARGUMENTS.ts`
2. **Test**: `static/eslint/eslintPluginScraps/src/rules/$ARGUMENTS.spec.ts`

### Rule Template

```typescript
import {ESLintUtils} from '@typescript-eslint/utils';

export const $RULE_NAME = ESLintUtils.RuleCreator.withoutDocs({
  meta: {
    type: 'problem',
    docs: {
      description: '[Rule description]',
    },
    fixable: 'code', // include if rule has autofix — see Autofix Guidance
    schema: [],
    messages: {
      forbidden: 'Error message shown to user',
    },
  },
  create(context) {
    return {
      // AST visitor methods — see your chosen archetype
    };
  },
});
```

If your rule needs configurable options, load [references/schema-patterns.md](references/schema-patterns.md).

### Test Template

```typescript
import {RuleTester} from '@typescript-eslint/rule-tester';

import {$RULE_NAME} from './$ARGUMENTS';

const ruleTester = new RuleTester();

ruleTester.run('$ARGUMENTS', $RULE_NAME, {
  valid: [
    {
      code: '// valid code',
      filename: '/project/src/file.tsx',
    },
  ],
  invalid: [
    {
      code: '// invalid code',
      filename: '/project/src/file.tsx',
      errors: [{messageId: 'forbidden'}],
      output: '// expected output after autofix', // REQUIRED for fixable rules
    },
  ],
});
```

Run tests:

```bash
pnpm test-ci "static/eslint/eslintPluginScraps/src/rules/$ARGUMENTS.spec.ts"
```

## Autofix Guidance

**Default stance: implement autofix** unless the transformation is ambiguous or could change runtime behavior.

### Safe autofix patterns

- Import path rewrites (see `no-core-import.ts` as canonical example)
- Adding/removing JSX attributes with known values
- Wrapping expressions in a known component
- Identifier renames with no shadowing risk

### Do NOT autofix when

- Multiple valid fixes exist and the right choice requires human judgment
- The fix requires type information not available from the AST alone
- The transformation alters control flow or runtime behavior
- The change spans multiple files

### Fixer API

```typescript
context.report({
  node,
  messageId: 'forbidden',
  fix(fixer) {
    return fixer.replaceText(node, newText);
    // Also: fixer.replaceTextRange([start, end], text)
    //        fixer.insertTextBefore(node, text)
    //        fixer.insertTextAfter(node, text)
    //        fixer.remove(node)
    // Return single fix or array of fixes
  },
});
```

When a rule is fixable, every invalid test case MUST include `output` showing the expected code after the fix.

## Step 4: Register the Rule

### 1. Rule Index

Add to `static/eslint/eslintPluginScraps/src/rules/index.ts`:

```typescript
import {$RULE_NAME} from './$ARGUMENTS';

export const rules = {
  // existing rules...
  $ARGUMENTS: $RULE_NAME,
};
```

### 2. ESLint Config

Add to `eslint.config.ts` inside the `name: 'plugin/@sentry/scraps'` block:

```typescript
'@sentry/scraps/$ARGUMENTS': 'error',
// or with options:
'@sentry/scraps/$ARGUMENTS': ['error', { /* options */ }],
```

### 3. Verify

```bash
pnpm test-ci "static/eslint/eslintPluginScraps/src/rules/$ARGUMENTS.spec.ts"
```

## Extending an Existing Rule

If modifying an existing rule rather than creating a new one:

1. Read the existing rule and its config files to understand the architecture
2. For **config-driven rules** (like `use-semantic-token`): changes often only require editing the config file (e.g., `src/config/tokenRules.ts`), not the rule logic
3. Watch for reverse-mapping side effects — adding a new category can change which category is _suggested_ for shared properties (last writer wins in `buildPropertyToRule`)
4. Update existing tests for any changed behavior, then add new test cases

## Naming Convention

- **Rule name** (kebab-case): `my-rule-name` — verb-noun pattern (e.g., `no-token-import`, `use-semantic-token`)
- **Export name** (camelCase): `myRuleName`
- **File name**: matches rule name exactly (`my-rule-name.ts`, `my-rule-name.spec.ts`)

More Design Systems skills

stitch-design-taste

leonxlnx/taste-skill

Semantic Design System Skill for Google Stitch. Generates agent-friendly DESIGN.md files that enforce premium, anti-generic UI standards — strict typography, calibrated color, asymmetric layouts, perpetual micro-motion, and hardware-accelerated performance.

265.6k

figma

heygen-com/hyperframes

Import Figma content into a HyperFrames composition — rendered assets, brand tokens, components, storyboard sections → reconstructed motion (frames read as states, not slides) (REST/CLI), connector-assisted motion when available, and shaders from a connector or native export. Use when the user pastes a figma.com link or asks to bring a Figma design, frame, logo, brand, or animation into a video/composition.

101.8k

image

coreyhaines31/marketingskills

When the user wants to create, generate, edit, or optimize images for marketing — blog heroes, social graphics, product mockups, profile banners, listing visuals, or brand assets. Also use when the user mentions 'AI image generation,' 'generate an image,' 'create a graphic,' 'product mockup,' 'hero image,' 'social media graphic,' 'banner image,' 'cover photo,' 'profile banner,' 'listing screenshot,' 'Flux,' 'Flux Kontext,' 'Midjourney,' 'DALL-E,' 'GPT Image,' 'ChatGPT Images,' 'Ideogram,' 'Gemini image,' 'Nano Banana,' 'Recraft,' 'Stable Diffusion,' 'Canva,' 'Figma,' 'image optimization,' 'compress images,' 'WebP,' or 'OG image.' Use this for general-purpose marketing image creation and optimization. For paid ad image creative and platform-specific ad specs, see ad-creative. For video production, see video.

62.9k

← All Design Systems skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY