# ES-Guard

[![codecov](https://codecov.io/gh/mkayander/es-guard/branch/main/graph/badge.svg)](https://codecov.io/gh/mkayander/es-guard)

A powerful TypeScript-based tool that ensures your JavaScript code is compatible with target environments using ESLint.

## Features

- 🔍 **ES Version Validation**: Verify your JavaScript code compatibility with specific ES versions (ES2015, ES2016, ES2017, and beyond)
- 🌐 **Browser Support Verification**: Validate browser compatibility using eslint-plugin-compat
- 🎯 **Smart Browser Detection**: Automatically determine browser targets from ES version (optional)
- 📁 **Comprehensive Directory Scanning**: Effortlessly scan directories for JavaScript files
- 🚀 **GitHub Actions Integration**: Seamlessly integrate with GitHub Actions workflows
- 📦 **Flexible Installation**: Install globally or use as a project dependency

## Installation

### Global Installation

```bash
npm install -g es-guard
```

### Local Installation

```bash
npm install --save-dev es-guard
```

### From Source

```bash
git clone https://github.com/mkayander/es-guard.git
cd es-guard
npm install
npm run build
```

## Usage

### Command Line Interface

```bash
# Basic usage with auto-detected browsers
es-guard

# Validate specific directory
es-guard build

# Specify target ES version (year format)
es-guard -t 2020 build

# Specify target ES version (numeric format)
es-guard -t 11 build

# Use latest ES version
es-guard -t latest build

# Specify custom browser targets
es-guard --browsers "> 0.5%, last 2 versions, Firefox ESR, not dead" dist


# Display help information
es-guard --help

# Show version information
es-guard --version
```

### Programmatic Usage

ES-Guard provides a comprehensive programmatic API for integration into your build tools, CI/CD pipelines, or custom scripts.

#### Basic Usage

```typescript
import { checkCompatibility } from "es-guard";

// Basic compatibility check
const result = await checkCompatibility({
  dir: "dist",
  target: "2020",
});

console.log(`Found ${result.errors.length} errors and ${result.warnings.length} warnings`);
```

#### Auto-detection

```typescript
import { checkCompatibility, detectProjectConfig } from "es-guard";

// Auto-detect project configuration
const config = detectProjectConfig(process.cwd());

if (config.target && config.outputDir) {
  const result = await checkCompatibility({
    dir: config.outputDir,
    target: config.target,
    browsers: config.browserslist?.join(", "),
  });
}
```

#### CI/CD Pipeline Usage

```typescript
import { checkCompatibility, detectProjectConfig } from "es-guard";

// Running in CI where working directory might be different
const projectRoot = process.env.PROJECT_ROOT || process.cwd();
const buildDir = process.env.BUILD_DIR || "dist";

// Detect configuration from project root
const config = detectProjectConfig(projectRoot);

if (config.target) {
  const scanDir = config.outputDir || buildDir;
  const fullScanPath = path.isAbsolute(scanDir) ? scanDir : path.join(projectRoot, scanDir);

  const result = await checkCompatibility({
    dir: fullScanPath,
    target: config.target,
    browsers: config.browserslist?.join(", "),
  });

  // In CI, exit with error code if issues found
  if (result.errors.length > 0) {
    process.exit(1);
  }
}
```

#### Advanced Usage

```typescript
import { checkCompatibility, getBrowserTargetsFromString, setVerboseMode, validateConfig } from "es-guard";

// Validate configuration
validateConfig({
  dir: "dist",
  target: "2020",
});

// Get browser targets for specific ES version
const browsers = getBrowserTargetsFromString("2015");

// Enable verbose mode for detailed output
setVerboseMode(true);

// Run compatibility check
const result = await checkCompatibility({
  dir: "dist",
  target: "2020",
  browsers: "> 1%, last 2 versions, not dead, ie 11",
});

// Process results
result.errors.forEach((violation) => {
  console.log(`Error in ${violation.file}:`);
  violation.messages.forEach((message) => {
    console.log(`  Line ${message.line}: ${message.message}`);
  });
});
```

#### TypeScript Support

ES-Guard includes full TypeScript support with proper type definitions:

```typescript
import type { CompatibilityResult, Config, Violation } from "es-guard";

const config: Config = {
  dir: "dist",
  target: "2020",
  browsers: "> 1%, last 2 versions, not dead",
};

const result: CompatibilityResult = await checkCompatibility(config);
```

See the [examples](./examples/) directory for more comprehensive usage examples.

### GitHub Actions Integration

```yaml
name: Validate Compatibility
on: [push, pull_request]

jobs:
  compatibility:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: "18"
      - run: npm install
      - run: npm run build
      - run: npx es-guard -t 2015 dist
```

## Configuration

### Parameters

| Parameter  | Description                                | Default                   | Required |
| ---------- | ------------------------------------------ | ------------------------- | -------- |
| `path`     | Directory to scan for JavaScript files     | `dist`                    | No       |
| `target`   | Target ES version                          | `2015`                    | Yes      |
| `browsers` | Browser targets for compatibility checking | Auto-detected from target | No       |

### CLI Options

| Option                     | Description       | Default       |
| -------------------------- | ----------------- | ------------- |
| `-t, --target <version>`   | Target ES version | Auto-detected |
| `-b, --browsers <targets>` | Browser targets   | Auto-detected |

| `-v, --verbose` | Enable verbose output | `false` |
| `--skip` | Continue on compatibility errors | `false` |
| `--no-compat` | Disable compat/compat rule - only report syntax errors | `false` |

### ES Target Versions

The `target` parameter supports multiple formats:

- **Year format**: `2015`, `2016`, `2017`, etc.
- **Numeric format**: `6` (ES2015), `7` (ES2016), `11` (ES2020), etc.
- **Latest**: `latest` for the most recent ES version

### Browser Targets

The `browsers` parameter follows the Browserslist format. When not specified, browsers are automatically determined based on the ES target:

- **ES2015/ES6**: `> 1%, last 2 versions, not dead, ie 11`
- **ES2016-2017/ES7-8**: `> 1%, last 2 versions, not dead, not ie 11`
- **ES2018-2019/ES9-10**: `> 1%, last 2 versions, not dead, not ie 11, not op_mini all`
- **ES2020+/ES11+**: `> 1%, last 2 versions, not dead, not ie 11, not op_mini all, not android < 67`

Custom browser target examples:

- `> 1%, last 2 versions, not dead, ie 11` - Modern browsers with IE11 support
- `> 0.5%, last 2 versions, Firefox ESR, not dead` - Broader browser support
- `defaults` - Default Browserslist targets
- `last 1 version` - Latest version of each browser

## CI/CD Integration

### Test Output in CI

When running tests in CI environments, you may see error messages like:

```
Error: Directory "/path/to/nonexistent-directory" does not exist
Error: Invalid ES target: "invalid"
Warning: Could not lint directory
Error [BrowserslistError]: Unknown browser query
```

**These are NOT test failures!** These are expected outputs from tests that are designed to test error handling scenarios. The tests are actually passing successfully.

### CI Configuration

To properly handle test output in CI, configure your CI system to:

1. **Only fail on test exit codes** (not on stderr output)
2. **Use the JUnit reporter** for structured test results
3. **Check the JUnit XML file** for actual test failures

### Example CI Commands

```bash
# Run tests with JUnit output (recommended for CI)
pnpm run test:junit

# Run tests with coverage and JUnit output
pnpm run test:junit:coverage

# Check test results
cat test-report.junit.xml
```

### GitHub Actions Example

```yaml
- name: Run Tests
  run: pnpm run test:junit:coverage

- name: Upload Test Results
  uses: actions/upload-artifact@v3
  with:
    name: test-results
    path: test-report.junit.xml
    retention-days: 30
```

## Development

### Setup

```bash
npm install
```

### Build

```bash
npm run build
```

### Development Mode

```bash
npm run dev
```

### Testing

```bash
# Run all tests
pnpm run test

# Run tests in watch mode
pnpm run test:dev

# Run a specific test file
pnpm run build && pnpm run test:only src/path/to/test.ts

# Run a specific test by name
pnpm run build && pnpm run test:only -t "test name"

# Run tests without building first (requires previous build)
pnpm run test:only -t "test name"
```

### Linting

```bash
npm run lint
```

## Publishing to NPM

1. Update the version in `package.json`
2. Update the repository URL in `package.json`
3. Build the project: `npm run build`
4. Publish: `npm publish`

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Contributing

1. Fork the repository
2. Create a feature branch
3. Implement your changes
4. Add tests where applicable
5. Run the linter: `npm run lint`
6. Submit a pull request

## Support

- 🐛 [Issues](https://github.com/mkayander/es-guard/issues)
- 💬 [Discussions](https://github.com/mkayander/es-guard/discussions)
