npm package: @latte-macchiat-io/latte-eslint-rules
Description: Brewing better code, one rule at a time.
Author: Latte Macchiat.io
Latest version: 0.0.10

# @latte-macchiat-io/latte-eslint-rules

A collection of categorized ESLint rules for typography, accessibility, performance, and security.

Intended for internal use by the 👩‍💻🧑‍💻 [latte-macchiat.io](https://latte-macchiat.io) team, we can't offer official support, but feel free to reach out with questions, feedback or project ideas!

## Installation

```bash
pnpm add -D @latte-macchiat-io/latte-eslint-rules
```

## Usage

### ESLint Flat Config (recommended)

```js
// Option 1: Default import (most common)
import latteRules from "@latte-macchiat-io/latte-eslint-rules";

export default [
  {
    plugins: {
      "@latte-macchiat-io/latte-eslint-rules": latteRules,
    },
    rules: {
      "@latte-macchiat-io/latte-eslint-rules/require-french-non-breakable-spacing": "warn",
    },
  },
  // Or use pre-configured rule sets
  latteRules.configs.recommended,
];
```

```js
// Option 2: Named imports (alternative)
import { configs } from "@latte-macchiat-io/latte-eslint-rules";

export default [
  configs.recommended,
  // ... other configs
];
```

```js
// Option 3: CommonJS style (if using require)
const latteRules = require("@latte-macchiat-io/latte-eslint-rules");

module.exports = [
  latteRules.configs.recommended,
  // ... other configs
];
```

### Legacy ESLint Config

```js
module.exports = {
  plugins: ["@latte-macchiat-io/latte-eslint-rules"],
  extends: ["@latte-macchiat-io/latte-eslint-rules/recommended"],
};
```

### Category-Specific Configs

Use only specific categories of rules:

```js
// Typography rules only
extends: ['@latte-macchiat-io/latte-eslint-rules/typography']

// Multiple categories
extends: [
  '@latte-macchiat-io/latte-eslint-rules/typography',
  '@latte-macchiat-io/latte-eslint-rules/accessibility'
]

// All available configs
extends: [
  '@latte-macchiat-io/latte-eslint-rules/typography',    // Typography rules
  '@latte-macchiat-io/latte-eslint-rules/accessibility', // A11y rules (coming soon)
  '@latte-macchiat-io/latte-eslint-rules/performance',   // Performance rules (coming soon)
  '@latte-macchiat-io/latte-eslint-rules/security',      // Security rules (coming soon)
  '@latte-macchiat-io/latte-eslint-rules/recommended',   // Curated selection
]
```

### Custom Configuration

#### Using Pre-built Configs (Recommended)

```js
// Use the pre-built config that targets translation JSON files
import latteRules from "@latte-macchiat-io/latte-eslint-rules";

export default [
  // This targets only JSON files in translation directories
  latteRules.configs.typography,

  // Other configs for your project
  {
    files: ["**/*.ts", "**/*.js"],
    // ... other rules
  }
];
```

#### Custom File Targeting

```js
// Target different file patterns for different rules
export default [
  // French typography rule for translation files only
  {
    files: ["**/translations/**/*.json", "**/locales/**/*.json"],
    plugins: {
      "@latte-macchiat-io/latte-eslint-rules": latteRules
    },
    rules: {
      "@latte-macchiat-io/latte-eslint-rules/require-french-non-breakable-spacing": "warn"
    }
  },

  // Hypothetical future rule for all TypeScript files
  {
    files: ["**/*.ts", "**/*.tsx"],
    plugins: {
      "@latte-macchiat-io/latte-eslint-rules": latteRules
    },
    rules: {
      "@latte-macchiat-io/latte-eslint-rules/some-typescript-rule": "error"
    }
  },

  // Apply multiple rules with different targeting
  {
    files: ["**/*.{js,ts,json}"],
    plugins: {
      "@latte-macchiat-io/latte-eslint-rules": latteRules
    },
    rules: {
      "@latte-macchiat-io/latte-eslint-rules/require-french-non-breakable-spacing": ["warn", {
        punctuation: ['?', '!', ':', ';'] // Custom punctuation
      }]
    }
  }
];
```

#### Fine-grained Rule Options

```js
rules: {
  '@latte-macchiat-io/latte-eslint-rules/require-french-non-breakable-spacing': ['warn', {
    punctuation: ['?', '!', ':', ';'], // Which punctuation to check
  }],
}
```

## How File Targeting Works

### Pre-built Configs vs Custom Targeting

**Pre-built configs** (like `latteRules.configs.typography`) come with predefined `files` patterns that target specific file types. This is convenient but means all rules in that config share the same file targeting.

**Custom targeting** allows you to create multiple configuration objects with different `files` patterns, giving you granular control over which rules apply to which files.

### Adding Rules with Different File Targets

When you add new rules that should target different files, you have two options:

1. **Separate config objects** (Recommended):
```js
export default [
  // Typography rules for JSON translation files
  {
    files: ["**/translations/**/*.json"],
    plugins: { latte: latteRules },
    rules: { "latte/require-french-non-breakable-spacing": "warn" }
  },

  // Future TypeScript-specific rules
  {
    files: ["**/*.ts", "**/*.tsx"],
    plugins: { latte: latteRules },
    rules: { "latte/typescript-specific-rule": "error" }
  }
];
```

2. **Override the pre-built config**:
```js
export default [
  // Use pre-built config as base
  latteRules.configs.typography,

  // Override for different file targeting
  {
    files: ["**/*.ts", "**/*.js"],  // Different files
    plugins: {
      "@latte-macchiat-io/latte-eslint-rules": latteRules
    },
    rules: {
      "@latte-macchiat-io/latte-eslint-rules/require-french-non-breakable-spacing": "off", // Turn off for code files
      "@latte-macchiat-io/latte-eslint-rules/new-code-rule": "error" // New rule for code files
    }
  }
];
```

## Rules

### Typography Rules

#### `require-french-non-breakable-spacing`

Enforces proper French typography rules for punctuation spacing in translation JSON files

**File Targeting:** By default, this rule only applies to JSON files in common translation directories:
- `**/translations/**/*.json`
- `**/translation/**/*.json`
- `**/locales/**/*.json`
- `**/locale/**/*.json`
- `**/i18n/**/*.json`
- `**/lang/**/*.json`
- `**/languages/**/*.json`

**Options:**

- `punctuation`: Array of punctuation marks to check (default: `['?', '!', ':', ';']`)

##### ✅ Correct

```json
{
  "welcome": "Bonjour\u00A0!",
  "question": "Comment allez-vous\u00A0?",
  "result": "Résultat\u00A0: {{value}}",
  "exclamation": "Parfait\u00A0!"
}
```

##### ❌ Incorrect

```json
{
  "welcome": "Bonjour !",
  "question": "Comment allez-vous?",
  "result": "Résultat: {{value}}",
  "missing": "Attention;"
}
```

**Auto-fixable:** ✅ This rule automatically fixes spacing and quotation issues with `--fix`.

## Troubleshooting

### TypeScript/ESLint Errors on JSON Files

If you're getting TypeScript ESLint errors like `@typescript-eslint/no-unused-expressions` on JSON files, this is because ESLint is trying to parse JSON as JavaScript. The pre-built configs automatically disable conflicting TypeScript rules for JSON files.

**Solution 1: Use pre-built configs (Recommended)**
```js
import { configs } from '@latte-macchiat-io/latte-eslint-rules';

export default [
  configs.recommended,  // Automatically disables TypeScript rules for JSON
  // ... your other configs
];
```

**Solution 2: Manual configuration**
If you're using custom configuration, make sure to disable TypeScript rules for JSON files:

```js
export default [
  {
    files: ["**/*.json"],
    rules: {
      "@typescript-eslint/no-unused-expressions": "off",
      // ... disable other TypeScript rules as needed
    }
  },
  {
    files: ["**/translations/**/*.json"],
    plugins: {
      "@latte-macchiat-io/latte-eslint-rules": latteRules
    },
    rules: {
      "@latte-macchiat-io/latte-eslint-rules/require-french-non-breakable-spacing": "warn"
    }
  }
];
```

**Solution 3: Use a JSON parser**
For more robust JSON handling, consider using `jsonc-eslint-parser`:

```bash
npm install --save-dev jsonc-eslint-parser
```

```js
export default [
  {
    files: ["**/*.json"],
    languageOptions: {
      parser: require("jsonc-eslint-parser")
    }
  }
];
```

To add new rules:

1. Create a new file in the appropriate category folder (`src/rules/{category}/`)
2. Export the rule following ESLint's rule format
3. Add the rule to the category's `index.js`
4. Update the main TypeScript definitions in `src/index.d.ts`
5. Add the rule to relevant config files in `src/configs/`
6. Run `pnpm build` to compile

### Project Structure

```
@latte-macchiat-io/latte-eslint-rules/
├── src/
│   ├── rules/
│   │   ├── typography/
│   │   │   ├── require-french-non-breakable-spacing.js
│   │   │   └── index.js
│   │   ├── accessibility/    # Coming soon
│   │   ├── performance/      # Coming soon
│   │   ├── security/        # Coming soon
│   │   └── index.js
│   ├── configs/
│   │   ├── typography.js
│   │   ├── accessibility.js
│   │   ├── performance.js
│   │   ├── security.js
│   │   ├── recommended.js
│   │   └── index.js
│   └── index.js
```