swc_feature_flags 0.5.0

Feature flag transformation for SWC - build-time marking and runtime dead code elimination
Documentation
# SWC Feature Flags

A two-phase feature flag system for SWC that provides build-time marking and runtime dead code elimination.

## Overview

This library enables powerful feature flag management with aggressive dead code elimination. It works in two phases:

1. **Build-time (SWC Plugin)**: Marks feature flag usage locations by replacing flag identifiers with `__SWC_FLAGS__` markers
2. **Runtime (Standalone Crate)**: Substitutes flag values and eliminates dead code branches

## Features

- **Multiple usage patterns**: Direct destructuring, indirect destructuring, and property access
-**Customizable function names**: Not hardcoded to specific function names
-**Scope-safe**: Uses SWC's `Id` system to handle variable shadowing correctly
-**Dead code elimination**: Removes unreachable code branches
-**Statistics tracking**: Reports bytes removed and branches eliminated
-**Minifier-safe markers**: Uses `__SWC_FLAGS__` pattern that minifiers preserve

## Architecture

### Phase 1: Build-Time Transformation

The build-time plugin (`@swc/plugin-feature-flags`) performs these transformations:

**Input:**
```javascript
import { useExperimentalFlags } from '@their/library';

function App() {
  const { featureA, featureB } = useExperimentalFlags();

  if (featureA) {
    console.log('Feature A enabled');
  }

  return featureB ? 'Beta' : 'Stable';
}
```

**Output:**
```javascript
function App() {
  if (__SWC_FLAGS__.featureA) {
    console.log('Feature A enabled');
  }

  return __SWC_FLAGS__.featureB ? 'Beta' : 'Stable';
}
```

The plugin:
1. Tracks imports from configured libraries
2. Detects destructuring from configured flag functions
3. Replaces flag identifiers with `__SWC_FLAGS__.flagName` markers
4. Removes import statements and hook calls

### Supported Usage Patterns

The build-time plugin supports multiple ways of accessing feature flags:

#### Pattern 1: Direct Destructuring
```javascript
import { useExperimentalFlags } from '@their/library';

const { featureA, featureB } = useExperimentalFlags();
if (featureA) {
  // Transformed to: if (__SWC_FLAGS__.featureA)
}
```

#### Pattern 2: Indirect Destructuring
```javascript
import { useExperimentalFlags } from '@their/library';

const flags = useExperimentalFlags();
const { featureA } = flags;
if (featureA) {
  // Transformed to: if (__SWC_FLAGS__.featureA)
}
```

#### Pattern 3: Property Access
```javascript
import { useExperimentalFlags } from '@their/library';

const flags = useExperimentalFlags();
if (flags.featureA) {
  // Transformed to: if (__SWC_FLAGS__.featureA)
}
```

All three patterns are transformed identically and can be mixed in the same file.

### Phase 2: Runtime Transformation

The runtime transformer substitutes flag values and eliminates dead code:

**Input (from Phase 1):**
```javascript
function App() {
  if (__SWC_FLAGS__.featureA) {
    console.log('Feature A enabled');
  }

  return __SWC_FLAGS__.featureB ? 'Beta' : 'Stable';
}
```

**Runtime Config:**
```json
{
  "featureA": true,
  "featureB": false
}
```

**Output:**
```javascript
function App() {
  console.log('Feature A enabled');

  return 'Stable';
}
```

## Installation

### Rust API

Add to your `Cargo.toml`:

```toml
[dependencies]
swc_feature_flags = "0.1"
```

### SWC Plugin (WASM)

```bash
npm install @swc/plugin-feature-flags
```

## Usage

### Rust API

```rust
use swc_feature_flags::{build_time_pass, runtime_pass, BuildTimeConfig, RuntimeConfig, LibraryConfig};
use std::collections::HashMap;
use swc_ecma_transforms_base::resolver;
use swc_common::Mark;

// Build-time configuration
let mut libraries = HashMap::new();
libraries.insert(
    "@their/library".to_string(),
    LibraryConfig {
        functions: vec!["useExperimentalFlags".to_string()],
    },
);

let build_config = BuildTimeConfig {
    libraries,
    marker_object: "__SWC_FLAGS__".to_string(),
};

// Apply resolver first (required for scope safety)
let unresolved_mark = Mark::new();
let top_level_mark = Mark::new();
program = program.apply(resolver(unresolved_mark, top_level_mark, false));

// Apply build-time pass
program = program.apply(build_time_pass(build_config));

// Runtime configuration
let mut flag_values = HashMap::new();
flag_values.insert("featureA".to_string(), true);
flag_values.insert("featureB".to_string(), false);

let runtime_config = RuntimeConfig {
    flag_values,
    remove_markers: true,
    collect_stats: true,
    marker_object: "__SWC_FLAGS__".to_string(),
};

// Apply runtime pass
program = program.apply(runtime_pass(runtime_config));
```

### SWC Plugin (.swcrc)

```json
{
  "jsc": {
    "experimental": {
      "plugins": [
        ["@swc/plugin-feature-flags", {
          "libraries": {
            "@their/library": {
              "functions": ["useExperimentalFlags", "getExperimentalFlags"]
            },
            "@another/flags": {
              "functions": ["useFeatures"]
            }
          },
          "markerObject": "__SWC_FLAGS__"
        }]
      ]
    }
  }
}
```

## Configuration

### Build-Time Config

```typescript
interface BuildTimeConfig {
  /** Library configurations: library name -> config */
  libraries: Record<string, LibraryConfig>;

  /** Global object name for markers (default: "__SWC_FLAGS__") */
  markerObject?: string;
}

interface LibraryConfig {
  /** Function names to detect (e.g., ["useExperimentalFlags"]) */
  functions: string[];
}
```

### Runtime Config

```rust
pub struct RuntimeConfig {
    /// Flag values to apply (flag_name -> boolean)
    pub flag_values: HashMap<String, bool>,

    /// Whether to remove markers after processing
    pub remove_markers: bool, // default: true

    /// Whether to collect statistics
    pub collect_stats: bool, // default: true

    /// Marker object name (must match build-time)
    pub marker_object: String, // default: "__SWC_FLAGS__"
}
```

## Dead Code Elimination

The runtime transformer eliminates:

### If Statements
```javascript
// Input
if (__SWC_FLAGS__.featureA) {  // true
  console.log('A');
} else {
  console.log('B');
}

// Output
console.log('A');
```

### Ternary Expressions
```javascript
// Input
const result = __SWC_FLAGS__.featureB ? 'On' : 'Off';  // false

// Output
const result = 'Off';
```

### Logical Operators
```javascript
// Input
const a = __SWC_FLAGS__.featureA && expensive();  // true
const b = __SWC_FLAGS__.featureB && shouldNotRun();  // false
const c = __SWC_FLAGS__.featureA || fallback();  // true

// Output
const a = expensive();
const b = false;
const c = true;
```

### Negation
```javascript
// Input
const notA = !__SWC_FLAGS__.featureA;  // true

// Output
const notA = false;
```

## Scope Safety

The library uses SWC's `Id` system (symbol + syntax context) to handle variable shadowing correctly:

```javascript
import { useExperimentalFlags } from '@their/library';

function App() {
  const { featureA } = useExperimentalFlags();

  if (featureA) {  // Replaced with __SWC_FLAGS__.featureA
    console.log('Outer');

    const featureA = false;  // Shadowed variable
    if (featureA) {  // NOT replaced - uses local variable
      console.log('Inner');
    }
  }
}
```

**Output:**
```javascript
function App() {
  if (__SWC_FLAGS__.featureA) {
    console.log('Outer');

    const featureA = false;
    if (featureA) {
      console.log('Inner');
    }
  }
}
```

## Statistics

When `collect_stats` is enabled, the runtime transformer tracks:

```rust
pub struct TransformStats {
    pub original_bytes: usize,      // Approximate original size
    pub removed_bytes: usize,        // Bytes removed by DCE
    pub branches_eliminated: usize,  // Number of branches eliminated
    pub flags_processed: HashSet<String>, // Flags that were processed
}
```

## Testing

```bash
# Run all tests
cargo test -p swc_feature_flags

# Run fixture tests only
cargo test -p swc_feature_flags --test fixture
```

## License

Apache-2.0

## Contributing

Contributions are welcome! Please ensure:
- All tests pass
- Code follows Rust formatting guidelines
- New features include tests