npm package: @rainbowindex/oxide
Description: Rust-based scanner for Rainbow Index
Author: Rainbow Index Contributors
Latest version: 0.1.4
Deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.

# @rainbowindex/oxide

The file scanner for Rainbow Index. Extracts utility class candidates from source files.

This package is the extraction layer—it finds the classes you use so the compiler knows what CSS to generate. Most
projects interact with it indirectly through the [Vite plugin](./../vite), [CLI](./../cli), or
[PostCSS plugin](./../postcss). Direct use is helpful when building custom tooling or integrating with non-standard
build systems.

The scanner is written in Rust for speed, with a TypeScript fallback when native binaries aren't available.

## Installation

```bash
npm install @rainbowindex/oxide
```

## Usage

```typescript
import { Scanner } from "@rainbowindex/oxide";

const scanner = new Scanner({
    sources: [
        { base: "./src", pattern: "**/*.tsx" },
        { base: "./src", pattern: "**/*.jsx" },
    ],
});

// Scan for candidates
for (const candidate of scanner.scan()) {
    console.log(candidate);
}

// Get list of scanned files
console.log(scanner.files);
```

## API Reference

### `Scanner`

The main scanner class.

```typescript
interface ScannerOptions {
    sources: ScannerSource[];
}

interface ScannerSource {
    base: string; // Base directory
    pattern: string; // Glob pattern
    negated?: boolean; // Exclude pattern
}

class Scanner {
    constructor(options: ScannerOptions);

    // Scan and yield candidates
    *scan(): Generator<string>;

    // Get scanned file paths
    get files(): string[];

    // Get active source patterns
    get globs(): ScannerSource[];
}
```

### `createScanner(options)`

Factory function to create a Scanner instance.

```typescript
import { createScanner } from "@rainbowindex/oxide";

const scanner = createScanner({
    sources: [{ base: "./src", pattern: "**/*.tsx" }],
});
```

## Supported File Types

The scanner processes these file extensions:

- `.html`, `.htm`
- `.js`, `.ts`, `.jsx`, `.tsx`, `.mjs`, `.mts`
- `.vue`, `.svelte`, `.astro`
- `.md`, `.mdx`, `.markdown`
- `.json`, `.jsonc`
- `.php`, `.blade.php`, `.erb`, `.haml`, `.slim`, `.pug`, `.twig`, `.liquid`
- `.rb`, `.rs`
- `.clj`, `.cljs`, `.cljc`
- `.ex`, `.exs`, `.eex`, `.heex`, `.leex`
- `.razor`, `.cshtml`

## Detection Patterns

The scanner extracts class names from common patterns:

### Attribute Patterns

```html
<!-- class attribute -->
<div class="p-4 bg-blue-500"></div>

<!-- className attribute (JSX) -->
<div className="p-4 bg-blue-500"></div>
```

### Template Literals

```tsx
<div className={`p-4 ${condition ? "bg-blue-500" : "bg-red-500"}`} />
```

### Helper Functions

```tsx
// clsx, cn, classNames
clsx("p-4", "bg-blue-500");
cn("p-4", isActive && "bg-blue-500");
classNames("p-4", { "bg-blue-500": isActive });
```

### Utility Detection

The scanner uses heuristics to identify utility classes:

1. Starts with letter or number
2. Contains only `a-zA-Z0-9_:/-` characters
3. Matches common utility patterns: layout (`flex`, `grid`), spacing (`m-*`, `p-*`), sizing (`w-*`, `h-*`), colors
   (`bg-*`, `text-*`), variants (`hover:*`, `dark:*`, `64:*`)

## Directory Handling

The scanner automatically skips `node_modules` directories and hidden directories (starting with `.`), and handles
nested directory structures.

## Known Limitations

### TypeScript Fallback Glob Support

When the native module is unavailable, the TypeScript fallback glob matcher supports a minimal subset of patterns: exact
relative paths, `*`, `**`, `**/*`, `*.ext`, and `*.{ext1,ext2}`. Unsupported patterns are skipped with a one-time
warning.

### Parallel HTML Extraction Overlap

The native HTML fast path splits large inputs into parallel chunks with a 128-byte overlap window. Extremely long class
attribute values that cross a chunk boundary by more than this overlap can be missed in the parallel path.

## Performance

The scanner yields candidates as they're found (streaming), deduplicates candidates automatically, and caches the
scanned file list.

## Caching & Invalidation

The scanner keeps small in-memory caches to speed up repeated scans:

- `scan()` caches per-`Scanner` results and (by default) in a shared LRU keyed by resolved source roots + patterns
- `extractCandidates()` uses a hash-based cache key to avoid duplicating large content strings in memory
- Cache entries are invalidated when any tracked file `mtime` changes (default)
- `rescanFile()` and `scanWithFiles()` always invalidate the instance cache
- `reset()` clears the instance cache and forces a fresh scan

Environment flags that control cache behavior:

| Flag                     | Effect                                                     |
| ------------------------ | ---------------------------------------------------------- |
| `RI_SCAN_MTIME=0`        | Disable `mtime` validation for cached results              |
| `RI_SCAN_GLOBAL_CACHE=0` | Disable shared cache across scanner instances              |
| `RI_SCAN_VERIFY=1`       | Force cache validation even when global caching is enabled |
| `RI_SCAN_LOG_ERRORS=1`   | Log file system and UTF-8 decode errors                    |
| `RI_NATIVE_LOG=1`        | Log native module load failures                            |
| `RI_OXIDE_PATH=<path>`   | Override native module resolution                          |

## Example: Full Scan

```typescript
import { Scanner } from "@rainbowindex/oxide";

const scanner = new Scanner({
    sources: [
        { base: "./src", pattern: "**/*.{tsx,jsx,ts,js,html,vue,svelte}" },
        { base: "./components", pattern: "**/*.tsx" },
    ],
});

const candidates = [...scanner.scan()];

console.log(`Scanned ${scanner.files.length} files`);
console.log(`Found ${candidates.length} unique candidates`);
```

## Fuzzing (Rust Core)

The Rust core includes `cargo fuzz` targets under `rust-src/fuzz`:

```bash
cd packages/@rainbowindex/oxide/rust-src
cargo fuzz run css_parser
cargo fuzz run candidate_extractor
```

## Platform Support

The scanner includes native Rust binaries for maximum performance. When the native module is unavailable, a TypeScript
fallback is used automatically.

### Supported Platforms

| Platform | Architecture          | Package                                |
| -------- | --------------------- | -------------------------------------- |
| macOS    | ARM64 (Apple Silicon) | `@rainbowindex/oxide-darwin-arm64`     |
| macOS    | x64 (Intel)           | `@rainbowindex/oxide-darwin-x64`       |
| Linux    | ARM64 (glibc)         | `@rainbowindex/oxide-linux-arm64-gnu`  |
| Linux    | ARM64 (musl)          | `@rainbowindex/oxide-linux-arm64-musl` |
| Linux    | x64 (glibc)           | `@rainbowindex/oxide-linux-x64-gnu`    |
| Linux    | x64 (musl)            | `@rainbowindex/oxide-linux-x64-musl`   |
| FreeBSD  | x64                   | `@rainbowindex/oxide-freebsd-x64`      |
| Windows  | x64                   | `@rainbowindex/oxide-win32-x64-msvc`   |

Platform-specific packages are installed automatically as optional dependencies.

### Native Module Loading

The package attempts to load the native module in this order:

1. Platform-specific optional dependency (e.g., `@rainbowindex/oxide-darwin-arm64`)
2. Local `.node` file in the package directory
3. Custom path via `RI_OXIDE_PATH` environment variable
4. Falls back to TypeScript implementation

### Building Native Binaries (Contributors)

To build native binaries locally:

```bash
# Build for current platform
pnpm -C packages/@rainbowindex/oxide build:native

# Build for all configured platforms (CI)
NAPI_TARGETS=aarch64-apple-darwin pnpm -C packages/@rainbowindex/oxide build:native:all
```

Requirements: Rust toolchain (`rustup`), target architecture (`rustup target add <target>`), Node.js >= 20.

## License

MIT