# Shop Minis ESLint Plugin

Custom ESLint rules for Shop Minis apps. ESLint is included with the SDK.

## Quick Setup

Create **`eslint.config.js`** (NOT `.eslintrc.js`):

```javascript
const shopMinisConfig = require('@shopify/shop-minis-react/eslint/config')

module.exports = [shopMinisConfig]
```

**That's it!** TypeScript and JSX are supported out of the box.

**Important:**
- File must be named `eslint.config.js` (no dot, no "rc")
- This will lint all `.js`, `.jsx`, `.ts`, `.tsx` files in your project
- TypeScript and JSX parsing is configured automatically

## Usage

```bash
# Check for errors
npx eslint .

# Auto-fix (converts <img> to <Image> AND adds import!)
npx eslint . --fix
```

## What You Get

- ✅ No internal imports allowed
- ✅ Warnings for `<img>`, `<button>`, `<label>` tags (auto-fixes with imports)
- ✅ Manifest scope validation - ensures manifest.json has scopes for hooks you use (auto-fixes manifest)

## Rules

### `no-internal-imports`

Prevents importing from internal SDK directories.

```tsx
// ❌ Error
import {something} from '@shopify/shop-minis-react/internal'

// ✅ Correct
import {Component} from '@shopify/shop-minis-react'
```

### `prefer-sdk-components`

Suggests using SDK components instead of native HTML elements. **Fully auto-fixable** - fixes both tags and imports!

**Before:**
```tsx
const MyComponent = () => (
  <img src="product.jpg" alt="Product" />
)
```

**After running `npx eslint . --fix`:**
```tsx
import {Image} from '@shopify/shop-minis-react'

const MyComponent = () => (
  <Image src="product.jpg" alt="Product" />
)
```

**Supported Components:**
- `<img>` → `<Image>`
- `<button>` → `<Button>`
- `<label>` → `<Label>`

**Auto-fix does TWO things:**
1. ✅ Replaces native element with SDK component
2. ✅ Adds import statement automatically (or adds to existing import)

### `validate-manifest`

Validates `src/manifest.json` configuration for scopes and permissions. **Auto-fixable** - adds missing values to manifest!

#### Scopes

Checks that hooks have required scopes:

```tsx
// If you use this hook:
import {useCurrentUser} from '@shopify/shop-minis-react'

// Manifest must include:
{
  "scopes": ["USER_SETTINGS_READ"]
}
```

**Scope Requirements:**
- `useCurrentUser` → `USER_SETTINGS_READ`
- `useSavedProducts` → `FAVORITES`
- `useOrders` → `ORDERS`

#### Permissions

Checks for native permission usage:

```tsx
// If you use this hook:
import {useImagePicker} from '@shopify/shop-minis-react'

// Or browser APIs:
navigator.mediaDevices.getUserMedia({video: true})

// Manifest must include:
{
  "permissions": ["CAMERA"]
}
```

**Supported Permissions:**
- `CAMERA` - Required for `useImagePicker` hook or getUserMedia video
- `MICROPHONE` - Required for getUserMedia audio
- `MOTION` - Required for DeviceOrientation/DeviceMotion events

#### Trusted Domains

Checks that external URLs are in trusted_domains. Detects:

**Network Requests:**
- `fetch('https://api.example.com/data')`
- `new XMLHttpRequest().open('GET', 'https://...')`
- `new WebSocket('wss://api.example.com')`
- `new EventSource('https://api.example.com/events')`
- `navigator.sendBeacon('https://analytics.example.com')`
- `window.open('https://external.com')`

**Media & Resources:**
- `<img src="https://cdn.shopify.com/image.jpg" />`
- `<video src="https://videos.example.com/video.mp4" />`
- `<video poster="https://cdn.example.com/poster.jpg" />`
- `<audio src="https://audio.example.com/sound.mp3" />`
- `<source src="https://media.example.com/video.mp4" />`
- `<track src="https://cdn.example.com/captions.vtt" />`
- `<object data="https://cdn.example.com/file.pdf" />`
- `<embed src="https://cdn.example.com/file.swf" />`
- `<form action="https://api.example.com/submit" />`

**Note:** External scripts (`<script>`), stylesheets (`<link>`), and iframes are not supported and excluded from validation.

**Example manifest:**
```json
{
  "trusted_domains": [
    "api.example.com",
    "cdn.shopify.com",
    "videos.example.com"
  ]
}
```

**Wildcard support:**
```json
{
  "trusted_domains": [
    "*.shopify.com",     // Matches any Shopify subdomain
    "api.example.com",   // Exact domain
    "cdn.example.com/assets"  // Specific path
  ]
}
```

**Auto-fix:**
```bash
npx eslint . --fix
# Automatically updates src/manifest.json
```

**Example errors:**
```
Hook "useCurrentUser" requires scope "USER_SETTINGS_READ" in src/manifest.json.
Hook "useImagePicker" requires permission "CAMERA" in src/manifest.json.
fetch() call loads from "api.example.com" which is not in trusted_domains.
<img> src attribute loads from "cdn.shopify.com" which is not in trusted_domains.
```

## Extending Rules

To add more component mappings to `prefer-sdk-components`, edit `eslint/rules/prefer-sdk-components.cjs`:

```javascript
const defaultComponents = {
  img: 'Image',
  button: 'Button',
  label: 'Label',
  input: 'Input', // Add this
  a: 'TransitionLink', // Add this
}
```

All consumers automatically get new rules - no config changes needed!
