---
name: 'ESM Module Validation'
description: 'Enforces strict ESM compliance: .js extensions, no CommonJS patterns, package.json type module, and compiled output verification'
on:
  pull_request:
    paths:
      - 'src/**/*.ts'
      - 'tests/**/*.ts'
      - 'package.json'
      - 'tsconfig.json'
  push:
    branches:
      - main
  workflow_dispatch:
permissions:
  issues: read
  pull-requests: read
safe-outputs:
  create-issue:
    title-prefix: '[esm-validation]'
  add-comment:
tools:
  bash: true
  github:
    toolsets: [issues, pull_requests]
network:
  allowed:
    - defaults
    - node
---

# ESM Module Validation

You enforce strict ESM (ECMAScript Module) compliance across the entire codebase. This project is pure ESM — no CommonJS is allowed.

## Context

The mcp-adr-analysis-server is a **pure ESM project**:

- `package.json` declares `"type": "module"`
- TypeScript targets ES2022 with ESNext module resolution
- All relative imports must use `.js` extensions (even for `.ts` source files)
- No `require()`, `module.exports`, or `__dirname` allowed
- Directory resolution uses `import.meta.url` via the `getCurrentDirCompat()` helper from `src/utils/directory-compat.ts`

## Validation Steps

### Step 1: Verify package.json ESM Configuration

```bash
if ! grep -q '"type": "module"' package.json; then
  echo "package.json must have type: module"
  exit 1
fi
echo "package.json correctly configured for ESM"
```

### Step 2: Validate Import Extensions

Check that all relative imports in `src/` use `.js` extensions:

```bash
# Check for imports without .js extension in src/
missing_ext=$(find src -name "*.ts" -type f -exec grep -H "from ['\"]\..*[^.js]['\"]" {} \; | grep -v ".json" || true)

if [ ! -z "$missing_ext" ]; then
  echo "Found imports without .js extension:"
  echo "$missing_ext"
  echo ""
  echo "All relative imports must use .js extension for ESM compatibility"
  exit 1
fi

echo "All imports use correct .js extensions"
```

### Step 3: Check for CommonJS Patterns

Scan the source code for forbidden CommonJS patterns:

```bash
# Check for require() calls (excluding comments and test mocks)
if git grep -n "require(" -- "src/**/*.ts" | grep -v "// " | grep -v "/\*" | grep -v "mock" | grep -v "jest" | head -n 10; then
  echo "Found require() calls in source code"
  echo "Use ES6 import statements instead"
  exit 1
fi

# Check for module.exports
if git grep -n "module\.exports" -- "src/**/*.ts" | grep -v "// " | grep -v "/\*" | head -n 10; then
  echo "Found module.exports in source code"
  echo "Use ES6 export statements instead"
  exit 1
fi

# Check for __dirname usage (should use getCurrentDirCompat)
if git grep -n "__dirname" -- "src/**/*.ts" | grep -v "// " | grep -v "/\*" | grep -v "getCurrentDirCompat" | head -n 10; then
  echo "Found __dirname usage in source code"
  echo "Use import.meta.url or getCurrentDirCompat() instead"
  exit 1
fi

echo "No CommonJS patterns found"
```

### Step 4: Verify ESM Output

Build the project and verify the compiled output can be imported as ESM:

```bash
node -e "
  import { promises as fs } from 'fs';
  import path from 'path';

  const distFiles = await fs.readdir('./dist/src');
  console.log('Compiled files:', distFiles);

  // Try importing main entry point
  try {
    await import('./dist/src/index.js');
    console.log('Main entry point imports successfully');
  } catch (e) {
    console.error('Failed to import main entry point:', e.message);
    process.exit(1);
  }
"
```

### Step 5: Test ESM Module Loading

Run the utility tests to verify runtime ESM behavior:

```bash
npm test -- --testPathPattern="utils" --maxWorkers=2
```

### Step 6: Validate import.meta.url Helper

Verify the `getCurrentDirCompat()` helper works correctly:

```bash
node -e "
  import { getCurrentDirCompat } from './dist/src/utils/directory-compat.js';
  import path from 'path';

  const dir = getCurrentDirCompat(import.meta.url);

  if (!dir || !path.isAbsolute(dir)) {
    console.error('getCurrentDirCompat() returned invalid path:', dir);
    process.exit(1);
  }

  console.log('import.meta.url helper working correctly');
  console.log('Current directory:', dir);
"
```

## On Failure

If any step fails:

1. **For PR events**: Add a comment to the PR with:
   - Which ESM rule was violated
   - The specific file(s) and line(s) with violations
   - The correct ESM pattern to use instead
   - Example: "Change `import { foo } from './bar'` to `import { foo } from './bar.js'`"

2. **For push/dispatch events**: Create an issue:

**Title**: `[esm-validation] {violation type} in {file count} file(s)`

**Body**:

````markdown
## ESM Module Validation Failure

### Violations Found

| Type                  | Count   | Files   |
| --------------------- | ------- | ------- |
| Missing .js extension | {count} | {files} |
| require() usage       | {count} | {files} |
| module.exports usage  | {count} | {files} |
| \_\_dirname usage     | {count} | {files} |

### Detailed Findings

{list each violation with file path, line number, and the offending code}

### How to Fix

**Missing .js extension**:

```typescript
// Wrong
import { foo } from './bar';
// Correct
import { foo } from './bar.js';
```
````

**require() → import**:

```typescript
// Wrong
const fs = require('fs');
// Correct
import fs from 'fs';
```

**\_\_dirname → import.meta.url**:

```typescript
// Wrong
const dir = __dirname;
// Correct
import { getCurrentDirCompat } from './utils/directory-compat.js';
const dir = getCurrentDirCompat(import.meta.url);
```

---

_Generated by ESM Module Validation agentic workflow_

```

## On Success

- **For PR events**: Add a comment confirming ESM compliance: all imports use `.js` extensions, no CommonJS patterns detected
- **For push/dispatch events**: No issue needed

---
*Replaces: .github/agents/esm-module-validation.yml*
```
