{"owner":"oxc-project","repo":"oxc","hasSkills":true,"totalSkillsCount":27,"totalTokensCount":30580,"categories":["anthropic-skill","root-instruction","project-spec","copilot-instructions","plugin-manifest","cursor-rule"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/insta-snapshots/SKILL.md","AGENTS.md","ARCHITECTURE.md",".agents/skills/migrate-oxlint/SKILL.md",".agents/skills/migrate-oxfmt/SKILL.md",".agents/skills/performance-lint-rules/SKILL.md",".github/copilot-instructions.md","npm/oxlint-plugins/README.md","packages/codegen/CHANGELOG.md","crates/oxc_transformer_plugins/README.md","packages/codegen/DESIGN.md","packages/codegen/README.md","packages/codegen/package.json","packages/codegen/tsconfig.json","packages/codegen/test/fixtures/README.md","apps/oxfmt/test/cli/extensions/0.snap.md","apps/oxfmt/test/cli/extensions/options.json","crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json","crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json","crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json","crates/oxc_linter/fixtures/extends_config/plugins/jest.json","crates/oxc_linter/fixtures/extends_config/plugins/parent.json","crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json","crates/oxc_linter/fixtures/extends_config/plugins/react.json","crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json","apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json",".cursorrules (typescript-expo-jest-detox-cursorrules-prompt-file.mdc)"],"skills":{".agents/skills/insta-snapshots/SKILL.md":"---\nname: insta-snapshots\ndescription: Review and update Oxc Insta snapshots non-interactively after expected output changes or snapshot test failures.\n---\n\n# Insta snapshots\n\nRun the affected test to generate pending snapshots. Use `cargo insta pending-snapshots` and read the old `.snap` and new `.snap.new` outputs to decide whether each change matches the intended behavior.\n\nUse the CLI instead of the interactive `cargo insta review` UI or manually editing expected output:\n\n```bash\ncargo insta pending-snapshots\ncargo insta accept --snapshot <snapshot_name>\n```\n\nScope acceptance to the reviewed snapshots from this task; check `cargo insta accept --help` if a name or package filter is ambiguous. Avoid workspace-wide acceptance or rejection when unrelated pending snapshots exist. Fix unexpected output in the implementation before accepting it.\n\nAfter accepting, inspect the snapshot diff and rerun the affected test. Finish with the intended `.snap` updates and no task-generated `.snap.new` files left pending. Updating snapshots does not itself call for a commit.\n\nSnapshots record expected output, including successful output in some crates. Linter diagnostic snapshots are under `crates/oxc_linter/src/snapshots/`; other crates have their own locations. Conformance `.snap.md` reports use a separate update mechanism; inspect the producing test harness rather than treating them as Insta pending snapshots.\n","AGENTS.md":"# AGENTS.md - AI Assistant Guide for Oxc\n\nOxc is a high-performance JavaScript/TypeScript toolchain written in Rust containing:\n\n- Parser (JS/TS with AST), Linter (oxlint), Formatter (oxfmt), Transformer, Minifier\n\n## AI Usage Policy for Contributors\n\n**IMPORTANT**: If you are an AI assistant helping a human contributor:\n\n- **Disclose AI usage** - Contributors must disclose when AI tools were used to reduce maintainer fatigue\n- **Full responsibility** - The human contributor is responsible for all AI-generated issues or PRs they submit\n- **Quality standards** - Low-quality or unreviewed AI content will be closed immediately\n\nAll AI-generated code must be thoroughly reviewed, tested, and understood by the contributor before submission. Code should meet Oxc's performance and quality standards.\n\n- **Ban policy** - Contributors who submit repeated low-quality (\"slop\") PRs will be banned without prior warning. Bans may be lifted if you commit to contributing to Oxc in accordance with the policy above. You may request an unban via our Discord.\n\n## Repository Structure\n\nRust workspace with key directories:\n\n- `crates/` - Core functionality (start here when exploring)\n- `apps/` - Application binaries (oxlint, oxfmt)\n- `napi/` - Node.js bindings\n- `npm/` - npm packages\n- `tasks/` - Development tools/automation\n\nAvoid editing `generated` subdirectories.\n\n### Core Crates\n\n- `oxc_parser` - JS/TS parser\n- `oxc_ast` - AST definitions/utilities\n- `oxc_semantic` - Semantic analysis/symbols/scopes\n- `oxc_linter` - Linting engine/rules\n- `oxc_formatter` - Code formatting (Prettier-like for JS)\n- `oxc_formatter_*` - Code formatting (Prettier-like for other languages)\n- `oxc_transformer` - Code transformation (Babel-like)\n- `oxc_minifier` - Code minification\n- `oxc_codegen` - Code generation\n- `oxc_isolated_declarations` - TypeScript declaration generation\n- `oxc_diagnostics` - Error reporting\n- `oxc_traverse` - AST traversal utilities\n- `oxc_allocator` - Memory management\n- `oxc_language_server` - LSP server for editor integration\n- `oxc` - Main crate\n\n## Development Commands\n\nPrerequisites: Rust (MSRV: 1.97), Node.js, pnpm, just\n\n**Setup Notes:**\n\n- All tools already installed (`cargo-insta`, `typos-cli`, `cargo-shear`, `ast-grep`)\n- Rust components already installed (`clippy`, `rust-docs`, `rustfmt`)\n- Use Conventional Commits for commit messages; `.github/workflows/pr.yml` requires a scoped title like `fix(parser): handle trailing comma`\n- Choose verification from the changed code; reserve `just ready` for an optional broad integration checkpoint as described below.\n- You run in an environment where `ast-grep` is available; whenever a search requires syntax-aware or structural matching, default to `ast-grep --lang rust -p '<pattern>'` (or set `--lang` appropriately) and avoid falling back to text-only tools like `rg` or `grep` unless I explicitly request a plain-text search.\n\n### Essential Commands\n\n```bash\njust fmt             # Format code (run after modifications)\njust test            # Run unit/integration tests\njust conformance     # Run conformance tests\njust ready           # Optional broad integration checkpoint (see below)\ncargo lintgen        # Regenerate linter rules enum and impls after adding/modifying rules\ncargo lint-timings   # Update linter timing data after changing linter rule codegen\n\n# Crate-specific updates\njust ast             # Update generated files (oxc_ast changes)\njust minsize         # Update size snapshots (oxc_minifier changes)\njust allocs          # Update allocation snapshots (oxc_parser changes)\n\n# Useful shortcuts\njust watch \"command\" # Watch files and re-run command\njust example tool    # Run tool example (e.g., just example linter)\n```\n\nMore commands can be found in `justfile`.\n\n### Local and stacked development\n\nBefore selecting verification checks, read the [verification guidance](.agents/verification.md).\n\n- Select checks from changed code and affected consumers; for a stack, use each PR's diff against its parent.\n- Reuse passing results while tested inputs remain unchanged. Reserve `just ready` for stable integration checkpoints when broad validation is needed.\n- Preserve required subsystem checks and per-PR CI; a passing stack tip does not establish that intermediate PRs pass.\n\n## Manual Testing & Examples\n\nRun crate examples for quick testing and debugging:\n\n```bash\ncargo run -p <crate_name> --example <example_name> -- [args]\n\n# Common examples:\ncargo run -p oxc_parser --example parser -- test.js\ncargo run -p oxc_linter --example linter -- src/\ncargo run -p oxc_transformer --example transformer -- input.js\ncargo run -p oxc --example compiler --features=\"full\" -- test.js\n```\n\nModify examples in `crates/<crate_name>/examples/` to test specific scenarios.\n\n## Code Navigation\n\n### Key Locations\n\n- AST: Start with `oxc_ast`, use `oxc_ast_visit` for traversal\n- Linting rules: `crates/oxc_linter/src/rules/` (visitor pattern)\n- Parser: `crates/oxc_parser/src/lib.rs`, lexer in `src/lexer/`\n- Tests: Co-located with source, integration in `tests/`, uses `insta` for snapshots\n\n### Conventions\n\n- Use `oxc_allocator` for memory management\n- Follow rustfmt config in `.rustfmt.toml`\n- Use `oxc_diagnostics` for errors with source locations\n- Performance-critical: avoid unnecessary allocations\n\n## Common Tasks\n\n### Adding Linting Rule\n\n1. Use rule generator: `just new-rule <name>` (ESLint rules)\n   - Or plugin-specific: `just new-ts-rule`, `just new-jest-rule`, etc.\n2. Implement using visitor pattern\n3. Add tests in same module\n4. Register in appropriate category\n\n### Parser Changes\n\n1. Research and test grammar changes thoroughly\n2. Update AST definitions in `oxc_ast` if needed\n3. Ensure existing tests pass\n4. Add tests for new features\n\n### Working with Transformations\n\n1. Understand AST structure first\n2. Use visitor pattern for traversal\n3. Handle node ownership/allocator carefully\n4. Test with various input patterns\n\n## Testing\n\nOxc uses multiple testing approaches tailored to each crate:\n\n- **Unit/Integration tests**: Standard Rust tests in `tests/` directories\n- **Conformance tests**: Against external suites (Test262, Babel, TypeScript)\n- **Snapshot tests**: Track failures and expected outputs using `insta`\n\n### Quick Test Commands\n\n```bash\njust test                              # Run all Rust tests\njust conformance                       # Run all conformance tests (alias: cargo coverage)\ncargo test -p <crate_name>             # Test specific crate\n\n# Conformance for specific tools\ncargo coverage parser                  # Parser conformance\ncargo coverage transformer             # Transformer conformance\ncargo run -p oxc_transform_conformance # Transformer Babel tests\n\n# NAPI packages\npnpm test                              # Test all Node.js bindings\n```\n\n### Crate-Specific Testing\n\nEach crate follows distinct testing patterns:\n\n#### oxc_parser\n\n- **Conformance only** via `tasks/coverage`\n- **Command**: `cargo coverage parser`\n- **Suites**: Test262, Babel, TypeScript\n- **Special**: `just allocs` for allocation tracking\n\n#### oxc_linter\n\n- **Inline tests** in rule files (`src/rules/**/*.rs`)\n- **Pattern**: Use `Tester` helper with pass/fail cases\n\n```rust\n#[test]\nfn test() {\n    Tester::new(RuleName::NAME, RuleName::PLUGIN, pass, fail)\n        .test_and_snapshot();\n}\n```\n\n#### oxc_formatter (and other `oxc_formatter_*` language crates)\n\n- Read the target crate's `AGENTS.md`; it includes the shared formatter policy plus language-specific rules and verification commands\n\n#### oxc_minifier\n\n- **Unit tests** in `tests/` subdirectories:\n  - `ecmascript/` - Operations\n  - `peephole/` - Optimizations\n  - `mangler/` - Name mangling\n- **Size tracking**: `just minsize`\n\n#### oxc_transformer\n\n- **Multiple approaches**:\n  - Unit tests: `tests/integrations/`\n  - Conformance: `tasks/transform_conformance/`\n  - Babel plugins: `tasks/transform_conformance/tests/babel-plugin-*/`\n- **Commands**:\n\n```bash\ncargo test -p oxc_transformer                    # Unit tests\ncargo run -p oxc_transform_conformance          # Conformance\njust test-transform --filter <path>             # Filter tests\n```\n\n#### oxc_codegen\n\n- **Integration tests** in `tests/integration/`\n- Test files: `js.rs`, `ts.rs`, `sourcemap.rs`, `comments.rs`\n\n#### oxc_isolated_declarations\n\n- **Snapshot testing** with `insta`\n- Input: `tests/fixtures/*.{ts,tsx}`\n- Output: `tests/snapshots/*.snap`\n- Update: `cargo insta review`\n\n#### oxc_semantic\n\n- **Multiple testing approaches**:\n  - **Conformance tests** (`tests/conformance/`) - Contract-as-code tests for symbols and identifier references\n  - **Integration tests** (`tests/integration/`) - Tests for scopes, symbols, modules, classes, CFG\n  - **Snapshot tests** (`tests/main.rs`) - Verifies scoping data correctness (scope trees, bindings, symbols, references) using `insta` snapshots from `fixtures/`\n  - **Coverage tests** - Via `tasks/coverage` using Test262, Babel, TypeScript suites\n- **Command**: `cargo test -p oxc_semantic`\n- **Update snapshots**: `cargo insta review`\n\n#### Other Crates\n\n- **oxc_traverse**: AST traversal - `cargo test -p oxc_traverse`\n- **oxc_ecmascript**: ECMAScript operations - `cargo test -p oxc_ecmascript`\n- **oxc_regular_expression**: Regex parsing - `cargo test -p oxc_regular_expression`\n- **oxc_syntax**: Syntax utilities - `cargo test -p oxc_syntax`\n- **oxc_language_server**: Editor integration - `cargo test -p oxc_language_server`\n\n### Conformance Testing Foundation\n\n**CRITICAL**: These external test suites are the CORE of Oxc's testing strategy, providing thousands of real-world test cases from mature JavaScript ecosystem projects. They ensure Oxc correctly handles the full complexity of JavaScript/TypeScript.\n\nSuites cloned via `just submodules`:\n\n| Submodule            | Description                                                                                                                                        | Location                            | Used by Crates                                           |\n| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------- |\n| `test262`            | **ECMAScript Conformance Suite**<br>Official JavaScript test suite from TC39, testing compliance with the ECMAScript specification                 | `tasks/coverage/test262`            | parser, semantic, codegen, transformer, minifier, estree |\n| `babel`              | **Babel Test Suite**<br>Comprehensive transformation and parsing tests from the Babel compiler, covering modern JavaScript features and edge cases | `tasks/coverage/babel`              | parser, semantic, codegen, transformer, minifier         |\n| `typescript`         | **TypeScript Test Suite**<br>Microsoft's TypeScript compiler tests, ensuring correct handling of TypeScript syntax and semantics                   | `tasks/coverage/typescript`         | parser, semantic, codegen, transformer, estree           |\n| `estree-conformance` | **ESTree Conformance Tests**<br>Test262, TypeScript, and acorn-jsx suites adapted for ESTree format validation, ensuring correct AST structure     | `tasks/coverage/estree-conformance` | estree                                                   |\n\n**These suites provide:**\n\n- **Thousands of battle-tested cases** from real-world usage\n- **Edge case coverage** that would be impossible to write manually\n- **Industry standard compliance** ensuring compatibility\n- **Continuous validation** against evolving JavaScript standards\n\nRun all conformance tests with `cargo coverage` or `just conformance`.\n\n### Searching Test Suites\n\nThese test suites are pre-cloned and ready to search:\n\n- **Test262** (`tasks/coverage/test262/`) - ECMAScript spec compliance\n- **Babel** (`tasks/coverage/babel/`) - Parsing and transformation edge cases\n- **TypeScript** (`tasks/coverage/typescript/`) - TypeScript syntax and semantics\n\nNOTE: These suites are script-cloned and fully gitignored. ripgrep respects `.gitignore`, so a plain `rg` inside them silently returns nothing.\nUse `rg --no-ignore` (or `-u`) when searching them.\n\n### Snapshot Testing\n\n- Uses `insta` crate for snapshot testing\n- Snapshots track **failing** tests, not passing ones\n- Located in `tasks/coverage/snapshots/` and conformance directories\n- Update with `cargo insta review` after changes\n- Formats: `.snap` (counts), `.snap.md` (detailed failures)\n\n### NAPI (Node.js Bindings) Testing\n\nNAPI packages use **Vitest** for testing Node.js bindings:\n\n```bash\npnpm run build-test   # Build all NAPI packages\npnpm test             # Run all NAPI tests\n```\n\n**Package-specific commands:**\n\n- `oxc-parser`: `cd napi/parser && pnpm run build-test && pnpm test` (also has `pnpm test-browser`)\n- `oxc-minify`: `cd napi/minify && pnpm run build-test && pnpm test`\n- `oxc-transform`: `cd napi/transform && pnpm run build-test && pnpm test`\n\nTests are TypeScript files in each package's `test/` directory.\n\n### Where to Add Tests\n\n| Crate                 | Location                                 |\n| --------------------- | ---------------------------------------- |\n| Parser                | `tasks/coverage/misc/pass/` or `fail/`   |\n| Linter                | Inline in rule files                     |\n| Formatter             | `tests/fixtures/` in the formatter crate |\n| Minifier              | `tests/` subdirectories                  |\n| Transformer           | `tests/integrations/` or Babel fixtures  |\n| Codegen               | `tests/integration/`                     |\n| Isolated Declarations | `tests/fixtures/*.ts`                    |\n| Semantic              | `tests/` directory                       |\n| NAPI packages         | `test/` directory (Vitest)               |\n| Language Server       | Inline and `/fixtures`                   |\n\n### Linter Codegen Changes\n\nWhen changing linter codegen files such as `tasks/linter_codegen/**` or generated linter rule runner files under `crates/oxc_linter/src/generated/`, run `cargo lintgen` and `cargo lint-timings` so generated rule metadata and linter timing data stay in sync.\n\n## Notes\n\n- Rapidly evolving project - APIs may change\n- Performance is critical for all changes\n- Maintain JS/TS standard compatibility\n- Breaking changes need documentation and discussion\n\n---\n\nFor human contributors see `CONTRIBUTING.md` and [oxc.rs](https://oxc.rs/docs/contribute/introduction.html)\n","ARCHITECTURE.md":"# Architecture\n\n## System Overview\n\nOxc (The Oxidation Compiler) is a collection of high-performance JavaScript and TypeScript tools written in Rust. The system is designed as a modular, composable set of compiler components that can be used independently or together to build complete toolchains for JavaScript/TypeScript development.\n\n### Core Mission\n\n- **Performance**: Deliver faster performance than existing JavaScript tools\n- **Correctness**: Maintain compatibility with JavaScript/TypeScript standards\n- **Modularity**: Enable users to compose tools according to their specific needs\n- **Developer Experience**: Provide excellent error messages and tooling integration\n\n## High-Level Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                          Applications                           │\n├─────────────────────────────────────────────────────────────────┤\n│  oxlint  │  Language Server  │  NAPI Bindings  │  Future Tools  │\n├─────────────────────────────────────────────────────────────────┤\n│                        Core Libraries                           │\n├─────────────────────────────────────────────────────────────────┤\n│ Parser │ Semantic │ Linter │ Transformer │ Minifier │ Codegen   │\n├─────────────────────────────────────────────────────────────────┤\n│                    Foundation Libraries                         │\n├─────────────────────────────────────────────────────────────────┤\n│    AST    │  Allocator  │  Diagnostics  │   Span   │  Syntax    │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Architecture Principles\n\n### 1. Zero-Copy Architecture\n\nThe system is built around an arena allocator (`oxc_allocator`) that enables zero-copy operations throughout the compilation pipeline. All AST nodes are allocated in a single arena, eliminating the need for reference counting or garbage collection.\n\n### 2. Visitor Pattern\n\nAST traversal is implemented using the visitor pattern (`oxc_ast_visit`) with automatic visitor generation through procedural macros. This ensures type safety and performance while maintaining code clarity.\n\n### 3. Shared Infrastructure\n\nCommon functionality like error reporting (`oxc_diagnostics`), source positions (`oxc_span`), and syntax definitions (`oxc_syntax`) are shared across all components to ensure consistency.\n\n## Core Components\n\n### Foundation Layer\n\n#### oxc_allocator\n\n- **Purpose**: Arena-based memory allocator for zero-copy operations\n- **Key Features**:\n  - Single allocation arena for entire compilation unit\n  - Eliminates need for Rc/Arc in hot paths\n  - Enables structural sharing of AST nodes\n- **Dependencies**: None (foundational)\n\n#### oxc_span\n\n- **Purpose**: Source position tracking and text manipulation\n- **Key Features**:\n  - Byte-based indexing for UTF-8 correctness\n  - Efficient span operations for source maps\n  - Integration with diagnostic reporting\n- **Dependencies**: None (foundational)\n\n#### oxc_syntax\n\n- **Purpose**: JavaScript/TypeScript language definitions\n- **Key Features**:\n  - Token definitions and keyword mappings\n  - Language feature flags and compatibility\n  - Shared syntax validation logic\n- **Dependencies**: oxc_span\n\n#### oxc_diagnostics\n\n- **Purpose**: Error reporting and diagnostic infrastructure\n- **Key Features**:\n  - Rich error messages with source context\n  - Multiple output formats (JSON, pretty-printed)\n  - Integration with language server protocol\n- **Dependencies**: oxc_span\n\n#### oxc_ast\n\n- **Purpose**: Abstract Syntax Tree definitions and utilities\n- **Key Features**:\n  - Complete JavaScript/TypeScript AST coverage\n  - Generated visitor traits for type safety\n  - Serialization support for caching\n- **Dependencies**: oxc_allocator, oxc_span, oxc_syntax\n\n##### AST Design Principles\n\nThe Oxc AST differs significantly from the [estree](https://github.com/estree/estree) AST specification by removing ambiguous nodes and introducing distinct types. While many existing JavaScript tools rely on estree as their AST specification, a notable drawback is its abundance of ambiguous nodes that often leads to confusion during development.\n\nFor example, instead of using a generic estree `Identifier`, the Oxc AST provides specific types such as:\n\n- `BindingIdentifier` - for variable declarations and bindings\n- `IdentifierReference` - for variable references\n- `IdentifierName` - for property names and labels\n\nThis clear distinction greatly enhances the development experience by aligning more closely with the ECMAScript specification and providing better type safety.\n\n### Core Processing Layer\n\n#### oxc_parser\n\n- **Purpose**: JavaScript/TypeScript parsing\n- **Key Features**:\n  - Hand-written recursive descent parser\n  - Full ES2024+ and TypeScript support\n  - Preservation of comments and trivia\n- **Dependencies**: oxc_allocator, oxc_ast, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_semantic\n\n- **Purpose**: Semantic analysis and symbol resolution\n- **Key Features**:\n  - Scope chain construction\n  - Symbol table generation\n  - Dead code detection\n- **Dependencies**: oxc_ast, oxc_cfg, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_linter\n\n- **Purpose**: ESLint-compatible linting engine\n- **Key Features**:\n  - 200+ built-in rules\n  - Plugin architecture for custom rules\n  - Automatic fixing for many rules\n  - Configuration compatibility with ESLint\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_diagnostics, oxc_cfg\n\n#### oxc_transformer\n\n- **Purpose**: Code transformation and transpilation\n- **Key Features**:\n  - TypeScript to JavaScript transformation\n  - Modern JavaScript feature transpilation\n  - React JSX transformation\n  - Babel plugin compatibility layer\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_allocator\n\n#### oxc_minifier\n\n- **Purpose**: Code size optimization\n- **Key Features**:\n  - Dead code elimination\n  - Constant folding and propagation\n  - Identifier mangling integration\n  - Statement and expression optimization\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_mangler\n\n#### oxc_codegen\n\n- **Purpose**: AST to source code generation\n- **Key Features**:\n  - Configurable output formatting\n  - Source map generation\n  - Comment preservation options\n  - Minified and pretty-printed output modes\n- **Dependencies**: oxc_ast, oxc_span\n\n### Application Layer\n\n#### oxlint (apps/oxlint)\n\n- **Purpose**: Command-line linter application\n- **Key Features**:\n  - File discovery and parallel processing\n  - Configuration file support\n  - Multiple output formats\n  - Integration with CI/CD systems\n- **Dependencies**: oxc_linter, oxc_parser, oxc_semantic\n\n#### Language Server (oxc_language_server)\n\n- **Purpose**: LSP implementation for editor integration\n- **Key Features**:\n  - Real-time diagnostics\n  - Go-to-definition and references\n  - Symbol search and completion\n- **Dependencies**: All core components\n\n#### NAPI Bindings (napi/\\*)\n\n- **Purpose**: Node.js integration layer\n- **Key Features**:\n  - Parser bindings for JavaScript tooling\n  - Linter integration for build tools\n  - Transform pipeline for bundlers\n  - Async processing support\n- **Dependencies**: Core components + Node.js FFI\n\n## Data Flow\n\n### Compilation Pipeline\n\n1. **Input**: Source text + configuration\n2. **Lexing/Parsing**: `oxc_parser` → AST + comments\n3. **Semantic Analysis**: `oxc_semantic` → Symbol table + scope info\n4. **Processing**: Tool-specific analysis (linting, transformation, etc.)\n5. **Output**: Results (diagnostics, transformed code, etc.)\n\n### Memory Management Flow\n\n```\nSource Text → Arena Allocator → AST Nodes → Visitors → Results\n     ↓              ↓              ↓           ↓          ↓\n   UTF-8          Arena         Borrowed    Zero-copy   Owned\n  String         Memory         References  Processing  Output\n```\n\n## Quality Attributes\n\n### Performance\n\n- **Target**: 10-100x faster than comparable tools\n- **Strategies**:\n  - Arena allocation for memory efficiency\n  - Zero-copy data structures\n  - Parallel processing where possible\n  - Minimal allocations in hot paths\n\n#### Parser Performance Implementation\n\n- AST is allocated in a memory arena (oxc_allocator) for fast AST memory allocation and deallocation\n- Short strings are inlined by [CompactString](https://crates.io/crates/compact_str)\n- No other heap allocations are done except the above two\n- Scope binding, symbol resolution and some syntax errors are not done in the parser, they are delegated to the semantic analyzer\n\n#### Linter Performance Implementation\n\n- Oxc parser is used for optimal performance\n- AST visit is a fast operation due to linear memory scan from the memory arena\n- Files are linted in a multi-threaded environment, so scales with the total number of CPU cores\n- Every single lint rule is tuned for performance\n\n### Correctness\n\n- **Target**: 100% compatibility with language standards\n- **Strategies**:\n  - Comprehensive test suites\n  - Real-world codebase testing\n  - Conformance testing against official specs\n  - Conservative error handling\n\n### Maintainability\n\n- **Target**: Clear, reviewable, extensible codebase\n- **Strategies**:\n  - Strong type system usage\n  - Procedural macro code generation\n  - Clear separation of concerns\n  - Comprehensive documentation\n\n### Usability\n\n- **Target**: Drop-in replacement for existing tools\n- **Strategies**:\n  - Configuration compatibility\n  - Familiar CLI interfaces\n  - Rich error messages\n  - Editor integration\n\n## Technical Constraints\n\n### Language Choice\n\n- **Rust**: Chosen for memory safety, performance, and zero-cost abstractions\n- **MSRV**: N-2 policy for stability\n\n### Memory Model\n\n- **Arena Allocation**: Single arena per compilation unit\n- **Lifetime Management**: Explicit lifetimes tied to arena\n- **No Garbage Collection**: Manual memory management for predictable performance\n\n### Threading Model\n\n- **File-level Parallelism**: Multiple files processed in parallel\n- **Single-threaded Pipeline**: Each file processed by single thread\n- **Shared State**: Minimal shared state to avoid synchronization overhead\n\n### Compatibility Requirements\n\n- **JavaScript**: ES2024+ compatibility\n- **TypeScript**: Latest TypeScript syntax support\n- **Node.js**: LTS versions through NAPI bindings\n- **Editors**: LSP compatibility for all major editors\n\n## Design Decisions\n\n### Arena Allocator Choice\n\n**Decision**: Use custom arena allocator instead of Rc/Arc\n**Rationale**:\n\n- Eliminates reference counting overhead\n- Enables zero-copy string operations\n- Simplifies memory management\n- Improves cache locality\n\n**Trade-offs**:\n\n- ✅ 10-50% performance improvement\n- ✅ Simplified ownership model\n- ❌ Requires lifetime management\n- ❌ Less flexible memory patterns\n\n### Hand-written Parser\n\n**Decision**: Implement recursive descent parser instead of parser generator\n**Rationale**:\n\n- Easier debugging and maintenance\n- More efficient generated code\n- Faster compilation times\n\n**Trade-offs**:\n\n- ✅ Better performance and error messages\n- ✅ More maintainable code\n- ❌ More manual implementation work\n- ❌ Higher risk of parser bugs\n\n### Visitor Pattern\n\n**Decision**: Use visitor pattern with procedural macros\n**Rationale**:\n\n- Type-safe AST traversal\n- Automatic visitor generation\n- Consistent patterns across tools\n- Efficient dispatch\n\n**Trade-offs**:\n\n- ✅ Type safety and performance\n- ✅ Reduced boilerplate code\n- ❌ Compile-time complexity\n- ❌ Learning curve for contributors\n\n## Future Considerations\n\n### Planned Extensions\n\n- **Formatter**: Complete code formatting tool\n- **Bundler**: Integration with bundling workflows\n- **Type Checker**: Full TypeScript type checking\n- **Plugin System**: User-defined transformations\n\n### Scalability Concerns\n\n- **Large Codebases**: Processing optimization improvements\n- **Memory Usage**: Streaming processing for huge files\n- **Parallel Processing**: Fine-grained parallelization\n\n### Technology Evolution\n\n- **Rust Evolution**: Leveraging new language features\n- **JavaScript Standards**: Keeping pace with TC39 proposals\n- **Editor Integration**: Advanced IDE features\n\n## Development Infrastructure\n\n### Test Infrastructure\n\nCorrectness and reliability are taken extremely seriously in Oxc. We spend significant effort on strengthening the test infrastructure to prevent problems from propagating to downstream tools:\n\n- **Conformance Testing**: Test262, Babel, and TypeScript conformance suites\n- **Fuzzing**: Extensive fuzzing to discover edge cases\n- **Snapshot Testing**: Linter diagnostic snapshots for regression prevention\n- **Ecosystem CI**: Testing against real-world codebases\n- **Idempotency Testing**: Ensuring transformations are stable\n- **Code Coverage**: Comprehensive coverage tracking\n- **End-to-End Testing**: Testing against top 3000 npm packages\n\n### Build and Development Tools\n\n- **Rust**: MSRV 1.86.0+ with clippy and rustfmt integration\n- **Just**: Command runner for development tasks (`just --list` for available commands)\n- **Performance Monitoring**: Continuous benchmarking and performance regression detection\n- **Cross-platform**: Support for Linux, macOS, and Windows\n- **CI/CD**: Automated testing, building, and publishing pipelines\n\nFor detailed development guidelines, see [CONTRIBUTING.md](./CONTRIBUTING.md) and [AGENTS.md](./AGENTS.md).\n\n---\n\nThis architecture document follows the [architecture.md](https://architecture.md/) format for documenting software architecture decisions and system design.\n",".agents/skills/migrate-oxlint/SKILL.md":"---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                         | Oxlint Plugin Name |\n| ----------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                    | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks`   | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`     | `import`           |\n| `eslint-plugin-unicorn`                               | `unicorn`          |\n| `eslint-plugin-jsx-a11y` / `eslint-plugin-jsx-a11y-x` | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                            | `react-perf`       |\n| `eslint-plugin-promise`                               | `promise`          |\n| `eslint-plugin-jest`                                  | `jest`             |\n| `@vitest/eslint-plugin`                               | `vitest`           |\n| `eslint-plugin-jsdoc`                                 | `jsdoc`            |\n| `eslint-plugin-next`                                  | `nextjs`           |\n| `eslint-plugin-node`                                  | `node`             |\n| `eslint-plugin-vue`                                   | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n",".agents/skills/migrate-oxfmt/SKILL.md":"---\nname: migrate-oxfmt\ndescription: Guide for migrating a project from Prettier or Biome to Oxfmt. Use when asked to migrate, convert, or switch a project's formatter from Prettier or Biome to Oxfmt.\n---\n\nThis skill guides you through migrating a project from Prettier or Biome to [Oxfmt](https://oxc.rs/docs/guide/usage/formatter).\n\n## Overview\n\nOxfmt is a high-performance, Prettier-compatible code formatter. Most Prettier options are supported directly.\n\nAn automated migration tool is built into Oxfmt, supporting both Prettier and Biome as migration sources.\n\n## Step 1: Run Automated Migration\n\nFirst, decide whether the automated tool fits:\n\n- Static config (JSON, or a JS/TS config that just exports a plain object): use `--migrate` below.\n- Dynamic JS/TS config (logic such as environment branches or computed values): `--migrate` writes only a resolved snapshot of the values, and the logic does not survive (a warning is printed). Port the logic to `oxfmt.config.ts` manually with the option mappings in this guide.\n- Nested per-directory configs: `--migrate` only handles the config in the current directory. Run it again in each nested directory (see \"Nested Config\" in Step 2).\n\n### From Prettier\n\n```bash\nnpx oxfmt@latest --migrate prettier\n```\n\nThis will:\n\n- Find your Prettier config in the current directory, in the same order as Prettier\n  - Supported: `package.json` (`prettier` field), `.prettierrc` (JSON only), `.prettierrc.json`, and JS/TS configs (`.prettierrc.*`, `prettier.config.*`)\n  - Shareable configs (e.g. `\"prettier\": \"@company/prettier-config\"`) are resolved\n  - YAML, JSON5 and TOML configs are not supported. Convert them to JSON or JS first\n- Create `.oxfmtrc.json` with migrated options\n  - Only Prettier-compatible options are migrated, others are skipped with a warning\n- Migrate `overrides`\n  - `overrides[].options.parser` is migrated to `associations[].language`\n- Migrate `.prettierignore` patterns to `ignorePatterns`\n- Migrate `prettier-plugin-tailwindcss` options to `sortTailwindcss`\n- Migrate `prettier-plugin-svelte` options to `svelte` (`svelteSortOrder` → `sortOrder`, `svelteAllowShorthand` → `allowShorthand`, `svelteIndentScriptAndStyle` → `indentScriptAndStyle`)\n- Migrate `prettier-plugin-astro` options to `astro` (`astroAllowShorthand` → `allowShorthand`, `astroSkipFrontmatter` → `skipFrontmatter`, `astroCompressHTML` → `compressHTML`)\n- Detect `prettier-plugin-packagejson` and enable `sortPackageJson`\n- Skip other plugins with a warning\n\nNotes (Prettier):\n\n- Fails if no Prettier config is found in the current directory. Use `--init` to create a blank `.oxfmtrc.json` instead.\n- The generated `.oxfmtrc.json` may not be formatted yet. Run `oxfmt` after migration.\n\n### From Biome\n\n```bash\nnpx oxfmt@latest --migrate biome\n```\n\nThis will:\n\n- Find and read `biome.json` or `biome.jsonc`\n- Create `.oxfmtrc.json` with migrated options\n- Migrate negated patterns from `files.includes` to `ignorePatterns`\n- Map Biome's two-level config (`formatter.*` and `javascript.formatter.*`) to Oxfmt options\n\nBiome option mapping:\n\n| Biome                                                       | Oxfmt                             |\n| ----------------------------------------------------------- | --------------------------------- |\n| `formatter.indentStyle` (`\"tab\"`/`\"space\"`)                 | `useTabs` (`true`/`false`)        |\n| `formatter.indentWidth`                                     | `tabWidth`                        |\n| `formatter.lineWidth`                                       | `printWidth`                      |\n| `javascript.formatter.quoteStyle`                           | `singleQuote`                     |\n| `javascript.formatter.jsxQuoteStyle`                        | `jsxSingleQuote`                  |\n| `javascript.formatter.quoteProperties` (`\"asNeeded\"`)       | `quoteProps` (`\"as-needed\"`)      |\n| `javascript.formatter.trailingCommas`                       | `trailingComma`                   |\n| `javascript.formatter.semicolons` (`\"always\"`/`\"asNeeded\"`) | `semi` (`true`/`false`)           |\n| `javascript.formatter.arrowParentheses` (`\"asNeeded\"`)      | `arrowParens` (`\"avoid\"`)         |\n| `javascript.formatter.bracketSameLine`                      | `bracketSameLine`                 |\n| `formatter.bracketSpacing`                                  | `bracketSpacing`                  |\n| `formatter.attributePosition` (`\"multiline\"`)               | `singleAttributePerLine` (`true`) |\n\nNotes (Biome):\n\n- For `formatter.*` options in the table, a `javascript.formatter.*` value of the same name takes precedence when present. `bracketSameLine` is read only from `javascript.formatter.bracketSameLine`.\n- Options not set in `biome.json` are written to `.oxfmtrc.json` with Biome's default values explicitly (e.g. `printWidth: 80`, `useTabs: true`), so the output preserves Biome's formatting behavior.\n- If no Biome config is found, creates a blank `.oxfmtrc.json` instead.\n- `overrides` cannot be auto-migrated and must be converted manually (a warning is printed if detected).\n\nNotes (both sources):\n\n- Fails if an Oxfmt config (`.oxfmtrc.json`, `.oxfmtrc.jsonc`, `oxfmt.config.ts` or `oxfmt.config.mts`) already exists. Delete it first if you want to re-run.\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxfmtrc.json` for these key differences:\n\n### printWidth\n\nPrettier and Biome default is 80, Oxfmt default is 100. The migration tool sets `printWidth: 80` if not specified in your source config. Decide whether to keep 80 or adopt 100.\n\n### Unsupported Options (Prettier only)\n\nAny option not in \"Prettier-Compatible Options\" below is skipped with a warning, including options of unsupported plugins (e.g. `importOrder`). Notable ones:\n\n| Option                          | Status                                                                    |\n| ------------------------------- | ------------------------------------------------------------------------- |\n| `endOfLine: \"auto\"`             | Not supported. Use `\"lf\"` or `\"crlf\"` explicitly                          |\n| `experimentalTernaries`         | Not supported                                                             |\n| `requirePragma`, `insertPragma` | Not supported                                                             |\n| `parser`                        | Skipped at top-level. In `overrides`, migrated to `associations` if known |\n| `plugins`                       | Only known plugins are migrated (see Step 1)                              |\n\nRegex values (e.g. `\"/^my-/\"`) in `tailwindFunctions` / `tailwindAttributes` are also skipped with a warning, Oxfmt only supports literal strings there.\n\n### sortPackageJson (Prettier only)\n\nEnabled by default in Oxfmt, but the migration tool disables it unless `prettier-plugin-packagejson` was detected. Review whether you want this enabled.\n\nNote: Oxfmt's sorting algorithm differs from `prettier-plugin-packagejson`.\n\n### embeddedLanguageFormatting (Prettier only)\n\nEmbedded language formatting (e.g., CSS-in-JS) generally works.\n\n### overrides\n\nPrettier `overrides` are auto-migrated. Unsupported options in them are skipped with a warning, so review the result.\n\n`overrides[].options.parser` is migrated to `associations`, which tells Oxfmt which language to use for matched files:\n\n```json\n{\n  \"associations\": [{ \"files\": [\"*.svg\"], \"language\": \"html\" }]\n}\n```\n\n`language` is a Shiki language id (as in Markdown code fences), not a Prettier parser name (e.g. `angular-html` instead of `angular`, `typescript` instead of `babel-ts`).\n\nBiome `overrides` cannot be auto-migrated. Convert manually:\n\n```json\n{\n  \"overrides\": [\n    {\n      \"files\": [\"*.md\"],\n      \"excludeFiles\": [\"CHANGELOG.md\"],\n      \"options\": { \"tabWidth\": 4 }\n    }\n  ]\n}\n```\n\n(`excludeFiles` is optional.)\n\n### Nested Config\n\nOxfmt supports nested configuration files: a config in a subdirectory applies to files under that directory. Pass `--disable-nested-config` to opt out and use only the root config.\n\nHowever, `--migrate` only migrates the config in the current directory, and does not walk subdirectories. If the project has per-directory Prettier or Biome configs, run `--migrate` in each of those directories.\n\n### Prettier-Compatible Options\n\nThese options transfer directly with the same behavior:\n`printWidth`, `tabWidth`, `useTabs`, `semi`, `singleQuote`, `jsxSingleQuote`, `quoteProps`, `trailingComma`, `arrowParens`, `bracketSpacing`, `bracketSameLine`, `objectWrap`, `experimentalOperatorPosition`, `endOfLine`, `proseWrap`, `htmlWhitespaceSensitivity`, `singleAttributePerLine`, `vueIndentScriptAndStyle`, `embeddedLanguageFormatting`\n\n## Step 3: Configure Oxfmt Extensions\n\nOxfmt offers features not available in Prettier:\n\n### sortImports\n\nSort import statements, inspired by `eslint-plugin-perfectionist/sort-imports` (disabled by default):\n\n```json\n{\n  \"sortImports\": {\n    \"partitionByNewline\": true,\n    \"newlinesBetween\": false\n  }\n}\n```\n\nOther options: `partitionByComment`, `sortSideEffects`, `order`, `ignoreCase`, `internalPattern`, `groups`, `customGroups`.\n\n### sortTailwindcss\n\nReplaces `prettier-plugin-tailwindcss`. Auto-migrated with renamed options:\n\n| Prettier (top-level)         | Oxfmt (`sortTailwindcss.*`) |\n| ---------------------------- | --------------------------- |\n| `tailwindConfig`             | `config`                    |\n| `tailwindStylesheet`         | `stylesheet`                |\n| `tailwindFunctions`          | `functions`                 |\n| `tailwindAttributes`         | `attributes`                |\n| `tailwindPreserveWhitespace` | `preserveWhitespace`        |\n| `tailwindPreserveDuplicates` | `preserveDuplicates`        |\n\n### Other Extensions\n\n| Option               | Default  | Description                                                                     |\n| -------------------- | -------- | ------------------------------------------------------------------------------- |\n| `insertFinalNewline` | `true`   | Whether to add a final newline at end of file                                   |\n| `sortPackageJson`    | `true`   | Sort `package.json` keys. Set `{ \"sortScripts\": true }` to also sort scripts    |\n| `jsdoc`              | disabled | Format JSDoc comments. Set `true` or an options object for fine-grained control |\n| `svelte`             | disabled | Svelte formatting options, replacing `prettier-plugin-svelte` (auto-migrated)   |\n| `astro`              | disabled | Astro formatting options, replacing `prettier-plugin-astro` (auto-migrated)     |\n| `associations`       | `[]`     | Format matched files as the given language (e.g. `*.svg` as `html`)             |\n\n## Step 4: Update CI and Scripts\n\nReplace formatter commands with Oxfmt:\n\n```bash\n# Before (Prettier)\nnpx prettier --write .\nnpx prettier --check .\n\n# Before (Biome)\nnpx biome format --write .\nnpx biome check .\n\n# After\nnpx oxfmt@latest\nnpx oxfmt@latest --check\n```\n\n### Common CLI Options\n\n| Prettier / Biome                                | Oxfmt                                        |\n| ----------------------------------------------- | -------------------------------------------- |\n| `prettier --write .` / `biome format --write .` | `oxfmt` (default: cwd, `--write` mode)       |\n| `prettier --check .` / `biome check .`          | `oxfmt --check`                              |\n| `prettier --list-different .`                   | `oxfmt --list-different`                     |\n| `prettier --config path`                        | `oxfmt --config path`                        |\n| `prettier --ignore-path .prettierignore`        | `oxfmt --ignore-path .prettierignore`        |\n| `cat file \\| prettier --stdin-filepath=file.ts` | `cat file \\| oxfmt --stdin-filepath=file.ts` |\n\n### File Type Coverage\n\n- JS/TS, JSON/JSONC/JSON5, CSS/SCSS/Less, GraphQL, YAML, Markdown, TOML: Formatted natively by Oxfmt\n- HTML, Vue, Svelte, Astro, etc.: Delegated to Prettier internally (when using `npx oxfmt`)\n\n## Tips\n\n- Config file: `.oxfmtrc.jsonc` and `oxfmt.config.ts` are also supported as auto-discovered config file names, in addition to `.oxfmtrc.json`. `--migrate` and `--init` only generate `.oxfmtrc.json`; keep it as-is unless the user explicitly asks for another format or the source config had logic to preserve (see Step 1).\n- EditorConfig: Oxfmt reads `.editorconfig` automatically for `useTabs`, `tabWidth`, `endOfLine`, `insertFinalNewline`, `printWidth`, and `singleQuote`. Options in `.oxfmtrc.json` take precedence.\n  - Nearest one only, does not respect `root`\n- CI: Use `npx oxfmt@latest --check` to enforce formatting in CI.\n- LSP: Run `oxfmt --lsp` for editor integration via Language Server Protocol.\n- Schema support: Add `\"$schema\": \"./node_modules/oxfmt/configuration_schema.json\"` to `.oxfmtrc.json` for editor autocompletion.\n- Init: Run `npx oxfmt@latest --init` to create a default `.oxfmtrc.json` without migration.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/formatter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/formatter/config-file-reference.html)\n- [Unsupported Features](https://oxc.rs/docs/guide/usage/formatter/unsupported-features.html)\n",".agents/skills/performance-lint-rules/SKILL.md":"---\nname: performance-lint-rules\ndescription: Performance review guidance for Oxc linter rule implementations. Use only when reviewing Rust rule code under crates/oxc_linter/src/rules/ or when explicitly auditing those rules for performance improvements.\n---\n\n## Performance Guidelines\n\n### Prefer top-level node kind checks\n\nPut node kind checks at the rule entry point. If a rule only handles a few syntactic forms, start `run` with an `AstKind` match and return for all other nodes, even when a helper filters again internally. This lets lintgen derive narrower `NODE_TYPES` and avoids dispatching the rule on unrelated AST nodes.\n\nAfter changing the relevant node kinds for a rule, regenerate the rule runner with `cargo lintgen` and consider adding or updating `assert_rule_runs_on_node_types` coverage in `crates/oxc_linter/src/rule.rs`.\n\nImplement only the needed entry point. If a rule is a whole-file pass over semantic indexes, use `run_once` by itself. Implementing both `run` and `run_once` prevents useful node-type narrowing.\n\n### Do cheaper checks first\n\nOrder checks from cheapest and most selective to most expensive. Return quickly for common non-matches before doing semantic lookups, allocations, or deeper traversal.\n\n- Matching a small fixed string set with `matches!` before semantic checks.\n- Rejecting lowercase identifiers before global-object checks when only constructors can match.\n- Checking whether a JSX attribute starts with `aria-` before lowercasing it.\n- Checking for required syntax such as a `key` prop before looking up callback parameter symbols.\n- Checking `source_range(span).contains(\"this\")` before running a visitor that only finds `this`.\n\n### Delay expensive context\n\nMost files do not contain lint errors. Do not prepare diagnostics, labels, help text, fix data, ancestors, symbols, JSX element types, or replacement strings until the rule has found a syntactic candidate that could actually report.\n\n### Iterate over the smallest set possible\n\n- Use `run_once` when the rule only needs a whole-file pass and does not need to run on every node.\n- Iterate over symbols instead of AST nodes when looking for references to specific names.\n- Prefer targeted lists or semantic data over broad AST traversal when available.\n- For name-based binding checks, use `ctx.scoping().get_binding(scope_id, name)` instead of scanning every binding in `get_bindings(scope_id)`.\n- For global or unresolved identifier checks, start from `ctx.scoping().root_unresolved_references().get(name)` for the small set of relevant names instead of visiting every `IdentifierReference`.\n- When iterating unresolved references, still verify the reference is the right kind: skip references with a symbol, type-only references, and nodes whose `AstKind` is not the expected identifier or member access.\n- When checking imported specifiers or exported names, iterate the concrete specifiers or precomputed export set rather than scanning all root bindings for every item.\n\nUse precomputed `FxHashSet`s only when many symbols need the same membership test. Prefer keyed semantic lookup when each lookup already has an exact name.\n\n### Avoid unnecessary regular expressions\n\nAvoid regular expressions when byte or string checks are enough: `contains`, `starts_with`, `ends_with`, or matching a small fixed set.\n\nFor hot comment or string scanning paths, prefer a cheap `memchr` or byte search to reject most inputs, then parse only candidates. Preserve regex semantics when replacing one, especially identifier boundaries, optional prefixes, and multiline whitespace.\n\n### Avoid heap allocations\n\n- Use copy-on-write utilities when a value usually does not need to change.\n- Avoid intermediate `Vec`s and `String`s when iteration or borrowed data is enough.\n- Keep temporary data on the stack when practical.\n- Delay allocation until a diagnostic, fix, or transformed value is actually needed.\n- Use allocation-free ASCII comparisons such as `starts_with_ignore_case` before calling `cow_to_ascii_lowercase`.\n- Use byte scans such as `as_bytes().array_windows()` for simple ASCII patterns like escape sequences.\n- Reserve hash maps or sets when the final size is known.\n- Avoid building a `HashSet` just to check names that can be looked up directly in scoping data.\n",".github/copilot-instructions.md":"# Copilot Review Instructions\n\nWhen reviewing pull requests in this repository:\n\n- Do not guess or infer compiler errors. Only report them when supported by actual compiler diagnostics.\n- Treat files inside any `fixtures` directories as test data, not production code.\n- Fixture files may intentionally contain buggy, unsafe, or syntactically invalid code to validate parser/linter/transformer behavior.\n- Do not report normal code-quality or correctness issues for fixture files.\n- You may report a fixture issue only when the test data appears incorrect or mislabeled:\n  - The folder or filename does not match what the test claims to cover.\n  - The fixture content does not actually test the described case.\n  - The fixture appears accidentally broken rather than intentionally crafted.\n","npm/oxlint-plugins/README.md":"# @oxlint/plugins\n\nPlugin utilities for [Oxlint](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n\nThis package provides optional functions to assist in creating Oxlint JS plugins and rules.\n\n## Installation\n\n```bash\nnpm install @oxlint/plugins\n```\n\n## Usage\n\n### Define functions\n\nUse `definePlugin` and `defineRule` if authoring your plugin in TypeScript for type safety.\n\n```typescript\nimport { definePlugin, defineRule } from \"@oxlint/plugins\";\n\nconst rule = defineRule({\n  create(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n});\n\nexport default definePlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n### Types\n\nThis package also includes types for plugins and rules.\n\n```typescript\nimport type { Context, Rule, ESTree } from \"@oxlint/plugins\";\n\nconst rule: Rule = {\n  create(context: Context) {\n    return {\n      Program(node: ESTree.Program) {\n        // Rule logic here\n      },\n    };\n  },\n};\n```\n\n### ESLint compatibility\n\nIf your plugin uses Oxlint's [alternative `createOnce` API](https://oxc.rs/docs/guide/usage/linter/js-plugins#alternative-api),\nuse `eslintCompatPlugin` to convert the plugin so it will also work with ESLint.\n\n```typescript\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nconst rule = {\n  createOnce(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n};\n\nexport default eslintCompatPlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n## Node.js version\n\nThis package requires Node.js 12.22.0+, 14.17.0+, 16.0.0+, or later.\nThis matches the minimum Node.js version required by ESLint 8.\n\nThis package provides both ESM and CommonJS entry points.\n\nSo a plugin which depends on `@oxlint/plugins` can be:\n\n- Used with any version of Oxlint.\n- Used with ESLint 8+.\n- Published as either ESM or CommonJS.\n\n## Docs\n\nFor full documentation, see [Oxlint JS Plugins docs](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n","packages/codegen/CHANGELOG.md":"# Changelog\n\nAll notable changes to this package will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0).\n\n## [0.155.0] - 2026-10-19\n\n### ⚡ Performance\n\n- 2ab6b63 packages/codegen: Remove redundant `printSpaceBeforeIdentifier` and `last` updates in `printParams` (#27652) (overlookmotel)\n- e533278 codegen: Reduce writes in `printDecorators` (#27651) (overlookmotel)\n\n### 📚 Documentation\n\n- 3de03c1 packages/codegen: Correct outdated comments (#27838) (overlookmotel)\n\n## [0.154.0] - 2026-10-13\n\n### 🐛 Bug Fixes\n\n- 760b60b codegen, packages/codegen: Remove extraneous space before `RestElement` in `ArrayPattern` (#27613) (overlookmotel)\n- 0353d37 packages/codegen: Support printing `V8IntrinsicExpression`s (#27597) (overlookmotel)\n- f50257e codegen: Avoid extra indent before empty do-while bodies (#27407) (camc314)\n- 5461c1a packages/codegen: Fix printing negative `BigIntLiteral` after `-` (#27377) (overlookmotel)\n- bebef88 packages/codegen: Fix debug assert fail printing `TSJSDocNonNullableType` (#27373) (overlookmotel)\n\n### ⚡ Performance\n\n- e1c97f8 packages/codegen: Remove all `printSpaceBeforeIdentifier` calls (#27620) (overlookmotel)\n- 70ecfdd packages/codegen: Skip redundant `printSpaceBeforeIdentifier` call (#27619) (overlookmotel)\n- 3c289f3 packages/codegen: Remove branches from loops printing arrays (#27614) (overlookmotel)\n- 27f5b45 packages/codegen: Remove branch printing `RestElement` in `ArrayPattern` (#27608) (overlookmotel)\n- adc90ed packages/codegen: Reduce branches in `printArguments` (#27607) (overlookmotel)\n- 262dd21 packages/codegen: Remove unnecessary `printSpaceBeforeIdentifier` calls (#27606) (overlookmotel)\n- 4bb9722 packages/codegen: Print `SpreadElement` arguments via `printExpression` (#27599) (overlookmotel)\n- 0c98dc5 packages/codegen: Reduce megamorphic `type` reads in `printParams` (#27595) (overlookmotel)\n- e3780b5 packages/codegen: Reduce writes in `printForOfStatement` (#27594) (overlookmotel)\n- 8d0ba20 packages/codegen: Reduce megamorphic `type` reads in `printForOfStatement` (#27593) (overlookmotel)\n- b29f35e packages/codegen: Reduce megamorphic `type` reads in `printDirectivesAndStatements` (#27592) (overlookmotel)\n- 256259d packages/codegen: Reduce megamorphic `type` reads in `printConditionalExpression` (#27591) (overlookmotel)\n- a9bd942 packages/codegen: Reduce megamorphic `type` reads in `printObjectProperty` (#27590) (overlookmotel)\n- 0a386ff packages/codegen: Ensure `shorthand` var is always a boolean (#27589) (overlookmotel)\n- 8d4c633 packages/codegen: Reduce branches in `printObjectProperty` (#27588) (overlookmotel)\n- b4c8c04 packages/codegen: Reduce megamorphic `type` reads in `printMemberExpression` (#27587) (overlookmotel)\n- ff5eae8 packages/codegen: Reduce megamorphic `type` reads (#27586) (overlookmotel)\n- 961f318 packages/codegen: Reduce megamorphic `type` reads in `binCheckAndPrepare` (#27460) (overlookmotel)\n- 3d2f93f packages/codegen: Reduce megamorphic property accesses for `BinaryExpression`s (#27459) (overlookmotel)\n- 675ec5e packages/codegen: Reduce megamorphic property accesses in `printBinaryish` (#27458) (overlookmotel)\n- b0dbfff packages/codegen: Reduce writes and branches printing functions (#27455) (overlookmotel)\n- 5f3865c packages/codegen: Reduce writes for optional `MemberExpression`s (#27454) (overlookmotel)\n\n### 📚 Documentation\n\n- 59472c5 packages/codegen: Add `@param`s to JSDoc comments which had none (#27601) (overlookmotel)\n- 5ee0fc9 packages/codegen: Complete `@param` lists in partly documented JSDoc comments (#27600) (overlookmotel)\n- 12dfa25 packages/codegen: Correct doc comment for `printBinaryish` (#27456) (overlookmotel)\n\n## [0.151.0] - 2026-09-21\n\n### 🚀 Features\n\n- 1fac655 coverage: Support new TypeScript fixture layout (#26226) (camc314)\n\n## [0.150.0] - 2026-09-14\n\n### 🐛 Bug Fixes\n\n- 32d00c5 codegen: Preserve instantiation expression precedence (#26424) (camc314)\n- 5986187 packages/codegen: Preserve private-in right operand precedence (#26420) (camc314)\n- f8e6c6c packages/codegen: Preserve in restriction through yield arguments (#26421) (camc314)\n- 6e15ad5 packages/codegen: Print matching quoted import names as identifiers (#26404) (camc314)\n- bbbb4bc packages/codegen: Preserve private-in left operand precedence (#26403) (camc314)\n- a111b5b packages/codegen: Print accessibility modifiers before abstract (#26402) (camc314)\n\n## [0.148.0] - 2026-08-31\n\n### ⚡ Performance\n\n- 07a0793 packages/codegen: Flatten output in chunks (#26109) (overlookmotel)\n- 487427a packages/codegen: Faster string flattening (#26108) (overlookmotel)\n- 7785583 packages/codegen: Ensure indent strings are flattened (#26107) (overlookmotel)\n- 0dd4db3 packages/codegen: Store mapping positions in an `Int32Array` (#26085) (overlookmotel)\n\n## [0.147.0] - 2026-08-24\n\n### 🐛 Bug Fixes\n\n- 6c5ad1b codegen: Only add mapping names for `PrivateIdentifier`s which have changed (#25958) (overlookmotel)\n- a185ac3 codegen: Print `#` before private identifier in TS signature key (#25938) (overlookmotel)\n\n### ⚡ Performance\n\n- 53f6270 packages/codegen: Eagerly initialize `mapNames` (#25979) (overlookmotel)\n- 5b43c60 packages/codegen: Split recording mappings into multiple functions (#25978) (overlookmotel)\n- cc947b5 packages/codegen: Reduce property lookups (#25977) (overlookmotel)\n- c22a9cc packages/codegen: Combine condition checks (#25976) (overlookmotel)\n- 215a6a5 packages/codegen: Add functions for writing/marking unnamed mappings (#25975) (overlookmotel)\n- 1004439 packages/codegen: Merge `mark` and `write` calls in `printImportAttributes` (#25972) (overlookmotel)\n- 94c8a34 packages/codegen: Remove `node` param from `printString` and `printNonNegativeFloat` (#25971) (overlookmotel)\n- 7e2b4b3 packages/codegen: `printNonNegativeFloat` write mapping directly (#25968) (overlookmotel)\n- 53ff080 packages/codegen: Remove `lastWasPostfixClose` (#25888) (overlookmotel)\n\n### 📚 Documentation\n\n- 07de646 packages/codegen: Fix JSDoc comments on `write*` methods (#25966) (overlookmotel)\n- 3b09c04 packages/codegen: Clarify comment (#25959) (overlookmotel)\n\n## [0.146.0] - 2026-08-19\n\n### 🐛 Bug Fixes\n\n- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)\n- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)\n\n### ⚡ Performance\n\n- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)\n- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)\n- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)\n- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)\n- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)\n- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)\n- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)\n\n### 📚 Documentation\n\n- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)\n\n## [0.145.0] - 2026-08-18\n\n### 💥 BREAKING CHANGES\n\n- 365274e packages/codegen: [**BREAKING**] `printSync` return an object (#25720) (overlookmotel)\n\n### 🚀 Features\n\n- 6a7eb60 packages/codegen: Alter capitalization of `sourceFilename` option (#25854) (overlookmotel)\n- 300763f packages/codegen: Add full source map support (#25585) (camc314)\n- a4478e9 codegen: Add `oxc-codegen` package (#25488) (overlookmotel)\n\n### 🐛 Bug Fixes\n\n- 0fbcf64 codegen: Validate sourcemap options (#25860) (camc314)\n- 4cc7ea4 codegen: Reject invalid indent options (#25807) (camc314)\n- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582) (camc314)\n- 59e8895 codegen: Validate starting indent level (#25550) (camc314)\n\n### ⚡ Performance\n\n- 5444cbf codegen: Skip escaping harmless `<` tokens (#25564) (camc314)\n\n### 📚 Documentation\n\n- 58f7ab9 packages/codegen: Reformat docs and comments (#25848) (overlookmotel)\n- ce03ac1 packages/codegen: Fix JSDoc comments for `printSync` (#25722) (overlookmotel)\n- 386a699 packages/codegen: Correct JSDoc comment (#25716) (overlookmotel)\n- fd62354 codegen: Revamp package readme (#25560) (camc314)\n- ffa3153 codegen: Clarify raw transfer Node requirement (#25565) (camc314)\n- c5d4063 codegen: Clarify binary walk allocations (#25562) (camc314)\n- 5703d7a codegen: Correct stale printer comments (#25561) (camc314)\n\n","crates/oxc_transformer_plugins/README.md":"# Oxc Transformer Plugins\n\nSpecialized transformation plugins for common JavaScript and TypeScript patterns.\n\n## Overview\n\nThis crate provides specific transformation plugins that can be used independently or as part of the broader oxc transformation pipeline. These plugins handle common transformation needs like global variable injection and module format conversion.\n\n## Key Features\n\n- **Global variable injection**: Add global variables to modules\n- **Module runner transforms**: Transform modules for different execution environments\n- **Global defines replacement**: Replace compile-time constants\n- **Composable plugins**: Use individually or combine multiple plugins\n\n## Architecture\n\n### Plugin System\n\nEach plugin implements the transformation logic for a specific use case:\n\n- **Focused responsibility**: Each plugin handles one type of transformation\n- **Traverse integration**: Uses oxc's traversal system for efficient AST walking\n- **Composable**: Multiple plugins can be applied in sequence\n- **Context-aware**: Access to semantic information when needed\n\n### Common Use Cases\n\n#### Build-time Optimizations\n\n- **Dead code elimination**: Remove unreachable code paths\n- **Constant folding**: Replace compile-time constants\n- **Environment variables**: Inject environment-specific values\n\n#### Module System Compatibility\n\n- **Polyfill injection**: Add necessary polyfills for target environments\n- **Global shimming**: Provide Node.js globals in browser environments\n- **Format conversion**: Transform between module formats\n\n#### Development Tools\n\n- **Debug injection**: Add development-time debugging code\n- **Hot module replacement**: Support for HMR systems\n- **Testing utilities**: Inject test-specific globals\n\n### Integration Points\n\nThese plugins integrate with:\n\n- **oxc_transformer**: Core transformation infrastructure\n- **oxc_traverse**: AST traversal and mutation\n- **Build tools**: Webpack, Vite, Rollup, and other bundlers\n- **CLI tools**: Direct usage in command-line transformations\n\nThe plugin architecture enables extensible, composable transformations while maintaining high performance through oxc's efficient traversal system.\n","packages/codegen/DESIGN.md":"# Design\n\nWhy `oxc-codegen` is shaped the way it is.\n\nIt is a port of Oxc's Rust [`oxc_codegen`] crate, and its output has to be byte-identical to it.\nBut it is not a transliteration. A straight port would not take into account differences between Rust and JS,\nand would likely be several times slower.\n\nThis document is for someone about to change the printer, wondering which of its oddities are load bearing.\nEverything here is also documented at the point it applies. This is an overview.\n\n## Contents\n\n- [Why not native?](#why-not-native)\n- [What this is](#what-this-is)\n- [What it is not at present](#what-it-is-not-at-present)\n- [`state.last`](#statelast)\n- [Four builds from one source tree](#four-builds-from-one-source-tree)\n- [Other things which make it fast](#other-things-which-make-it-fast)\n- [Where the ESTree AST differs](#where-the-estree-ast-differs)\n- [How it is tested](#how-it-is-tested)\n- [Rules to keep](#rules-to-keep)\n\n## Why not native?\n\nOxc's other JS packages are bindings around a native library. This one deliberately is not.\n\nThe starting point was a hunch:\n\n> **When the AST is already in JS, no native printer can beat a JS one.**\n\n### The argument\n\n- **Printing is cheap.** It is a linear walk of the AST, emitting strings. There is no parsing, no analysis,\n  no search. It doesn't play to the strengths of a faster language the way parsing or minifying does.\n- **Crossing into native is not cheap.** A native printer cannot see a JS object graph.\n  The whole AST has to be serialized across the boundary before it can print a single character.\n- **The boundary dominates.** The serialization is proportional to the size of the AST, just as the printing is -\n  but it is work a JS printer can skip entirely. It is added to a total that was small to begin with.\n\nThe conclusion: A native printer loses the game before it's even started - still shovelling data over to native side,\nwhile the JS printer already finished work and has gone off to the pub.\n\n### Where this applies\n\nOnly when the AST is **already** in JS - it came from `oxc-parser`, or a plugin built it, or something\ntransformed it on JS side. That is the common use case this package is for.\n\nIt does **not** apply when you are printing source text you also parsed natively. Then stay on native side -\nthe AST never has to leave native memory, and Rust `oxc_codegen` is the right tool. The two are complementary.\n\nThe benchmarks in the [README](./README.md) measure `oxc-codegen` over representative JS-held ASTs.\n\n## What this is\n\nA printer from an ESTree AST to source text, written in TypeScript.\n\n### The output contract\n\nByte-identical to `oxc_codegen` with default options - tab indentation, double quotes, comments off.\n\nThe conformance suites check this on every fixture. It is what makes the two printers substitutable for one another.\n\n## What it is not at present\n\n`oxc_codegen` (Rust printer) is more capable than this one.\n\n- No minify mode.\n- No support for comments.\n- No symbol mangling.\n\nMany of these features can be added in future, and we should be able to do so without _much_ impact on performance.\n\n## `state.last`\n\nThis is the largest divergence from Rust, and most of the others follow from it.\n\n### The problem\n\nTwo adjacent tokens can merge into a different token when written straight up against each other.\n`in` after an identifier becomes part of that identifier. `+` after `+` becomes `++`.\n\nDeciding whether to insert a space means knowing what was written last.\n\n### How Rust answers it\n\nRust just looks. Its output buffer is a `Vec<u8>`, so `code.last_byte()` is a pointer dereference - very cheap.\n\n### Why this doesn't work in JS\n\n**In JS, reading the last character is catastrophic for perf.** `state.output` is built by repeated `+=`,\nwhich V8 represents as a **cons string** - a tree of the pieces, never actually concatenated. Reading any\ncharacter of it forces V8 to flatten that tree.\n\nDo that after every append and the printer spends its time flattening ropes it is about to extend again.\nResult is a **4x slow-down**.\n\nSo `oxc-codegen` has to find a different solution. Every time it pushes a string to output, it also records\nin `state.last` what the output now ends with. `state.output` never needs to be read from.\n\nThe rope is flattened once, at the end. Whatever the caller does with the returned code will flatten it anyway\n(and `generateSourceMap` scans it), so `printSync` pre-flattens with the fastest method available.\nA large output is flattened in chunks along the way rather than all at once at the end -\nsee `print/flatten.ts` for the two reasons.\n\n### The answer: A category, not a character\n\nIt turns out the functions which decide whether spaces need to be inserted between keywords / identifiers\ndon't actually need to know what the last character was, they only need to know what _category_ of character\nit was. i.e. It doesn't matter if the last character was `\"a\"` or `\"Z\"` or `\"$\"` - they're all equivalent.\n\nAlmost everywhere in the printer, it knows statically what _category_ it's writing, even if it doesn't know\nthe exact bytes until runtime. e.g. When writing an identifier, it's the `IDENT` category.\n\n```ts\nwrite(state, node.name, CAT_IDENT);\n```\n\nThis has 3 advantages over storing the last character:\n\n1. Categories are just small integers, the cheapest data type there is.\n2. The categories are statically defined at call sites. No `node.name.at(-1)`.\n3. Checking the category of last write is cheap - `last === CAT_IDENT` not `isIdentifierPart(lastChar)`.\n\nThat one `last` field replaces all of this:\n\n| Rust                                  | Question it answers                 | JS                      |\n| :------------------------------------ | :---------------------------------- | :---------------------- |\n| `last_byte()` + `is_identifier_part`  | Would a following identifier merge? | `CAT_IDENT`             |\n| `last_byte() == Some(b'?')`           | Would a following `?` make `??`?    | `CAT_QUESTION`          |\n| `peek_nth_byte_back(1) == Some(b'<')` | Is this `!` the `!` of a `<!`?      | `CAT_OP_LT_THEN_UN_NOT` |\n\n### Extending this scheme to operators\n\nRust `oxc_codegen` also holds state about what operator was last written. It stores this info as a pair:\n\n- `prev_op` - Last operator that was written\n- `prev_op_end` - Position of the end of that operator\n\nEach time an operator is written, it sets these 2 fields. When other code wants to know \"am I writing this straight\nafter an operator, and which operator?\" it:\n\n1. Checks if `code.len() == prev_op_end`. If true, then yes, it's right after an operator.\n2. `prev_op` then says which operator.\n\nIn Rust printer, this makes sense. Checking these 2 fields, and comparing to `code.len()` costs 2 or 3 memory accesses,\nand 1 or 2 comparisons. But that's fine! It only reads or writes these 2 fields infrequently - only when an operator\nis being written. And operators are only a small fraction of code.\n\nIn JS codegen, we're in a different situation. We've been forced to update `state.last` after every write,\nto avoid the huge perf hit of reading the last character from `output`.\n\nSo we might as well use `last` to record operators too, and also some similar character classes:\n\n| Rust                                    | Question it answers                                     | JS                |\n| :-------------------------------------- | :------------------------------------------------------ | :---------------- |\n| `code.len() == prev_op_end` + `prev_op` | Which operator came last, and was it right before this? | `CAT_OP_*`        |\n| `code.len() == need_space_before_dot`   | Is space needed after digit? e.g. `0 .toExponential()`  | `CAT_INT_DIGIT`   |\n| `code.len() == prev_reg_exp_end`        | Did a flagless regex just close?                        | `CAT_REGEX_SLASH` |\n\nAll these Rust state fields, and every read of the output buffer, collapse into one field in the JS printer.\n\nIt costs no write barrier to store (because categories are represented by \"SMI\" small integers), and one compare\nto test. Being touched by every single write, it is the hottest field in `State`, reliably in L1 cache,\nand probably often also benefits from fast store-to-load forwarding.\n\n#### The `<!--` case, as an example\n\nRust decides whether a `!` is the `!` of a `<!--` hazard by peeking at the _second_-to-last byte,\nat the moment a `--` is about to be written.\n\nHere the question is answered when the `!` is written, where the preceding character is already known,\nand the answer is baked into which category gets stored. The reader has nothing left to look up.\n\n### Position marks\n\nSimilar to operators, Rust printer also maintains state about the _context_. For example:\n\n```js\n// Function declaration\nexport default function foo () {}\n// Function expression\nexport default (function bar () {});\n```\n\nThese 2 are semantically different - `foo` is available in outer scope, `bar` is not.\n\nWhen printing a `FunctionExpression`, print needs to know \"am I the value of an `export default`?\"\nto know whether it needs to wrap the function in parentheses.\n\nSimilar to operators, Rust printer stores this info in field `start_of_default_export`.\nWhen `code.len() == start_of_default_export` then, yes, what's currently being printed is the value of\nan `export default` statement.\n\nAgain, JS codegen uses `last` to store this info. Except here `last` doesn't describe what _was_ written,\nit says what the context is.\n\nNote that the node which reads the mark need not be the direct child of the `export default`.\nIn `export default (function bar() {})();` the function is the callee of a call expression, and the parens\nare still needed. It gets them because `printCallExpression` wrote nothing before descending into the callee,\nso the mark was still there when `printFunction` looked. Whatever is printed leftmost sees the mark,\nhowever deeply nested it is.\n\n| Rust                                    | Question it answers                   | JS                            |\n| :-------------------------------------- | :------------------------------------ | :---------------------------- |\n| `code.len() == start_of_stmt`           | At the start of a statement?          | `CAT_START_OF_STMT`           |\n| `code.len() == start_of_arrow_expr`     | At the start of a concise arrow body? | `CAT_START_OF_ARROW_EXPR`     |\n| `code.len() == start_of_default_export` | At the start of an `export default`?  | `CAT_START_OF_DEFAULT_EXPORT` |\n\nThe reason this works is that in every case the last character printed in these positions was a space, a line break,\nor the start of the file, which all fall into the `CAT_OTHER` category. So these values of `last` aren't ambiguous\nto any other code which judges what to do next based on what came last. They just treat these values as\n\"last character written was something I don't need to do anything special about\".\n\n#### Why the substitution is safe\n\nTwo facts, both checked across every printer file:\n\n1. **Nothing writes output on the way down from a mark to its reader.** Every path from a mark to a\n   reader either writes nothing, or writes a real `(` - never a `writeNoLast`, which would leave the\n   mark visible past output which should have killed it.\n2. **It can only ever add parentheses, never remove them.** A real `write` always grows the output\n   (empty writes are illegal), so the set of things which kill a mark under categories is a strict\n   subset of what killed the offsets.\n\nSo every possible failure is a spurious pair of parentheses - slightly longer output, but not _invalid_ output.\nThe byte-for-byte conformance tests would catch this, but even if there are gaps in the conformance suite\nfor crazy edge cases and a bug sneaks through, it can't produce invalid JavaScript.\n\n### Why category numbering is load bearing\n\nThe full table is at the top of [`print/categories.ts`] and is the authority.\n\nThree properties are relied on. Adding a code without preserving them will silently space output wrongly.\n\n1. **Identifier hazards are the lowest codes.** So `printSpaceBeforeIdentifier` is `last <= CAT_REGEX_SLASH` -\n   one compare, no table, no branch tree. The operators `printSpaceBeforeOperator` must distinguish are the highest,\n   for the same reason (`last >= CAT_OP_LT_THEN_UN_NOT`).\n2. **The `CAT_START_OF_*` codes sit between those two ranges**, which is what makes both range checks\n   treat them as \"nothing to separate\".\n3. **`CAT_START_OF_STMT` is odd, with the other two marks either side.** The five reader sites each ask\n   about a _pair_ of marks, never all three, so both pairs fall out as one `|`-and-compare:\n\n   ```\n   (last | 1) === CAT_START_OF_STMT          // Statement or `export default`\n   ((last - 1) | 1) === CAT_START_OF_STMT    // Statement or concise arrow body\n   ```\n\nEach property has an `if (DEBUG)` block which iterates every category and asserts the compare selects\nexactly the intended set and nothing else - in [`print/categories.ts`] for the `|` identities, and in [`print/space.ts`]\nfor the two range checks.\n\nRenumber freely. The assertions will tell you what you broke, and cost release builds nothing.\n\n**One deliberate exception**: `CAT_OP_UN_NOT` is an operator code but sits _below_ the operator range.\nNo operator merges with a plain `!`, so storing it must not cost `printSpaceBeforeOperator` a call into its slow path.\n\n### `writeNoLast`\n\nNot every write needs to update `last`.\n\nWhen one token is written in pieces - a string's opening quote, its contents, its closing quote -\nonly the last piece's category can possibly be read. Hence eleven write primitives in [`print/write.ts`]:\n\n| Function                     | Updates `last` | Records a source mapping       |\n| :--------------------------- | :------------- | :----------------------------- |\n| `write`                      | Yes            | No                             |\n| `writeIdent`                 | Yes            | No                             |\n| `writePrivate`               | Yes            | No                             |\n| `writeWithMap`               | Yes            | At the node's start            |\n| `writeWithMapNamed`          | Yes            | At the node's start, with name |\n| `writeWithMapNamedPrivate`   | Yes            | At the node's start, with name |\n| `writeWithMapEnd`            | Yes            | At the node's last character   |\n| `writeNoLast`                | No             | No                             |\n| `writeWithMapNoLast`         | No             | At the node's start            |\n| `writeWithMapNamedNoLast`    | No             | At the node's start, with name |\n| `writeWithMapNamedJSXNoLast` | No             | At the node's start, with name |\n\n- Every `writeWithMap*` and `markMap*` function takes the node's `start` and `end` offsets, extracted by the caller.\n  The node itself is still passed, last, but only debug asserts read it - which is what checks the offsets\n  against the node they claim to describe. The point is where the read happens - in a caller the node's type is known,\n  so `node.start` is a monomorphic load, whereas inside the write functions the same read is megamorphic,\n  because the printer reaches them from every node type there is. Release builds have the parameter and every argument\n  removed by the TSDown plugin.\n- The `*Named` functions record the name the node had in the source, and take a `NamedMappableNode`,\n  which covers nodes with a string `name`. What they record is the text they print -\n  debug builds assert the two are the same string.\n- The other `writeWithMap*` functions take an `UnnamedMappableNode` which covers nodes without a string `name`.\n- There is a `*Named` function per way of recovering the original name from the source, because the scan differs -\n  a JSX identifier may hold a `-`, and a private identifier's span covers a `#` which is not part of its name.\n  Which one applies is settled at the call site, where the node's type is known statically, rather than read back\n  off a `node` which is megamorphic by the time it arrives.\n- `writeIdent` is `write` with the category fixed at `CAT_IDENT` - an identifier, a keyword, or the trailing digits\n  of a number. It is the commonest category by far, so every call which passed it was spending an argument\n  on a value which could never be anything else.\n- `writeWithMapNamed` and `writeWithMapNamedPrivate` take no category for the same reason, which is why their\n  plain forms are `writeIdent` and `writePrivate` rather than `write`.\n- The two `Private` forms write the `#` themselves, so the name they are given is what follows it,\n  and `last` is always `CAT_IDENT` - the caller passes neither.\n- The three `markMap*` functions record a mapping without writing anything, so `last` is not theirs to update.\n- The rule is exact: **only sound where the value of `last` is provably dead**.\n  Another real write must follow before anything reads it.\n- JSX carries the longest runs. Printing `<a.b x=\"1\">hi</a.b>` updates `last` exactly once, on the final `>`.\n- `writeNoLast` accepts an empty string, `write` does not.\n  A `write` of `\"\"` would leave `last` claiming a category for a character that was never written.\n\n### How the invariants are enforced\n\nEvery rule above fails quietly - a missing space in one construct out of thousands. So three debug-only\nmechanisms guard them, all removed from release builds.\n\n1. **`debugAssertCategoryMatches`** runs on every `write`, checking the category truthfully describes\n   the final character written. It cannot be a simple lookup - a category encodes the merge hazard of\n   the trailing _token_, not the trailing character - a digit legitimately ends both a `CAT_IDENT` write\n   and a `CAT_INT_DIGIT` one - so each final character has a permitted set.\n2. **`lastIsStale` / `debugAssertLastFresh`** track the `writeNoLast` rule. `writeNoLast` sets the flag,\n   the next real write clears it, and every reader of `last` calls `debugAssertLastFresh` first,\n   to make sure they're not reading a stale value.\n   An author who was wrong about `last` being dead gets a clear failure, not a wrongly spaced construct.\n3. **The numbering assertions** described above.\n\nThis is why the debug build is the one to run the conformance suites against - `pnpm run build-test`.\n\n## Four builds from one source tree\n\n`dist` holds the printer compiled four times, plus an entry point which picks between them.\n\n| Build              | `TS`  | `SOURCEMAPS` |  Size |\n| :----------------- | :---- | :----------- | ----: |\n| `print_js.js`      | false | false        | 25 KB |\n| `print_js_maps.js` | false | true         | 26 KB |\n| `print_ts.js`      | true  | false        | 39 KB |\n| `print_ts_maps.js` | true  | true         | 40 KB |\n\n`index.ts` picks one from the caller's `ts` and `sourcemap` options and `require`s it on first use -\n`require` rather than `import()`, because `printSync` is synchronous.\n\nA caller printing only JavaScript never loads, parses or compiles the TypeScript printers at all.\n\n### Why separate TS and JS builds\n\n**The obvious reason is code size and complexity.** JS-only builds have all TS-related code removed,\nas it's all gated behind compile-time `TS` constant. Every `if (TS && node.declare) { ... }` block is removed\nby minifier, and every `case \"TSEnumDeclaration\":` arm by the `strip_ts` TSDown plugin. With the arms gone,\nthe TS-only printer functions lose their last references and are tree-shaken away. That is a third of the code.\nHowever, it has a surprisingly small impact on perf.\n\n**The bigger reason is monomorphism.** `oxc-parser` produces differently-_shaped_ node objects for a\nJS-shaped AST and a TS-shaped one - the TS shape carries `optional`, `declare`, `accessibility`,\n`override` and the rest.\n\nIf both shapes flow through one `printFunction`, V8 sees two hidden classes at every property access,\nthe inline caches go polymorphic, and the whole printer slows down.\n\nTwo builds means each one only ever sees one shape. JS printer sees only JS AST-shaped objects,\nTS printer sees only TS AST-shaped objects. This gives a perf boost of over 10%.\n\nAdditionally, the split helps branch prediction. If feeding a mix of JS and TS ASTs through a single printer,\nfor a JS AST branches for \"does this node have a type annotation?\" always find the answer is \"no\".\nBranch predictor believes there's a pattern and when that code runs again, it expects no type annotation again.\nBut this time, the AST is a TS AST, and the answer is \"yes\". Mispredict! Go back 100 instructions, go directly back,\ndo not pass Go, do not collect $200!\n\nWith 2 different builds for JS and TS, this doesn't arise. In the JS build the branch isn't there at all -\nit was compiled away. The TS build only ever sees TS ASTs, so what the branch predictor learns stays true.\n\n### Why `State` lives outside `print` directory\n\nThe same argument, one level up.\n\n`State` is defined in `src-js/state.ts`, which is **not** part of any printer build. The printers import\nit as a type only; the entry point constructs it. So all four builds share one class, and therefore one\nhidden class, for the object they thread through every single function.\n\nThis is why **a build-time flag must never add or remove a field on `State`**.\n\n- The source-map fields are present in every build, and set to `null` when unused.\n- The debug fields are present in every build, and only _initialised_ under `if (DEBUG)`.\n\nMaking either conditional would give each build a different object shape, undoing the whole arrangement.\n\n### The build plugins\n\nThere are 4 TSDown plugins in `tsdown_plugins`. Order matters - `strip_ts` is a text transform,\nso it must run before the plugins which parse.\n\n#### `strip_ts`\n\nDeletes regions fenced with `/* IF TS */` ... `/* END_IF */` from JS-only builds.\n\nThey exist because a minifier can fold `if (TS)` away, but cannot remove a `switch` `case` arm on the grounds\nthat its node type never occurs.\n\nIt fails the build if the fences are unbalanced - and also if it finds _no_ fences at all.\nA plugin silently doing nothing is a failure mode worth guarding against.\n\n#### `unmap_writes`\n\nRuns in two modes, one per build flavour (sourcemaps / no sourcemaps), in both cases stripping arguments\nnothing in that build reads.\n\n**Without source maps**, rewrites every mapped write into the plain one it becomes with no mapping to record -\nso `writeWithMap(state, code, cat, node.start, node.end, node)` turns into `write(state, code, cat)` -\nand rewrites the import to match.\n`writeWithMapEnd` becomes `write` too, and every `NoLast` form becomes `writeNoLast`.\n`writeWithMapNamed` and `writeWithMapNamedPrivate` become `writeIdent` and `writePrivate`, which fix the category\ninstead of taking it. `writePrivate` exists only for the rewrite to land on.\n\n`printString` and `printNonNegativeFloat` take the offsets and the node only to hand them on to a mapped write.\nThey keep their names, but the arguments come off every call, and the parameters off their declarations -\nwhich, unlike the mapped writes, survive into the build.\n\n**With source maps**, in release builds only, just the trailing `node` comes off - from every call,\nand from every declaration. The mappings are real in these builds so the offsets stay, but `node` isn't read\nexcept by debug asserts. Debug builds get neither mode - the `node` parameter stays for the debug asserts to read.\n\nWithout it, the offsets would still be read and held live across a call which ignores them.\n\nThe three `markMap*` calls write nothing, so they have no plain form to become, and keep their names -\nthe arguments come off, and a body which opens `if (!SOURCEMAPS) return` lets the minifier remove what is left.\n\nIt fails the build if a mapped write is called with the wrong number of arguments, if a declaration it transforms\nhas the wrong number of parameters, or if a call it rewrote had no matching import to rewrite.\n\n#### `const_functions`\n\nRewrites top-level `function foo(...) { ... }` into `const foo = (...) => { ... }`.\n\nA function declaration creates a binding which could be assigned later, so every call has to read\nthe current value out of module scope. A `const` cannot be, so V8 calls it directly.\n\n- **Measured at ~3%** across all benchmark fixtures.\n- It really is the `const`. The same rewrite with `let` measures ~4% _slower_ than this, and slower than\n  leaving the declarations alone.\n- The source keeps its declarations, because they hoist and read better. Which is why this is a build step\n  rather than a house style.\n\n#### `remove_asserts`\n\nRemoves `debugAssert` and `typeAssertIs` calls _and the expressions inside them_ from release builds.\n\nThe minifier can drop the calls on its own, but cannot prove `node.value` in `debugAssert(node.value !== null)`\nhas no getter, so it would leave `node.value !== null` behind as a bare expression statement.\n\nThis is what gives us Rust `debug_assert!`-style checks in tests and conformance, with zero cost in release build.\n\n## Other things which make it fast\n\n### Popularity-ordered dispatch\n\n`printExpression` (34 arms) and `printStatement` (33 arms) are megamorphic string switches.\n\nV8 lowers a switch on strings to what amounts to an `if / else if` chain, so arm order is execution order.\nBoth are ordered roughly by how often each node type occurs in real code - common variants like `Identifier`,\n`MemberExpression`, `CallExpression` first.\n\nThis sped up the printer by ~30%.\n\nIt also means **adding an arm near the top of either switch has a cost**, however rare the node type is.\n\nEven after ordering `case` arms, these \"dispatch\" functions remain a large part of runtime (~25%).\nMuch sweat and tokens were spilled trying to reduce it further:\n\n- Perfect hash table to convert `node.type` to an integer ID.\n- `switch`-ing on an ID to reduce the switch to a jump table.\n- Branching on first character to narrow down the range - `switch (node.type.charCodeAt(0)) { case 69: ... }`.\n- Many others...\n\nAll to no effect. Arm ordering worked, nothing else did.\n\nIf anyone else can crack this, it's probably the single largest unrealized perf gain on the table.\n\n### Monomorphism\n\nAs much as possible, call paths are structured so that functions receive the same object shape consistently.\nOnce it has gathered type feedback from the first runs, V8 tiers up hot functions to TurboFan, which generates\ncode along these lines (pseudo-code):\n\n```ts\nfunction printThing(state, node): void {\n  if (SHAPE_OF(node) !== K_EXPECTED_SHAPE) {\n    DE_OPTIMIZE(printThing);\n    return printThing(state, node);\n  }\n\n  // Now it's known exactly what shape `node` is, it becomes static-typed like Rust.\n  // Working with it is very cheap.\n\n  // Original code: `const { left } = node;`\n  const left: Expression = GET_UNCHECKED(node, K_LEFT_OFFSET);\n  // ... fast code from here on ...\n}\n```\n\nIn particular, it is **essential** that `State` remains the same shape _everywhere_. Every function touches it,\nso any inconsistency in shape would turn every access polymorphic and completely tank performance.\n\nThe obvious exception is the dispatch functions discussed above (`printExpression` etc), which are\nunavoidably megamorphic.\n\nWe have not done a full audit of all functions to check if any are receiving polymorphic/megamorphic inputs\nwhen that could be avoided. Such an audit might throw up potential improvements.\n\n#### Moral of the story\n\nConsistent object shapes are _really_ important for good perf in JS engines.\n\nWe should provide users with utility functions to construct AST nodes which produce the exact same object shape\nas what `oxc-parser` produces. This would speed up not just `oxc-codegen`, but also their own code that\noperates on ASTs.\n\n```ts\n// Good\nconst binary = oxc.binaryExpression(left, operator, right);\n// Bad - different field order, different object shape\nconst binary = { left, right, operator };\n```\n\n### Intended parser pairing\n\nThe printer is meant to be used with `oxc-parser` with `experimentalRawTransfer` enabled.\n\nRaw transfer builds the AST with a JS deserializer rather than via JSON. Node `type` strings then come out\nas source literals, so those big `switch`es compare interned strings by pointer, rather than character by character.\nThis gives a 10% - 30% perf boost versus a JSON AST.\n\n### Fast paths\n\n- Pretty mode always puts a single space either side of a binary or assignment operator, so the whole\n  token is one constant string - `\" + \"`, `\" ??= \"` - and one `write`, instead of space-operator-space.\n  It also makes the token-glue checks unobservable around those operators, so they are not made at all.\n  See `PADDED_BIN_OPERATORS` in [`print/operators.ts`].\n- `printNonNegativeFloat` prints integers below 1000 straight from `String()`. They are the overwhelming\n  majority, and three digits are too few for any shortening form to win.\n- `printString` tests the whole string against one regex of characters needing escapes, and appends it\n  whole when there are none. The escaping loop is a separate function because it almost never runs.\n- `printSpaceBeforeOperator` is one compare, with its three-clause slow path split out so the common path\n  stays small enough for V8 to inline.\n- `printLiteral` switches on `typeof value` first, identifying strings and numbers without touching the\n  `regex` or `bigint` properties.\n- `printNumericLiteral` tests `value > 0 && value < Infinity` first, reaching the same branch the full\n  chain would without the two builtin calls.\n\nEach of these produced a small perf bump individually, ~10% gain in aggregate.\n\n### Indent cache\n\n`printIndent` reads a pre-built string for the current level out of an array.\n\n- The cache is created once per _process_, alongside the module-level state in `state.ts`.\n  Every `State` carries a reference to it. So indent strings one printer run has grown are there for the next.\n  This cache is shared between JS and TS, sourcemap and no-sourcemap builds.\n- `growIndents` deliberately forces each new indent string flat (a throwaway `charCodeAt(0)`) before caching it,\n  paying the flatten cost once rather than on every append.\n\n### Source maps computed at the end\n\nDuring printing, a mapped write records only an output offset and the node's original UTF-16 offset\nfrom `start` / `end`. Nodes without offsets are not mapped, and `sourceText` is required to convert\nsource offsets to line/column positions.\n\nThe offset pairs go into an `Int32Array` which, like the indent cache, is created once per process\nand reused by every print - each `State` tracks how much of it that print has filled, and the buffer\ndoubles when a print fills it, so a steady-state print allocates nothing to record its mappings.\n\n`generateSourceMap` then walks the output once at the end, counting ECMAScript line terminators,\nbuilds the equivalent line table for `sourceText`, turns both sets of offsets into line/column,\nand encodes the mappings as base64 VLQ. Tracking lines and columns throughout would cost every write\nin every build.\n\n#### Potential future improvement\n\nProducing source maps is exactly the kind of raw number-crunching that native code excels at.\nAnd, unlike the AST, it's just small integers - the kind of data that transfers across the JS-native\nboundary cheaply.\n\nThe offsets are already assembled on the JS side in an `Int32Array`, so it might be worthwhile making\na single JS-Rust-JS round-trip with it, with the sourcemap generation (VLQ encoding etc) happening on Rust side.\n\nWASM might also be a good option for this.\n\n### No separate stack in the binary walk\n\n`printBinaryish` walks the left spine of a binary/logical chain iteratively. `a + b + c + d` nests as deep\nas it is long, so recursion would put the stack at the mercy of the input.\n\nRust does the same, but with an explicit `Stack` on `Codegen`. Here each level has a small visitor\nobject, and pending outer levels are threaded through its `parent` field rather than a separate stack array.\n\n### Lookup tables are `__proto__: null`\n\nSo a property miss cannot walk up to `Object.prototype`.\n\n## Where the ESTree AST differs\n\nThe two printers walk different ASTs, and some of the differences reach the output.\n\n### Node types\n\n- **One `Literal` node type** covers strings, numbers, booleans, `null`, regexes and bigints, where Rust\n  has six separate types. `printLiteral` tells them apart from `typeof value` and the presence of `regex` / `bigint`.\n- **`#x in obj` arrives as a `BinaryExpression`** with a `PrivateIdentifier` on the left, not as its own node type.\n  Hence the extra test in `printExpression`'s `BinaryExpression` arm.\n\n### Parenthesized function expressions\n\nOxc's Rust AST records that a function expression was parenthesized in a `pife` flag, and prints the parentheses\nback from it.\n\nESTree has no such flag. The information is only there when the AST was parsed with `preserveParens`,\nas a `ParenthesizedExpression` wrapper. `printExpression`'s arm for that node re-emits the parentheses\naround a function or arrow, and is transparent around anything else.\n\nThis is why `printBinaryish` prints its operands **unstripped**. `withoutParens` is used for\nthe structural type tests, but the node handed to `printExpression` keeps its wrapper,\nso that arm can run.\n\n### Things ESTree cannot express\n\n- `import \"m\";` versus `import {} from \"m\";`\n- An absent `with` clause versus an empty `with {}`\n- `assert {...}` versus `with {...}`\n- The span of a `with {...}` clause (only its individual attributes have ESTree locations)\n\nThe conformance harness normalizes the Rust AST down to what ESTree can express before printing,\nrather than expecting the JS side to reproduce information it was never given.\nSee `Normalize` in `tasks/codegen_conformance/src/lib.rs`.\n\n### Positions, and other producers\n\n- **Oxc offsets only.** `start` / `end` offsets are converted to UTF-16 line/column once at the end.\n  Source maps require `sourceText`; `loc` and `range` are not supported inputs.\n\n## How it is tested\n\n`oxc-codegen` is tested against all Test262, Acorn-JSX, and TypeScript test cases - about 62,000 fixtures.\n\nEvery fixture is printed three times:\n\n1. In Rust, with a source map, via the `oxc-codegen-conformance` NAPI addon (`tasks/codegen_conformance`).\n2. In JS, through the no-maps build.\n3. In JS, through the separately compiled maps build.\n\nAll three generated outputs must agree byte for byte. Every decoded generated/original line, column,\nand optional original name from the maps build must also agree with Rust, including ordering and\nduplicate suppression.\n\nBoth sides parse the same source text with the same `SourceType`, derived by the same function.\nFixtures which do not parse cleanly are skipped, rather than passing quietly.\n\nEvery fixture is checked in **both `preserveParens` modes**. They are different paths through the printer -\nwith the wrappers present it must decide what to re-emit, without them it must re-derive every parenthesis\nfrom precedence.\n\n## Rules to keep\n\nA checklist, all of it argued for above.\n\n1. **Never read a character of `state.output`.**\n2. **A `write` must declare a category which truthfully describes its final character**, and must never be\n   given an empty string.\n3. **Only use `writeNoLast` where `last` is provably dead.** A real write must follow before any reader runs.\n4. **Preserve the three numbering properties** when adding or renumbering a `CAT_*` code, and keep the\n   `if (DEBUG)` assertions which check them.\n5. **Never let a build-time flag add or remove a field on `State`.**\n6. **Do not add an arm near the top of `printExpression`, `printStatement` or other dispatch functions**\n   unless the node type really is that common.\n7. **Do not relax `const_functions` to `let`.**\n8. **Run the conformance suites against a debug build** (`pnpm run build-test`), so the assertions are live.\n9. **Benchmark against a release build** (`pnpm run bench` rebuilds one first). A debug build keeps\n   `debugAssert` calls and does not represent shipped performance.\n\n[`oxc_codegen`]: https://github.com/oxc-project/oxc/tree/main/crates/oxc_codegen\n[`print/categories.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/categories.ts\n[`print/write.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/write.ts\n[`print/space.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/space.ts\n[`print/operators.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/operators.ts\n[`print/types.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/types.ts\n","packages/codegen/README.md":"# oxc-codegen\n\nFast, synchronous code generation for JavaScript and TypeScript ASTs.\n\n`oxc-codegen` turns an [ESTree](https://github.com/estree/estree) or\n[TS-ESTree](https://typescript-eslint.io/packages/typescript-estree/) AST into formatted source\ncode. It supports JavaScript, JSX, TypeScript, and TSX.\n\nThe printer is a port of Oxc's Rust `oxc_codegen` crate. With the default options, both printers\nproduce byte-identical output: tab indentation, double-quoted strings, and no comments.\n\n## Installation\n\n```sh\nnpm install oxc-codegen\n```\n\n`oxc-codegen` is ESM-only and requires Node.js `^20.19.0` or `>=22.12.0`.\n\n## Quick start\n\nPair it with [`oxc-parser`](https://www.npmjs.com/package/oxc-parser) to parse and print source code:\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst { program } = parseSync(\"input.js\", \"const answer=6*7\");\nconst { code } = printSync(program);\n\nconsole.log(code);\n// const answer = 6 * 7;\n```\n\nYou can also print a manually constructed AST:\n\n```js\nconst program = {\n  type: \"Program\",\n  sourceType: \"script\",\n  body: [\n    {\n      type: \"ExpressionStatement\",\n      expression: {\n        type: \"CallExpression\",\n        callee: {\n          type: \"MemberExpression\",\n          object: { type: \"Identifier\", name: \"console\" },\n          property: { type: \"Identifier\", name: \"log\" },\n          computed: false,\n          optional: false,\n        },\n        arguments: [{ type: \"Literal\", value: \"Hello!\" }],\n        optional: false,\n      },\n    },\n  ],\n};\n\nconsole.log(printSync(program).code);\n// console.log(\"Hello!\");\n```\n\n### TypeScript and TSX\n\nSet `ts` when the AST can contain TypeScript nodes. For TSX, set both `ts` and `jsx`:\n\n```js\nconst { program } = parseSync(\"component.tsx\", \"const Box = <T,>(value: T) => <div>{value}</div>\");\n\nconst { code } = printSync(program, {\n  ts: true,\n  jsx: true,\n});\n```\n\n## API\n\n### `printSync(node, options?)`\n\n```ts\nfunction printSync(\n  node: ESTree.Program | ESTree.Statement,\n  options?: Options,\n): {\n  code: string;\n  map: SourceMap | null;\n};\n```\n\nPrints a complete `Program` or a single statement and returns the generated source code,\nand (when requested) a standard Source Map v3 object.\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst sourceText = \"const answer=6*7\";\nconst { program } = parseSync(\"input.js\", sourceText);\nconst { code, map } = printSync(program, {\n  sourcemap: true,\n  sourceFilename: \"input.js\",\n  sourceText,\n});\n```\n\nSource-map mappings require `sourceText` and nodes with valid Oxc `start` / `end` offsets.\nA manually constructed AST without offsets can still be printed, but its source map has\nan empty `mappings` string.\n\n### Options\n\n| Option                | Type      | Default | Description                                                      |\n| :-------------------- | :-------- | :------ | :--------------------------------------------------------------- |\n| `indent`              | `string`  | `\"\\t\"`  | Non-empty string of spaces and/or tabs used for one indent level |\n| `startingIndentLevel` | `number`  | `0`     | Starting indent level, from `0` to `1000`                        |\n| `jsx`                 | `boolean` | `false` | Enable TSX-safe printing for ambiguous TypeScript syntax         |\n| `ts`                  | `boolean` | `false` | Select the printer that supports TypeScript nodes                |\n| `sourcemap`           | `boolean` | `false` | Return a Source Map v3 object in `map`                           |\n| `sourceFilename`      | `string`  | `\"\"`    | Original source filename recorded in the source map              |\n| `sourceText`          | `string`  | -       | Original text required for source-map mappings and content       |\n\n## Why pure JavaScript?\n\nMost Oxc packages use native bindings. This package deliberately does not: when an AST already\nlives in JavaScript, passing the entire object graph across a JS/native boundary can cost more than\nprinting it in place. `oxc-codegen` avoids that serialization and uses specialized printer builds\nfor JavaScript and TypeScript workloads.\n\nSee [DESIGN.md](https://github.com/oxc-project/oxc/blob/main/packages/codegen/DESIGN.md) for the\nimplementation details and performance constraints.\n\n## Current limitations\n\n- Comments are not printed.\n- Minified output is not supported.\n\n## Benchmarks\n\nRepresentative time per `printSync` call:\n\n| Fixture                      |     Bytes |       Time |\n| :--------------------------- | --------: | ---------: |\n| `tiny.js`                    |        26 |  0.0001 ms |\n| `RadixUIAdoptionSection.jsx` |     2,518 |  0.0070 ms |\n| `react.development.js`       |    72,141 |  0.1518 ms |\n| `binder.ts`                  |   193,077 |  0.3364 ms |\n| `App.tsx`                    |   415,340 |  1.2912 ms |\n| `lodash.js`                  |   544,096 |  0.7977 ms |\n| `kitchen-sink.tsx`           |   732,222 |  4.2924 ms |\n| `antd.js`                    | 6,683,633 | 16.9652 ms |\n\nThese figures come from one machine and are illustrative, not a regression baseline.\nResults vary between runs, most noticeably for large fixtures such as `antd.js`.\n","packages/codegen/package.json":"# Codegen Skill\n\nPath: `packages/codegen/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/package.json)","packages/codegen/tsconfig.json":"# Codegen Skill\n\nPath: `packages/codegen/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/tsconfig.json)","packages/codegen/test/fixtures/README.md":"# fixtures Documentation\n\nPath: `packages/codegen/test/fixtures/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/test/fixtures/README.md)","apps/oxfmt/test/cli/extensions/0.snap.md":"# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/0.snap.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/0.snap.md)","apps/oxfmt/test/cli/extensions/options.json":"# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/options.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/options.json)","crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json)","crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json)","crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json":"# plugins Plugin Manifest\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json)","crates/oxc_linter/fixtures/extends_config/plugins/jest.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/jest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/jest.json)","crates/oxc_linter/fixtures/extends_config/plugins/parent.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent.json)","crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json)","crates/oxc_linter/fixtures/extends_config/plugins/react.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/react.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/react.json)","crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json":"# Nested-package Skill\n\nPath: `crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json)","apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json":"# Sub Skill\n\nPath: `apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json)",".cursorrules (typescript-expo-jest-detox-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for TypeScript development with Expo, Jest, and Detox integration.\"\nglobs: **/*\nalwaysApply: false\n---\nYou are an expert in TypeScript, React Native, Expo, and Mobile UI development.\n\nCode Style and Structure\n\nNaming Conventions\nTypeScript Usage\nSyntax and Formatting\nUI and Styling\nSafe Area Management\nPerformance Optimization\nNavigation\nState Management\nError Handling and Validation\nTesting\nSecurity\nInternationalization (i18n)\n\nKey Conventions\n\nAPI Documentation\n\nRefer to Expo's documentation for detailed information on Views, Blueprints, and Extensions for best practices.\n"},"files":{".agents/skills/insta-snapshots/SKILL.md":"---\nname: insta-snapshots\ndescription: Review and update Oxc Insta snapshots non-interactively after expected output changes or snapshot test failures.\n---\n\n# Insta snapshots\n\nRun the affected test to generate pending snapshots. Use `cargo insta pending-snapshots` and read the old `.snap` and new `.snap.new` outputs to decide whether each change matches the intended behavior.\n\nUse the CLI instead of the interactive `cargo insta review` UI or manually editing expected output:\n\n```bash\ncargo insta pending-snapshots\ncargo insta accept --snapshot <snapshot_name>\n```\n\nScope acceptance to the reviewed snapshots from this task; check `cargo insta accept --help` if a name or package filter is ambiguous. Avoid workspace-wide acceptance or rejection when unrelated pending snapshots exist. Fix unexpected output in the implementation before accepting it.\n\nAfter accepting, inspect the snapshot diff and rerun the affected test. Finish with the intended `.snap` updates and no task-generated `.snap.new` files left pending. Updating snapshots does not itself call for a commit.\n\nSnapshots record expected output, including successful output in some crates. Linter diagnostic snapshots are under `crates/oxc_linter/src/snapshots/`; other crates have their own locations. Conformance `.snap.md` reports use a separate update mechanism; inspect the producing test harness rather than treating them as Insta pending snapshots.\n","AGENTS.md":"# AGENTS.md - AI Assistant Guide for Oxc\n\nOxc is a high-performance JavaScript/TypeScript toolchain written in Rust containing:\n\n- Parser (JS/TS with AST), Linter (oxlint), Formatter (oxfmt), Transformer, Minifier\n\n## AI Usage Policy for Contributors\n\n**IMPORTANT**: If you are an AI assistant helping a human contributor:\n\n- **Disclose AI usage** - Contributors must disclose when AI tools were used to reduce maintainer fatigue\n- **Full responsibility** - The human contributor is responsible for all AI-generated issues or PRs they submit\n- **Quality standards** - Low-quality or unreviewed AI content will be closed immediately\n\nAll AI-generated code must be thoroughly reviewed, tested, and understood by the contributor before submission. Code should meet Oxc's performance and quality standards.\n\n- **Ban policy** - Contributors who submit repeated low-quality (\"slop\") PRs will be banned without prior warning. Bans may be lifted if you commit to contributing to Oxc in accordance with the policy above. You may request an unban via our Discord.\n\n## Repository Structure\n\nRust workspace with key directories:\n\n- `crates/` - Core functionality (start here when exploring)\n- `apps/` - Application binaries (oxlint, oxfmt)\n- `napi/` - Node.js bindings\n- `npm/` - npm packages\n- `tasks/` - Development tools/automation\n\nAvoid editing `generated` subdirectories.\n\n### Core Crates\n\n- `oxc_parser` - JS/TS parser\n- `oxc_ast` - AST definitions/utilities\n- `oxc_semantic` - Semantic analysis/symbols/scopes\n- `oxc_linter` - Linting engine/rules\n- `oxc_formatter` - Code formatting (Prettier-like for JS)\n- `oxc_formatter_*` - Code formatting (Prettier-like for other languages)\n- `oxc_transformer` - Code transformation (Babel-like)\n- `oxc_minifier` - Code minification\n- `oxc_codegen` - Code generation\n- `oxc_isolated_declarations` - TypeScript declaration generation\n- `oxc_diagnostics` - Error reporting\n- `oxc_traverse` - AST traversal utilities\n- `oxc_allocator` - Memory management\n- `oxc_language_server` - LSP server for editor integration\n- `oxc` - Main crate\n\n## Development Commands\n\nPrerequisites: Rust (MSRV: 1.97), Node.js, pnpm, just\n\n**Setup Notes:**\n\n- All tools already installed (`cargo-insta`, `typos-cli`, `cargo-shear`, `ast-grep`)\n- Rust components already installed (`clippy`, `rust-docs`, `rustfmt`)\n- Use Conventional Commits for commit messages; `.github/workflows/pr.yml` requires a scoped title like `fix(parser): handle trailing comma`\n- Choose verification from the changed code; reserve `just ready` for an optional broad integration checkpoint as described below.\n- You run in an environment where `ast-grep` is available; whenever a search requires syntax-aware or structural matching, default to `ast-grep --lang rust -p '<pattern>'` (or set `--lang` appropriately) and avoid falling back to text-only tools like `rg` or `grep` unless I explicitly request a plain-text search.\n\n### Essential Commands\n\n```bash\njust fmt             # Format code (run after modifications)\njust test            # Run unit/integration tests\njust conformance     # Run conformance tests\njust ready           # Optional broad integration checkpoint (see below)\ncargo lintgen        # Regenerate linter rules enum and impls after adding/modifying rules\ncargo lint-timings   # Update linter timing data after changing linter rule codegen\n\n# Crate-specific updates\njust ast             # Update generated files (oxc_ast changes)\njust minsize         # Update size snapshots (oxc_minifier changes)\njust allocs          # Update allocation snapshots (oxc_parser changes)\n\n# Useful shortcuts\njust watch \"command\" # Watch files and re-run command\njust example tool    # Run tool example (e.g., just example linter)\n```\n\nMore commands can be found in `justfile`.\n\n### Local and stacked development\n\nBefore selecting verification checks, read the [verification guidance](.agents/verification.md).\n\n- Select checks from changed code and affected consumers; for a stack, use each PR's diff against its parent.\n- Reuse passing results while tested inputs remain unchanged. Reserve `just ready` for stable integration checkpoints when broad validation is needed.\n- Preserve required subsystem checks and per-PR CI; a passing stack tip does not establish that intermediate PRs pass.\n\n## Manual Testing & Examples\n\nRun crate examples for quick testing and debugging:\n\n```bash\ncargo run -p <crate_name> --example <example_name> -- [args]\n\n# Common examples:\ncargo run -p oxc_parser --example parser -- test.js\ncargo run -p oxc_linter --example linter -- src/\ncargo run -p oxc_transformer --example transformer -- input.js\ncargo run -p oxc --example compiler --features=\"full\" -- test.js\n```\n\nModify examples in `crates/<crate_name>/examples/` to test specific scenarios.\n\n## Code Navigation\n\n### Key Locations\n\n- AST: Start with `oxc_ast`, use `oxc_ast_visit` for traversal\n- Linting rules: `crates/oxc_linter/src/rules/` (visitor pattern)\n- Parser: `crates/oxc_parser/src/lib.rs`, lexer in `src/lexer/`\n- Tests: Co-located with source, integration in `tests/`, uses `insta` for snapshots\n\n### Conventions\n\n- Use `oxc_allocator` for memory management\n- Follow rustfmt config in `.rustfmt.toml`\n- Use `oxc_diagnostics` for errors with source locations\n- Performance-critical: avoid unnecessary allocations\n\n## Common Tasks\n\n### Adding Linting Rule\n\n1. Use rule generator: `just new-rule <name>` (ESLint rules)\n   - Or plugin-specific: `just new-ts-rule`, `just new-jest-rule`, etc.\n2. Implement using visitor pattern\n3. Add tests in same module\n4. Register in appropriate category\n\n### Parser Changes\n\n1. Research and test grammar changes thoroughly\n2. Update AST definitions in `oxc_ast` if needed\n3. Ensure existing tests pass\n4. Add tests for new features\n\n### Working with Transformations\n\n1. Understand AST structure first\n2. Use visitor pattern for traversal\n3. Handle node ownership/allocator carefully\n4. Test with various input patterns\n\n## Testing\n\nOxc uses multiple testing approaches tailored to each crate:\n\n- **Unit/Integration tests**: Standard Rust tests in `tests/` directories\n- **Conformance tests**: Against external suites (Test262, Babel, TypeScript)\n- **Snapshot tests**: Track failures and expected outputs using `insta`\n\n### Quick Test Commands\n\n```bash\njust test                              # Run all Rust tests\njust conformance                       # Run all conformance tests (alias: cargo coverage)\ncargo test -p <crate_name>             # Test specific crate\n\n# Conformance for specific tools\ncargo coverage parser                  # Parser conformance\ncargo coverage transformer             # Transformer conformance\ncargo run -p oxc_transform_conformance # Transformer Babel tests\n\n# NAPI packages\npnpm test                              # Test all Node.js bindings\n```\n\n### Crate-Specific Testing\n\nEach crate follows distinct testing patterns:\n\n#### oxc_parser\n\n- **Conformance only** via `tasks/coverage`\n- **Command**: `cargo coverage parser`\n- **Suites**: Test262, Babel, TypeScript\n- **Special**: `just allocs` for allocation tracking\n\n#### oxc_linter\n\n- **Inline tests** in rule files (`src/rules/**/*.rs`)\n- **Pattern**: Use `Tester` helper with pass/fail cases\n\n```rust\n#[test]\nfn test() {\n    Tester::new(RuleName::NAME, RuleName::PLUGIN, pass, fail)\n        .test_and_snapshot();\n}\n```\n\n#### oxc_formatter (and other `oxc_formatter_*` language crates)\n\n- Read the target crate's `AGENTS.md`; it includes the shared formatter policy plus language-specific rules and verification commands\n\n#### oxc_minifier\n\n- **Unit tests** in `tests/` subdirectories:\n  - `ecmascript/` - Operations\n  - `peephole/` - Optimizations\n  - `mangler/` - Name mangling\n- **Size tracking**: `just minsize`\n\n#### oxc_transformer\n\n- **Multiple approaches**:\n  - Unit tests: `tests/integrations/`\n  - Conformance: `tasks/transform_conformance/`\n  - Babel plugins: `tasks/transform_conformance/tests/babel-plugin-*/`\n- **Commands**:\n\n```bash\ncargo test -p oxc_transformer                    # Unit tests\ncargo run -p oxc_transform_conformance          # Conformance\njust test-transform --filter <path>             # Filter tests\n```\n\n#### oxc_codegen\n\n- **Integration tests** in `tests/integration/`\n- Test files: `js.rs`, `ts.rs`, `sourcemap.rs`, `comments.rs`\n\n#### oxc_isolated_declarations\n\n- **Snapshot testing** with `insta`\n- Input: `tests/fixtures/*.{ts,tsx}`\n- Output: `tests/snapshots/*.snap`\n- Update: `cargo insta review`\n\n#### oxc_semantic\n\n- **Multiple testing approaches**:\n  - **Conformance tests** (`tests/conformance/`) - Contract-as-code tests for symbols and identifier references\n  - **Integration tests** (`tests/integration/`) - Tests for scopes, symbols, modules, classes, CFG\n  - **Snapshot tests** (`tests/main.rs`) - Verifies scoping data correctness (scope trees, bindings, symbols, references) using `insta` snapshots from `fixtures/`\n  - **Coverage tests** - Via `tasks/coverage` using Test262, Babel, TypeScript suites\n- **Command**: `cargo test -p oxc_semantic`\n- **Update snapshots**: `cargo insta review`\n\n#### Other Crates\n\n- **oxc_traverse**: AST traversal - `cargo test -p oxc_traverse`\n- **oxc_ecmascript**: ECMAScript operations - `cargo test -p oxc_ecmascript`\n- **oxc_regular_expression**: Regex parsing - `cargo test -p oxc_regular_expression`\n- **oxc_syntax**: Syntax utilities - `cargo test -p oxc_syntax`\n- **oxc_language_server**: Editor integration - `cargo test -p oxc_language_server`\n\n### Conformance Testing Foundation\n\n**CRITICAL**: These external test suites are the CORE of Oxc's testing strategy, providing thousands of real-world test cases from mature JavaScript ecosystem projects. They ensure Oxc correctly handles the full complexity of JavaScript/TypeScript.\n\nSuites cloned via `just submodules`:\n\n| Submodule            | Description                                                                                                                                        | Location                            | Used by Crates                                           |\n| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------- |\n| `test262`            | **ECMAScript Conformance Suite**<br>Official JavaScript test suite from TC39, testing compliance with the ECMAScript specification                 | `tasks/coverage/test262`            | parser, semantic, codegen, transformer, minifier, estree |\n| `babel`              | **Babel Test Suite**<br>Comprehensive transformation and parsing tests from the Babel compiler, covering modern JavaScript features and edge cases | `tasks/coverage/babel`              | parser, semantic, codegen, transformer, minifier         |\n| `typescript`         | **TypeScript Test Suite**<br>Microsoft's TypeScript compiler tests, ensuring correct handling of TypeScript syntax and semantics                   | `tasks/coverage/typescript`         | parser, semantic, codegen, transformer, estree           |\n| `estree-conformance` | **ESTree Conformance Tests**<br>Test262, TypeScript, and acorn-jsx suites adapted for ESTree format validation, ensuring correct AST structure     | `tasks/coverage/estree-conformance` | estree                                                   |\n\n**These suites provide:**\n\n- **Thousands of battle-tested cases** from real-world usage\n- **Edge case coverage** that would be impossible to write manually\n- **Industry standard compliance** ensuring compatibility\n- **Continuous validation** against evolving JavaScript standards\n\nRun all conformance tests with `cargo coverage` or `just conformance`.\n\n### Searching Test Suites\n\nThese test suites are pre-cloned and ready to search:\n\n- **Test262** (`tasks/coverage/test262/`) - ECMAScript spec compliance\n- **Babel** (`tasks/coverage/babel/`) - Parsing and transformation edge cases\n- **TypeScript** (`tasks/coverage/typescript/`) - TypeScript syntax and semantics\n\nNOTE: These suites are script-cloned and fully gitignored. ripgrep respects `.gitignore`, so a plain `rg` inside them silently returns nothing.\nUse `rg --no-ignore` (or `-u`) when searching them.\n\n### Snapshot Testing\n\n- Uses `insta` crate for snapshot testing\n- Snapshots track **failing** tests, not passing ones\n- Located in `tasks/coverage/snapshots/` and conformance directories\n- Update with `cargo insta review` after changes\n- Formats: `.snap` (counts), `.snap.md` (detailed failures)\n\n### NAPI (Node.js Bindings) Testing\n\nNAPI packages use **Vitest** for testing Node.js bindings:\n\n```bash\npnpm run build-test   # Build all NAPI packages\npnpm test             # Run all NAPI tests\n```\n\n**Package-specific commands:**\n\n- `oxc-parser`: `cd napi/parser && pnpm run build-test && pnpm test` (also has `pnpm test-browser`)\n- `oxc-minify`: `cd napi/minify && pnpm run build-test && pnpm test`\n- `oxc-transform`: `cd napi/transform && pnpm run build-test && pnpm test`\n\nTests are TypeScript files in each package's `test/` directory.\n\n### Where to Add Tests\n\n| Crate                 | Location                                 |\n| --------------------- | ---------------------------------------- |\n| Parser                | `tasks/coverage/misc/pass/` or `fail/`   |\n| Linter                | Inline in rule files                     |\n| Formatter             | `tests/fixtures/` in the formatter crate |\n| Minifier              | `tests/` subdirectories                  |\n| Transformer           | `tests/integrations/` or Babel fixtures  |\n| Codegen               | `tests/integration/`                     |\n| Isolated Declarations | `tests/fixtures/*.ts`                    |\n| Semantic              | `tests/` directory                       |\n| NAPI packages         | `test/` directory (Vitest)               |\n| Language Server       | Inline and `/fixtures`                   |\n\n### Linter Codegen Changes\n\nWhen changing linter codegen files such as `tasks/linter_codegen/**` or generated linter rule runner files under `crates/oxc_linter/src/generated/`, run `cargo lintgen` and `cargo lint-timings` so generated rule metadata and linter timing data stay in sync.\n\n## Notes\n\n- Rapidly evolving project - APIs may change\n- Performance is critical for all changes\n- Maintain JS/TS standard compatibility\n- Breaking changes need documentation and discussion\n\n---\n\nFor human contributors see `CONTRIBUTING.md` and [oxc.rs](https://oxc.rs/docs/contribute/introduction.html)\n","ARCHITECTURE.md":"# Architecture\n\n## System Overview\n\nOxc (The Oxidation Compiler) is a collection of high-performance JavaScript and TypeScript tools written in Rust. The system is designed as a modular, composable set of compiler components that can be used independently or together to build complete toolchains for JavaScript/TypeScript development.\n\n### Core Mission\n\n- **Performance**: Deliver faster performance than existing JavaScript tools\n- **Correctness**: Maintain compatibility with JavaScript/TypeScript standards\n- **Modularity**: Enable users to compose tools according to their specific needs\n- **Developer Experience**: Provide excellent error messages and tooling integration\n\n## High-Level Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                          Applications                           │\n├─────────────────────────────────────────────────────────────────┤\n│  oxlint  │  Language Server  │  NAPI Bindings  │  Future Tools  │\n├─────────────────────────────────────────────────────────────────┤\n│                        Core Libraries                           │\n├─────────────────────────────────────────────────────────────────┤\n│ Parser │ Semantic │ Linter │ Transformer │ Minifier │ Codegen   │\n├─────────────────────────────────────────────────────────────────┤\n│                    Foundation Libraries                         │\n├─────────────────────────────────────────────────────────────────┤\n│    AST    │  Allocator  │  Diagnostics  │   Span   │  Syntax    │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Architecture Principles\n\n### 1. Zero-Copy Architecture\n\nThe system is built around an arena allocator (`oxc_allocator`) that enables zero-copy operations throughout the compilation pipeline. All AST nodes are allocated in a single arena, eliminating the need for reference counting or garbage collection.\n\n### 2. Visitor Pattern\n\nAST traversal is implemented using the visitor pattern (`oxc_ast_visit`) with automatic visitor generation through procedural macros. This ensures type safety and performance while maintaining code clarity.\n\n### 3. Shared Infrastructure\n\nCommon functionality like error reporting (`oxc_diagnostics`), source positions (`oxc_span`), and syntax definitions (`oxc_syntax`) are shared across all components to ensure consistency.\n\n## Core Components\n\n### Foundation Layer\n\n#### oxc_allocator\n\n- **Purpose**: Arena-based memory allocator for zero-copy operations\n- **Key Features**:\n  - Single allocation arena for entire compilation unit\n  - Eliminates need for Rc/Arc in hot paths\n  - Enables structural sharing of AST nodes\n- **Dependencies**: None (foundational)\n\n#### oxc_span\n\n- **Purpose**: Source position tracking and text manipulation\n- **Key Features**:\n  - Byte-based indexing for UTF-8 correctness\n  - Efficient span operations for source maps\n  - Integration with diagnostic reporting\n- **Dependencies**: None (foundational)\n\n#### oxc_syntax\n\n- **Purpose**: JavaScript/TypeScript language definitions\n- **Key Features**:\n  - Token definitions and keyword mappings\n  - Language feature flags and compatibility\n  - Shared syntax validation logic\n- **Dependencies**: oxc_span\n\n#### oxc_diagnostics\n\n- **Purpose**: Error reporting and diagnostic infrastructure\n- **Key Features**:\n  - Rich error messages with source context\n  - Multiple output formats (JSON, pretty-printed)\n  - Integration with language server protocol\n- **Dependencies**: oxc_span\n\n#### oxc_ast\n\n- **Purpose**: Abstract Syntax Tree definitions and utilities\n- **Key Features**:\n  - Complete JavaScript/TypeScript AST coverage\n  - Generated visitor traits for type safety\n  - Serialization support for caching\n- **Dependencies**: oxc_allocator, oxc_span, oxc_syntax\n\n##### AST Design Principles\n\nThe Oxc AST differs significantly from the [estree](https://github.com/estree/estree) AST specification by removing ambiguous nodes and introducing distinct types. While many existing JavaScript tools rely on estree as their AST specification, a notable drawback is its abundance of ambiguous nodes that often leads to confusion during development.\n\nFor example, instead of using a generic estree `Identifier`, the Oxc AST provides specific types such as:\n\n- `BindingIdentifier` - for variable declarations and bindings\n- `IdentifierReference` - for variable references\n- `IdentifierName` - for property names and labels\n\nThis clear distinction greatly enhances the development experience by aligning more closely with the ECMAScript specification and providing better type safety.\n\n### Core Processing Layer\n\n#### oxc_parser\n\n- **Purpose**: JavaScript/TypeScript parsing\n- **Key Features**:\n  - Hand-written recursive descent parser\n  - Full ES2024+ and TypeScript support\n  - Preservation of comments and trivia\n- **Dependencies**: oxc_allocator, oxc_ast, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_semantic\n\n- **Purpose**: Semantic analysis and symbol resolution\n- **Key Features**:\n  - Scope chain construction\n  - Symbol table generation\n  - Dead code detection\n- **Dependencies**: oxc_ast, oxc_cfg, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_linter\n\n- **Purpose**: ESLint-compatible linting engine\n- **Key Features**:\n  - 200+ built-in rules\n  - Plugin architecture for custom rules\n  - Automatic fixing for many rules\n  - Configuration compatibility with ESLint\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_diagnostics, oxc_cfg\n\n#### oxc_transformer\n\n- **Purpose**: Code transformation and transpilation\n- **Key Features**:\n  - TypeScript to JavaScript transformation\n  - Modern JavaScript feature transpilation\n  - React JSX transformation\n  - Babel plugin compatibility layer\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_allocator\n\n#### oxc_minifier\n\n- **Purpose**: Code size optimization\n- **Key Features**:\n  - Dead code elimination\n  - Constant folding and propagation\n  - Identifier mangling integration\n  - Statement and expression optimization\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_mangler\n\n#### oxc_codegen\n\n- **Purpose**: AST to source code generation\n- **Key Features**:\n  - Configurable output formatting\n  - Source map generation\n  - Comment preservation options\n  - Minified and pretty-printed output modes\n- **Dependencies**: oxc_ast, oxc_span\n\n### Application Layer\n\n#### oxlint (apps/oxlint)\n\n- **Purpose**: Command-line linter application\n- **Key Features**:\n  - File discovery and parallel processing\n  - Configuration file support\n  - Multiple output formats\n  - Integration with CI/CD systems\n- **Dependencies**: oxc_linter, oxc_parser, oxc_semantic\n\n#### Language Server (oxc_language_server)\n\n- **Purpose**: LSP implementation for editor integration\n- **Key Features**:\n  - Real-time diagnostics\n  - Go-to-definition and references\n  - Symbol search and completion\n- **Dependencies**: All core components\n\n#### NAPI Bindings (napi/\\*)\n\n- **Purpose**: Node.js integration layer\n- **Key Features**:\n  - Parser bindings for JavaScript tooling\n  - Linter integration for build tools\n  - Transform pipeline for bundlers\n  - Async processing support\n- **Dependencies**: Core components + Node.js FFI\n\n## Data Flow\n\n### Compilation Pipeline\n\n1. **Input**: Source text + configuration\n2. **Lexing/Parsing**: `oxc_parser` → AST + comments\n3. **Semantic Analysis**: `oxc_semantic` → Symbol table + scope info\n4. **Processing**: Tool-specific analysis (linting, transformation, etc.)\n5. **Output**: Results (diagnostics, transformed code, etc.)\n\n### Memory Management Flow\n\n```\nSource Text → Arena Allocator → AST Nodes → Visitors → Results\n     ↓              ↓              ↓           ↓          ↓\n   UTF-8          Arena         Borrowed    Zero-copy   Owned\n  String         Memory         References  Processing  Output\n```\n\n## Quality Attributes\n\n### Performance\n\n- **Target**: 10-100x faster than comparable tools\n- **Strategies**:\n  - Arena allocation for memory efficiency\n  - Zero-copy data structures\n  - Parallel processing where possible\n  - Minimal allocations in hot paths\n\n#### Parser Performance Implementation\n\n- AST is allocated in a memory arena (oxc_allocator) for fast AST memory allocation and deallocation\n- Short strings are inlined by [CompactString](https://crates.io/crates/compact_str)\n- No other heap allocations are done except the above two\n- Scope binding, symbol resolution and some syntax errors are not done in the parser, they are delegated to the semantic analyzer\n\n#### Linter Performance Implementation\n\n- Oxc parser is used for optimal performance\n- AST visit is a fast operation due to linear memory scan from the memory arena\n- Files are linted in a multi-threaded environment, so scales with the total number of CPU cores\n- Every single lint rule is tuned for performance\n\n### Correctness\n\n- **Target**: 100% compatibility with language standards\n- **Strategies**:\n  - Comprehensive test suites\n  - Real-world codebase testing\n  - Conformance testing against official specs\n  - Conservative error handling\n\n### Maintainability\n\n- **Target**: Clear, reviewable, extensible codebase\n- **Strategies**:\n  - Strong type system usage\n  - Procedural macro code generation\n  - Clear separation of concerns\n  - Comprehensive documentation\n\n### Usability\n\n- **Target**: Drop-in replacement for existing tools\n- **Strategies**:\n  - Configuration compatibility\n  - Familiar CLI interfaces\n  - Rich error messages\n  - Editor integration\n\n## Technical Constraints\n\n### Language Choice\n\n- **Rust**: Chosen for memory safety, performance, and zero-cost abstractions\n- **MSRV**: N-2 policy for stability\n\n### Memory Model\n\n- **Arena Allocation**: Single arena per compilation unit\n- **Lifetime Management**: Explicit lifetimes tied to arena\n- **No Garbage Collection**: Manual memory management for predictable performance\n\n### Threading Model\n\n- **File-level Parallelism**: Multiple files processed in parallel\n- **Single-threaded Pipeline**: Each file processed by single thread\n- **Shared State**: Minimal shared state to avoid synchronization overhead\n\n### Compatibility Requirements\n\n- **JavaScript**: ES2024+ compatibility\n- **TypeScript**: Latest TypeScript syntax support\n- **Node.js**: LTS versions through NAPI bindings\n- **Editors**: LSP compatibility for all major editors\n\n## Design Decisions\n\n### Arena Allocator Choice\n\n**Decision**: Use custom arena allocator instead of Rc/Arc\n**Rationale**:\n\n- Eliminates reference counting overhead\n- Enables zero-copy string operations\n- Simplifies memory management\n- Improves cache locality\n\n**Trade-offs**:\n\n- ✅ 10-50% performance improvement\n- ✅ Simplified ownership model\n- ❌ Requires lifetime management\n- ❌ Less flexible memory patterns\n\n### Hand-written Parser\n\n**Decision**: Implement recursive descent parser instead of parser generator\n**Rationale**:\n\n- Easier debugging and maintenance\n- More efficient generated code\n- Faster compilation times\n\n**Trade-offs**:\n\n- ✅ Better performance and error messages\n- ✅ More maintainable code\n- ❌ More manual implementation work\n- ❌ Higher risk of parser bugs\n\n### Visitor Pattern\n\n**Decision**: Use visitor pattern with procedural macros\n**Rationale**:\n\n- Type-safe AST traversal\n- Automatic visitor generation\n- Consistent patterns across tools\n- Efficient dispatch\n\n**Trade-offs**:\n\n- ✅ Type safety and performance\n- ✅ Reduced boilerplate code\n- ❌ Compile-time complexity\n- ❌ Learning curve for contributors\n\n## Future Considerations\n\n### Planned Extensions\n\n- **Formatter**: Complete code formatting tool\n- **Bundler**: Integration with bundling workflows\n- **Type Checker**: Full TypeScript type checking\n- **Plugin System**: User-defined transformations\n\n### Scalability Concerns\n\n- **Large Codebases**: Processing optimization improvements\n- **Memory Usage**: Streaming processing for huge files\n- **Parallel Processing**: Fine-grained parallelization\n\n### Technology Evolution\n\n- **Rust Evolution**: Leveraging new language features\n- **JavaScript Standards**: Keeping pace with TC39 proposals\n- **Editor Integration**: Advanced IDE features\n\n## Development Infrastructure\n\n### Test Infrastructure\n\nCorrectness and reliability are taken extremely seriously in Oxc. We spend significant effort on strengthening the test infrastructure to prevent problems from propagating to downstream tools:\n\n- **Conformance Testing**: Test262, Babel, and TypeScript conformance suites\n- **Fuzzing**: Extensive fuzzing to discover edge cases\n- **Snapshot Testing**: Linter diagnostic snapshots for regression prevention\n- **Ecosystem CI**: Testing against real-world codebases\n- **Idempotency Testing**: Ensuring transformations are stable\n- **Code Coverage**: Comprehensive coverage tracking\n- **End-to-End Testing**: Testing against top 3000 npm packages\n\n### Build and Development Tools\n\n- **Rust**: MSRV 1.86.0+ with clippy and rustfmt integration\n- **Just**: Command runner for development tasks (`just --list` for available commands)\n- **Performance Monitoring**: Continuous benchmarking and performance regression detection\n- **Cross-platform**: Support for Linux, macOS, and Windows\n- **CI/CD**: Automated testing, building, and publishing pipelines\n\nFor detailed development guidelines, see [CONTRIBUTING.md](./CONTRIBUTING.md) and [AGENTS.md](./AGENTS.md).\n\n---\n\nThis architecture document follows the [architecture.md](https://architecture.md/) format for documenting software architecture decisions and system design.\n",".agents/skills/migrate-oxlint/SKILL.md":"---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                         | Oxlint Plugin Name |\n| ----------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                    | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks`   | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`     | `import`           |\n| `eslint-plugin-unicorn`                               | `unicorn`          |\n| `eslint-plugin-jsx-a11y` / `eslint-plugin-jsx-a11y-x` | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                            | `react-perf`       |\n| `eslint-plugin-promise`                               | `promise`          |\n| `eslint-plugin-jest`                                  | `jest`             |\n| `@vitest/eslint-plugin`                               | `vitest`           |\n| `eslint-plugin-jsdoc`                                 | `jsdoc`            |\n| `eslint-plugin-next`                                  | `nextjs`           |\n| `eslint-plugin-node`                                  | `node`             |\n| `eslint-plugin-vue`                                   | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n",".agents/skills/migrate-oxfmt/SKILL.md":"---\nname: migrate-oxfmt\ndescription: Guide for migrating a project from Prettier or Biome to Oxfmt. Use when asked to migrate, convert, or switch a project's formatter from Prettier or Biome to Oxfmt.\n---\n\nThis skill guides you through migrating a project from Prettier or Biome to [Oxfmt](https://oxc.rs/docs/guide/usage/formatter).\n\n## Overview\n\nOxfmt is a high-performance, Prettier-compatible code formatter. Most Prettier options are supported directly.\n\nAn automated migration tool is built into Oxfmt, supporting both Prettier and Biome as migration sources.\n\n## Step 1: Run Automated Migration\n\nFirst, decide whether the automated tool fits:\n\n- Static config (JSON, or a JS/TS config that just exports a plain object): use `--migrate` below.\n- Dynamic JS/TS config (logic such as environment branches or computed values): `--migrate` writes only a resolved snapshot of the values, and the logic does not survive (a warning is printed). Port the logic to `oxfmt.config.ts` manually with the option mappings in this guide.\n- Nested per-directory configs: `--migrate` only handles the config in the current directory. Run it again in each nested directory (see \"Nested Config\" in Step 2).\n\n### From Prettier\n\n```bash\nnpx oxfmt@latest --migrate prettier\n```\n\nThis will:\n\n- Find your Prettier config in the current directory, in the same order as Prettier\n  - Supported: `package.json` (`prettier` field), `.prettierrc` (JSON only), `.prettierrc.json`, and JS/TS configs (`.prettierrc.*`, `prettier.config.*`)\n  - Shareable configs (e.g. `\"prettier\": \"@company/prettier-config\"`) are resolved\n  - YAML, JSON5 and TOML configs are not supported. Convert them to JSON or JS first\n- Create `.oxfmtrc.json` with migrated options\n  - Only Prettier-compatible options are migrated, others are skipped with a warning\n- Migrate `overrides`\n  - `overrides[].options.parser` is migrated to `associations[].language`\n- Migrate `.prettierignore` patterns to `ignorePatterns`\n- Migrate `prettier-plugin-tailwindcss` options to `sortTailwindcss`\n- Migrate `prettier-plugin-svelte` options to `svelte` (`svelteSortOrder` → `sortOrder`, `svelteAllowShorthand` → `allowShorthand`, `svelteIndentScriptAndStyle` → `indentScriptAndStyle`)\n- Migrate `prettier-plugin-astro` options to `astro` (`astroAllowShorthand` → `allowShorthand`, `astroSkipFrontmatter` → `skipFrontmatter`, `astroCompressHTML` → `compressHTML`)\n- Detect `prettier-plugin-packagejson` and enable `sortPackageJson`\n- Skip other plugins with a warning\n\nNotes (Prettier):\n\n- Fails if no Prettier config is found in the current directory. Use `--init` to create a blank `.oxfmtrc.json` instead.\n- The generated `.oxfmtrc.json` may not be formatted yet. Run `oxfmt` after migration.\n\n### From Biome\n\n```bash\nnpx oxfmt@latest --migrate biome\n```\n\nThis will:\n\n- Find and read `biome.json` or `biome.jsonc`\n- Create `.oxfmtrc.json` with migrated options\n- Migrate negated patterns from `files.includes` to `ignorePatterns`\n- Map Biome's two-level config (`formatter.*` and `javascript.formatter.*`) to Oxfmt options\n\nBiome option mapping:\n\n| Biome                                                       | Oxfmt                             |\n| ----------------------------------------------------------- | --------------------------------- |\n| `formatter.indentStyle` (`\"tab\"`/`\"space\"`)                 | `useTabs` (`true`/`false`)        |\n| `formatter.indentWidth`                                     | `tabWidth`                        |\n| `formatter.lineWidth`                                       | `printWidth`                      |\n| `javascript.formatter.quoteStyle`                           | `singleQuote`                     |\n| `javascript.formatter.jsxQuoteStyle`                        | `jsxSingleQuote`                  |\n| `javascript.formatter.quoteProperties` (`\"asNeeded\"`)       | `quoteProps` (`\"as-needed\"`)      |\n| `javascript.formatter.trailingCommas`                       | `trailingComma`                   |\n| `javascript.formatter.semicolons` (`\"always\"`/`\"asNeeded\"`) | `semi` (`true`/`false`)           |\n| `javascript.formatter.arrowParentheses` (`\"asNeeded\"`)      | `arrowParens` (`\"avoid\"`)         |\n| `javascript.formatter.bracketSameLine`                      | `bracketSameLine`                 |\n| `formatter.bracketSpacing`                                  | `bracketSpacing`                  |\n| `formatter.attributePosition` (`\"multiline\"`)               | `singleAttributePerLine` (`true`) |\n\nNotes (Biome):\n\n- For `formatter.*` options in the table, a `javascript.formatter.*` value of the same name takes precedence when present. `bracketSameLine` is read only from `javascript.formatter.bracketSameLine`.\n- Options not set in `biome.json` are written to `.oxfmtrc.json` with Biome's default values explicitly (e.g. `printWidth: 80`, `useTabs: true`), so the output preserves Biome's formatting behavior.\n- If no Biome config is found, creates a blank `.oxfmtrc.json` instead.\n- `overrides` cannot be auto-migrated and must be converted manually (a warning is printed if detected).\n\nNotes (both sources):\n\n- Fails if an Oxfmt config (`.oxfmtrc.json`, `.oxfmtrc.jsonc`, `oxfmt.config.ts` or `oxfmt.config.mts`) already exists. Delete it first if you want to re-run.\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxfmtrc.json` for these key differences:\n\n### printWidth\n\nPrettier and Biome default is 80, Oxfmt default is 100. The migration tool sets `printWidth: 80` if not specified in your source config. Decide whether to keep 80 or adopt 100.\n\n### Unsupported Options (Prettier only)\n\nAny option not in \"Prettier-Compatible Options\" below is skipped with a warning, including options of unsupported plugins (e.g. `importOrder`). Notable ones:\n\n| Option                          | Status                                                                    |\n| ------------------------------- | ------------------------------------------------------------------------- |\n| `endOfLine: \"auto\"`             | Not supported. Use `\"lf\"` or `\"crlf\"` explicitly                          |\n| `experimentalTernaries`         | Not supported                                                             |\n| `requirePragma`, `insertPragma` | Not supported                                                             |\n| `parser`                        | Skipped at top-level. In `overrides`, migrated to `associations` if known |\n| `plugins`                       | Only known plugins are migrated (see Step 1)                              |\n\nRegex values (e.g. `\"/^my-/\"`) in `tailwindFunctions` / `tailwindAttributes` are also skipped with a warning, Oxfmt only supports literal strings there.\n\n### sortPackageJson (Prettier only)\n\nEnabled by default in Oxfmt, but the migration tool disables it unless `prettier-plugin-packagejson` was detected. Review whether you want this enabled.\n\nNote: Oxfmt's sorting algorithm differs from `prettier-plugin-packagejson`.\n\n### embeddedLanguageFormatting (Prettier only)\n\nEmbedded language formatting (e.g., CSS-in-JS) generally works.\n\n### overrides\n\nPrettier `overrides` are auto-migrated. Unsupported options in them are skipped with a warning, so review the result.\n\n`overrides[].options.parser` is migrated to `associations`, which tells Oxfmt which language to use for matched files:\n\n```json\n{\n  \"associations\": [{ \"files\": [\"*.svg\"], \"language\": \"html\" }]\n}\n```\n\n`language` is a Shiki language id (as in Markdown code fences), not a Prettier parser name (e.g. `angular-html` instead of `angular`, `typescript` instead of `babel-ts`).\n\nBiome `overrides` cannot be auto-migrated. Convert manually:\n\n```json\n{\n  \"overrides\": [\n    {\n      \"files\": [\"*.md\"],\n      \"excludeFiles\": [\"CHANGELOG.md\"],\n      \"options\": { \"tabWidth\": 4 }\n    }\n  ]\n}\n```\n\n(`excludeFiles` is optional.)\n\n### Nested Config\n\nOxfmt supports nested configuration files: a config in a subdirectory applies to files under that directory. Pass `--disable-nested-config` to opt out and use only the root config.\n\nHowever, `--migrate` only migrates the config in the current directory, and does not walk subdirectories. If the project has per-directory Prettier or Biome configs, run `--migrate` in each of those directories.\n\n### Prettier-Compatible Options\n\nThese options transfer directly with the same behavior:\n`printWidth`, `tabWidth`, `useTabs`, `semi`, `singleQuote`, `jsxSingleQuote`, `quoteProps`, `trailingComma`, `arrowParens`, `bracketSpacing`, `bracketSameLine`, `objectWrap`, `experimentalOperatorPosition`, `endOfLine`, `proseWrap`, `htmlWhitespaceSensitivity`, `singleAttributePerLine`, `vueIndentScriptAndStyle`, `embeddedLanguageFormatting`\n\n## Step 3: Configure Oxfmt Extensions\n\nOxfmt offers features not available in Prettier:\n\n### sortImports\n\nSort import statements, inspired by `eslint-plugin-perfectionist/sort-imports` (disabled by default):\n\n```json\n{\n  \"sortImports\": {\n    \"partitionByNewline\": true,\n    \"newlinesBetween\": false\n  }\n}\n```\n\nOther options: `partitionByComment`, `sortSideEffects`, `order`, `ignoreCase`, `internalPattern`, `groups`, `customGroups`.\n\n### sortTailwindcss\n\nReplaces `prettier-plugin-tailwindcss`. Auto-migrated with renamed options:\n\n| Prettier (top-level)         | Oxfmt (`sortTailwindcss.*`) |\n| ---------------------------- | --------------------------- |\n| `tailwindConfig`             | `config`                    |\n| `tailwindStylesheet`         | `stylesheet`                |\n| `tailwindFunctions`          | `functions`                 |\n| `tailwindAttributes`         | `attributes`                |\n| `tailwindPreserveWhitespace` | `preserveWhitespace`        |\n| `tailwindPreserveDuplicates` | `preserveDuplicates`        |\n\n### Other Extensions\n\n| Option               | Default  | Description                                                                     |\n| -------------------- | -------- | ------------------------------------------------------------------------------- |\n| `insertFinalNewline` | `true`   | Whether to add a final newline at end of file                                   |\n| `sortPackageJson`    | `true`   | Sort `package.json` keys. Set `{ \"sortScripts\": true }` to also sort scripts    |\n| `jsdoc`              | disabled | Format JSDoc comments. Set `true` or an options object for fine-grained control |\n| `svelte`             | disabled | Svelte formatting options, replacing `prettier-plugin-svelte` (auto-migrated)   |\n| `astro`              | disabled | Astro formatting options, replacing `prettier-plugin-astro` (auto-migrated)     |\n| `associations`       | `[]`     | Format matched files as the given language (e.g. `*.svg` as `html`)             |\n\n## Step 4: Update CI and Scripts\n\nReplace formatter commands with Oxfmt:\n\n```bash\n# Before (Prettier)\nnpx prettier --write .\nnpx prettier --check .\n\n# Before (Biome)\nnpx biome format --write .\nnpx biome check .\n\n# After\nnpx oxfmt@latest\nnpx oxfmt@latest --check\n```\n\n### Common CLI Options\n\n| Prettier / Biome                                | Oxfmt                                        |\n| ----------------------------------------------- | -------------------------------------------- |\n| `prettier --write .` / `biome format --write .` | `oxfmt` (default: cwd, `--write` mode)       |\n| `prettier --check .` / `biome check .`          | `oxfmt --check`                              |\n| `prettier --list-different .`                   | `oxfmt --list-different`                     |\n| `prettier --config path`                        | `oxfmt --config path`                        |\n| `prettier --ignore-path .prettierignore`        | `oxfmt --ignore-path .prettierignore`        |\n| `cat file \\| prettier --stdin-filepath=file.ts` | `cat file \\| oxfmt --stdin-filepath=file.ts` |\n\n### File Type Coverage\n\n- JS/TS, JSON/JSONC/JSON5, CSS/SCSS/Less, GraphQL, YAML, Markdown, TOML: Formatted natively by Oxfmt\n- HTML, Vue, Svelte, Astro, etc.: Delegated to Prettier internally (when using `npx oxfmt`)\n\n## Tips\n\n- Config file: `.oxfmtrc.jsonc` and `oxfmt.config.ts` are also supported as auto-discovered config file names, in addition to `.oxfmtrc.json`. `--migrate` and `--init` only generate `.oxfmtrc.json`; keep it as-is unless the user explicitly asks for another format or the source config had logic to preserve (see Step 1).\n- EditorConfig: Oxfmt reads `.editorconfig` automatically for `useTabs`, `tabWidth`, `endOfLine`, `insertFinalNewline`, `printWidth`, and `singleQuote`. Options in `.oxfmtrc.json` take precedence.\n  - Nearest one only, does not respect `root`\n- CI: Use `npx oxfmt@latest --check` to enforce formatting in CI.\n- LSP: Run `oxfmt --lsp` for editor integration via Language Server Protocol.\n- Schema support: Add `\"$schema\": \"./node_modules/oxfmt/configuration_schema.json\"` to `.oxfmtrc.json` for editor autocompletion.\n- Init: Run `npx oxfmt@latest --init` to create a default `.oxfmtrc.json` without migration.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/formatter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/formatter/config-file-reference.html)\n- [Unsupported Features](https://oxc.rs/docs/guide/usage/formatter/unsupported-features.html)\n",".agents/skills/performance-lint-rules/SKILL.md":"---\nname: performance-lint-rules\ndescription: Performance review guidance for Oxc linter rule implementations. Use only when reviewing Rust rule code under crates/oxc_linter/src/rules/ or when explicitly auditing those rules for performance improvements.\n---\n\n## Performance Guidelines\n\n### Prefer top-level node kind checks\n\nPut node kind checks at the rule entry point. If a rule only handles a few syntactic forms, start `run` with an `AstKind` match and return for all other nodes, even when a helper filters again internally. This lets lintgen derive narrower `NODE_TYPES` and avoids dispatching the rule on unrelated AST nodes.\n\nAfter changing the relevant node kinds for a rule, regenerate the rule runner with `cargo lintgen` and consider adding or updating `assert_rule_runs_on_node_types` coverage in `crates/oxc_linter/src/rule.rs`.\n\nImplement only the needed entry point. If a rule is a whole-file pass over semantic indexes, use `run_once` by itself. Implementing both `run` and `run_once` prevents useful node-type narrowing.\n\n### Do cheaper checks first\n\nOrder checks from cheapest and most selective to most expensive. Return quickly for common non-matches before doing semantic lookups, allocations, or deeper traversal.\n\n- Matching a small fixed string set with `matches!` before semantic checks.\n- Rejecting lowercase identifiers before global-object checks when only constructors can match.\n- Checking whether a JSX attribute starts with `aria-` before lowercasing it.\n- Checking for required syntax such as a `key` prop before looking up callback parameter symbols.\n- Checking `source_range(span).contains(\"this\")` before running a visitor that only finds `this`.\n\n### Delay expensive context\n\nMost files do not contain lint errors. Do not prepare diagnostics, labels, help text, fix data, ancestors, symbols, JSX element types, or replacement strings until the rule has found a syntactic candidate that could actually report.\n\n### Iterate over the smallest set possible\n\n- Use `run_once` when the rule only needs a whole-file pass and does not need to run on every node.\n- Iterate over symbols instead of AST nodes when looking for references to specific names.\n- Prefer targeted lists or semantic data over broad AST traversal when available.\n- For name-based binding checks, use `ctx.scoping().get_binding(scope_id, name)` instead of scanning every binding in `get_bindings(scope_id)`.\n- For global or unresolved identifier checks, start from `ctx.scoping().root_unresolved_references().get(name)` for the small set of relevant names instead of visiting every `IdentifierReference`.\n- When iterating unresolved references, still verify the reference is the right kind: skip references with a symbol, type-only references, and nodes whose `AstKind` is not the expected identifier or member access.\n- When checking imported specifiers or exported names, iterate the concrete specifiers or precomputed export set rather than scanning all root bindings for every item.\n\nUse precomputed `FxHashSet`s only when many symbols need the same membership test. Prefer keyed semantic lookup when each lookup already has an exact name.\n\n### Avoid unnecessary regular expressions\n\nAvoid regular expressions when byte or string checks are enough: `contains`, `starts_with`, `ends_with`, or matching a small fixed set.\n\nFor hot comment or string scanning paths, prefer a cheap `memchr` or byte search to reject most inputs, then parse only candidates. Preserve regex semantics when replacing one, especially identifier boundaries, optional prefixes, and multiline whitespace.\n\n### Avoid heap allocations\n\n- Use copy-on-write utilities when a value usually does not need to change.\n- Avoid intermediate `Vec`s and `String`s when iteration or borrowed data is enough.\n- Keep temporary data on the stack when practical.\n- Delay allocation until a diagnostic, fix, or transformed value is actually needed.\n- Use allocation-free ASCII comparisons such as `starts_with_ignore_case` before calling `cow_to_ascii_lowercase`.\n- Use byte scans such as `as_bytes().array_windows()` for simple ASCII patterns like escape sequences.\n- Reserve hash maps or sets when the final size is known.\n- Avoid building a `HashSet` just to check names that can be looked up directly in scoping data.\n",".github/copilot-instructions.md":"# Copilot Review Instructions\n\nWhen reviewing pull requests in this repository:\n\n- Do not guess or infer compiler errors. Only report them when supported by actual compiler diagnostics.\n- Treat files inside any `fixtures` directories as test data, not production code.\n- Fixture files may intentionally contain buggy, unsafe, or syntactically invalid code to validate parser/linter/transformer behavior.\n- Do not report normal code-quality or correctness issues for fixture files.\n- You may report a fixture issue only when the test data appears incorrect or mislabeled:\n  - The folder or filename does not match what the test claims to cover.\n  - The fixture content does not actually test the described case.\n  - The fixture appears accidentally broken rather than intentionally crafted.\n","npm/oxlint-plugins/README.md":"# @oxlint/plugins\n\nPlugin utilities for [Oxlint](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n\nThis package provides optional functions to assist in creating Oxlint JS plugins and rules.\n\n## Installation\n\n```bash\nnpm install @oxlint/plugins\n```\n\n## Usage\n\n### Define functions\n\nUse `definePlugin` and `defineRule` if authoring your plugin in TypeScript for type safety.\n\n```typescript\nimport { definePlugin, defineRule } from \"@oxlint/plugins\";\n\nconst rule = defineRule({\n  create(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n});\n\nexport default definePlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n### Types\n\nThis package also includes types for plugins and rules.\n\n```typescript\nimport type { Context, Rule, ESTree } from \"@oxlint/plugins\";\n\nconst rule: Rule = {\n  create(context: Context) {\n    return {\n      Program(node: ESTree.Program) {\n        // Rule logic here\n      },\n    };\n  },\n};\n```\n\n### ESLint compatibility\n\nIf your plugin uses Oxlint's [alternative `createOnce` API](https://oxc.rs/docs/guide/usage/linter/js-plugins#alternative-api),\nuse `eslintCompatPlugin` to convert the plugin so it will also work with ESLint.\n\n```typescript\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nconst rule = {\n  createOnce(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n};\n\nexport default eslintCompatPlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n## Node.js version\n\nThis package requires Node.js 12.22.0+, 14.17.0+, 16.0.0+, or later.\nThis matches the minimum Node.js version required by ESLint 8.\n\nThis package provides both ESM and CommonJS entry points.\n\nSo a plugin which depends on `@oxlint/plugins` can be:\n\n- Used with any version of Oxlint.\n- Used with ESLint 8+.\n- Published as either ESM or CommonJS.\n\n## Docs\n\nFor full documentation, see [Oxlint JS Plugins docs](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n","packages/codegen/CHANGELOG.md":"# Changelog\n\nAll notable changes to this package will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0).\n\n## [0.155.0] - 2026-10-19\n\n### ⚡ Performance\n\n- 2ab6b63 packages/codegen: Remove redundant `printSpaceBeforeIdentifier` and `last` updates in `printParams` (#27652) (overlookmotel)\n- e533278 codegen: Reduce writes in `printDecorators` (#27651) (overlookmotel)\n\n### 📚 Documentation\n\n- 3de03c1 packages/codegen: Correct outdated comments (#27838) (overlookmotel)\n\n## [0.154.0] - 2026-10-13\n\n### 🐛 Bug Fixes\n\n- 760b60b codegen, packages/codegen: Remove extraneous space before `RestElement` in `ArrayPattern` (#27613) (overlookmotel)\n- 0353d37 packages/codegen: Support printing `V8IntrinsicExpression`s (#27597) (overlookmotel)\n- f50257e codegen: Avoid extra indent before empty do-while bodies (#27407) (camc314)\n- 5461c1a packages/codegen: Fix printing negative `BigIntLiteral` after `-` (#27377) (overlookmotel)\n- bebef88 packages/codegen: Fix debug assert fail printing `TSJSDocNonNullableType` (#27373) (overlookmotel)\n\n### ⚡ Performance\n\n- e1c97f8 packages/codegen: Remove all `printSpaceBeforeIdentifier` calls (#27620) (overlookmotel)\n- 70ecfdd packages/codegen: Skip redundant `printSpaceBeforeIdentifier` call (#27619) (overlookmotel)\n- 3c289f3 packages/codegen: Remove branches from loops printing arrays (#27614) (overlookmotel)\n- 27f5b45 packages/codegen: Remove branch printing `RestElement` in `ArrayPattern` (#27608) (overlookmotel)\n- adc90ed packages/codegen: Reduce branches in `printArguments` (#27607) (overlookmotel)\n- 262dd21 packages/codegen: Remove unnecessary `printSpaceBeforeIdentifier` calls (#27606) (overlookmotel)\n- 4bb9722 packages/codegen: Print `SpreadElement` arguments via `printExpression` (#27599) (overlookmotel)\n- 0c98dc5 packages/codegen: Reduce megamorphic `type` reads in `printParams` (#27595) (overlookmotel)\n- e3780b5 packages/codegen: Reduce writes in `printForOfStatement` (#27594) (overlookmotel)\n- 8d0ba20 packages/codegen: Reduce megamorphic `type` reads in `printForOfStatement` (#27593) (overlookmotel)\n- b29f35e packages/codegen: Reduce megamorphic `type` reads in `printDirectivesAndStatements` (#27592) (overlookmotel)\n- 256259d packages/codegen: Reduce megamorphic `type` reads in `printConditionalExpression` (#27591) (overlookmotel)\n- a9bd942 packages/codegen: Reduce megamorphic `type` reads in `printObjectProperty` (#27590) (overlookmotel)\n- 0a386ff packages/codegen: Ensure `shorthand` var is always a boolean (#27589) (overlookmotel)\n- 8d4c633 packages/codegen: Reduce branches in `printObjectProperty` (#27588) (overlookmotel)\n- b4c8c04 packages/codegen: Reduce megamorphic `type` reads in `printMemberExpression` (#27587) (overlookmotel)\n- ff5eae8 packages/codegen: Reduce megamorphic `type` reads (#27586) (overlookmotel)\n- 961f318 packages/codegen: Reduce megamorphic `type` reads in `binCheckAndPrepare` (#27460) (overlookmotel)\n- 3d2f93f packages/codegen: Reduce megamorphic property accesses for `BinaryExpression`s (#27459) (overlookmotel)\n- 675ec5e packages/codegen: Reduce megamorphic property accesses in `printBinaryish` (#27458) (overlookmotel)\n- b0dbfff packages/codegen: Reduce writes and branches printing functions (#27455) (overlookmotel)\n- 5f3865c packages/codegen: Reduce writes for optional `MemberExpression`s (#27454) (overlookmotel)\n\n### 📚 Documentation\n\n- 59472c5 packages/codegen: Add `@param`s to JSDoc comments which had none (#27601) (overlookmotel)\n- 5ee0fc9 packages/codegen: Complete `@param` lists in partly documented JSDoc comments (#27600) (overlookmotel)\n- 12dfa25 packages/codegen: Correct doc comment for `printBinaryish` (#27456) (overlookmotel)\n\n## [0.151.0] - 2026-09-21\n\n### 🚀 Features\n\n- 1fac655 coverage: Support new TypeScript fixture layout (#26226) (camc314)\n\n## [0.150.0] - 2026-09-14\n\n### 🐛 Bug Fixes\n\n- 32d00c5 codegen: Preserve instantiation expression precedence (#26424) (camc314)\n- 5986187 packages/codegen: Preserve private-in right operand precedence (#26420) (camc314)\n- f8e6c6c packages/codegen: Preserve in restriction through yield arguments (#26421) (camc314)\n- 6e15ad5 packages/codegen: Print matching quoted import names as identifiers (#26404) (camc314)\n- bbbb4bc packages/codegen: Preserve private-in left operand precedence (#26403) (camc314)\n- a111b5b packages/codegen: Print accessibility modifiers before abstract (#26402) (camc314)\n\n## [0.148.0] - 2026-08-31\n\n### ⚡ Performance\n\n- 07a0793 packages/codegen: Flatten output in chunks (#26109) (overlookmotel)\n- 487427a packages/codegen: Faster string flattening (#26108) (overlookmotel)\n- 7785583 packages/codegen: Ensure indent strings are flattened (#26107) (overlookmotel)\n- 0dd4db3 packages/codegen: Store mapping positions in an `Int32Array` (#26085) (overlookmotel)\n\n## [0.147.0] - 2026-08-24\n\n### 🐛 Bug Fixes\n\n- 6c5ad1b codegen: Only add mapping names for `PrivateIdentifier`s which have changed (#25958) (overlookmotel)\n- a185ac3 codegen: Print `#` before private identifier in TS signature key (#25938) (overlookmotel)\n\n### ⚡ Performance\n\n- 53f6270 packages/codegen: Eagerly initialize `mapNames` (#25979) (overlookmotel)\n- 5b43c60 packages/codegen: Split recording mappings into multiple functions (#25978) (overlookmotel)\n- cc947b5 packages/codegen: Reduce property lookups (#25977) (overlookmotel)\n- c22a9cc packages/codegen: Combine condition checks (#25976) (overlookmotel)\n- 215a6a5 packages/codegen: Add functions for writing/marking unnamed mappings (#25975) (overlookmotel)\n- 1004439 packages/codegen: Merge `mark` and `write` calls in `printImportAttributes` (#25972) (overlookmotel)\n- 94c8a34 packages/codegen: Remove `node` param from `printString` and `printNonNegativeFloat` (#25971) (overlookmotel)\n- 7e2b4b3 packages/codegen: `printNonNegativeFloat` write mapping directly (#25968) (overlookmotel)\n- 53ff080 packages/codegen: Remove `lastWasPostfixClose` (#25888) (overlookmotel)\n\n### 📚 Documentation\n\n- 07de646 packages/codegen: Fix JSDoc comments on `write*` methods (#25966) (overlookmotel)\n- 3b09c04 packages/codegen: Clarify comment (#25959) (overlookmotel)\n\n## [0.146.0] - 2026-08-19\n\n### 🐛 Bug Fixes\n\n- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)\n- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)\n\n### ⚡ Performance\n\n- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)\n- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)\n- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)\n- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)\n- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)\n- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)\n- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)\n\n### 📚 Documentation\n\n- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)\n\n## [0.145.0] - 2026-08-18\n\n### 💥 BREAKING CHANGES\n\n- 365274e packages/codegen: [**BREAKING**] `printSync` return an object (#25720) (overlookmotel)\n\n### 🚀 Features\n\n- 6a7eb60 packages/codegen: Alter capitalization of `sourceFilename` option (#25854) (overlookmotel)\n- 300763f packages/codegen: Add full source map support (#25585) (camc314)\n- a4478e9 codegen: Add `oxc-codegen` package (#25488) (overlookmotel)\n\n### 🐛 Bug Fixes\n\n- 0fbcf64 codegen: Validate sourcemap options (#25860) (camc314)\n- 4cc7ea4 codegen: Reject invalid indent options (#25807) (camc314)\n- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582) (camc314)\n- 59e8895 codegen: Validate starting indent level (#25550) (camc314)\n\n### ⚡ Performance\n\n- 5444cbf codegen: Skip escaping harmless `<` tokens (#25564) (camc314)\n\n### 📚 Documentation\n\n- 58f7ab9 packages/codegen: Reformat docs and comments (#25848) (overlookmotel)\n- ce03ac1 packages/codegen: Fix JSDoc comments for `printSync` (#25722) (overlookmotel)\n- 386a699 packages/codegen: Correct JSDoc comment (#25716) (overlookmotel)\n- fd62354 codegen: Revamp package readme (#25560) (camc314)\n- ffa3153 codegen: Clarify raw transfer Node requirement (#25565) (camc314)\n- c5d4063 codegen: Clarify binary walk allocations (#25562) (camc314)\n- 5703d7a codegen: Correct stale printer comments (#25561) (camc314)\n\n","crates/oxc_transformer_plugins/README.md":"# Oxc Transformer Plugins\n\nSpecialized transformation plugins for common JavaScript and TypeScript patterns.\n\n## Overview\n\nThis crate provides specific transformation plugins that can be used independently or as part of the broader oxc transformation pipeline. These plugins handle common transformation needs like global variable injection and module format conversion.\n\n## Key Features\n\n- **Global variable injection**: Add global variables to modules\n- **Module runner transforms**: Transform modules for different execution environments\n- **Global defines replacement**: Replace compile-time constants\n- **Composable plugins**: Use individually or combine multiple plugins\n\n## Architecture\n\n### Plugin System\n\nEach plugin implements the transformation logic for a specific use case:\n\n- **Focused responsibility**: Each plugin handles one type of transformation\n- **Traverse integration**: Uses oxc's traversal system for efficient AST walking\n- **Composable**: Multiple plugins can be applied in sequence\n- **Context-aware**: Access to semantic information when needed\n\n### Common Use Cases\n\n#### Build-time Optimizations\n\n- **Dead code elimination**: Remove unreachable code paths\n- **Constant folding**: Replace compile-time constants\n- **Environment variables**: Inject environment-specific values\n\n#### Module System Compatibility\n\n- **Polyfill injection**: Add necessary polyfills for target environments\n- **Global shimming**: Provide Node.js globals in browser environments\n- **Format conversion**: Transform between module formats\n\n#### Development Tools\n\n- **Debug injection**: Add development-time debugging code\n- **Hot module replacement**: Support for HMR systems\n- **Testing utilities**: Inject test-specific globals\n\n### Integration Points\n\nThese plugins integrate with:\n\n- **oxc_transformer**: Core transformation infrastructure\n- **oxc_traverse**: AST traversal and mutation\n- **Build tools**: Webpack, Vite, Rollup, and other bundlers\n- **CLI tools**: Direct usage in command-line transformations\n\nThe plugin architecture enables extensible, composable transformations while maintaining high performance through oxc's efficient traversal system.\n","packages/codegen/DESIGN.md":"# Design\n\nWhy `oxc-codegen` is shaped the way it is.\n\nIt is a port of Oxc's Rust [`oxc_codegen`] crate, and its output has to be byte-identical to it.\nBut it is not a transliteration. A straight port would not take into account differences between Rust and JS,\nand would likely be several times slower.\n\nThis document is for someone about to change the printer, wondering which of its oddities are load bearing.\nEverything here is also documented at the point it applies. This is an overview.\n\n## Contents\n\n- [Why not native?](#why-not-native)\n- [What this is](#what-this-is)\n- [What it is not at present](#what-it-is-not-at-present)\n- [`state.last`](#statelast)\n- [Four builds from one source tree](#four-builds-from-one-source-tree)\n- [Other things which make it fast](#other-things-which-make-it-fast)\n- [Where the ESTree AST differs](#where-the-estree-ast-differs)\n- [How it is tested](#how-it-is-tested)\n- [Rules to keep](#rules-to-keep)\n\n## Why not native?\n\nOxc's other JS packages are bindings around a native library. This one deliberately is not.\n\nThe starting point was a hunch:\n\n> **When the AST is already in JS, no native printer can beat a JS one.**\n\n### The argument\n\n- **Printing is cheap.** It is a linear walk of the AST, emitting strings. There is no parsing, no analysis,\n  no search. It doesn't play to the strengths of a faster language the way parsing or minifying does.\n- **Crossing into native is not cheap.** A native printer cannot see a JS object graph.\n  The whole AST has to be serialized across the boundary before it can print a single character.\n- **The boundary dominates.** The serialization is proportional to the size of the AST, just as the printing is -\n  but it is work a JS printer can skip entirely. It is added to a total that was small to begin with.\n\nThe conclusion: A native printer loses the game before it's even started - still shovelling data over to native side,\nwhile the JS printer already finished work and has gone off to the pub.\n\n### Where this applies\n\nOnly when the AST is **already** in JS - it came from `oxc-parser`, or a plugin built it, or something\ntransformed it on JS side. That is the common use case this package is for.\n\nIt does **not** apply when you are printing source text you also parsed natively. Then stay on native side -\nthe AST never has to leave native memory, and Rust `oxc_codegen` is the right tool. The two are complementary.\n\nThe benchmarks in the [README](./README.md) measure `oxc-codegen` over representative JS-held ASTs.\n\n## What this is\n\nA printer from an ESTree AST to source text, written in TypeScript.\n\n### The output contract\n\nByte-identical to `oxc_codegen` with default options - tab indentation, double quotes, comments off.\n\nThe conformance suites check this on every fixture. It is what makes the two printers substitutable for one another.\n\n## What it is not at present\n\n`oxc_codegen` (Rust printer) is more capable than this one.\n\n- No minify mode.\n- No support for comments.\n- No symbol mangling.\n\nMany of these features can be added in future, and we should be able to do so without _much_ impact on performance.\n\n## `state.last`\n\nThis is the largest divergence from Rust, and most of the others follow from it.\n\n### The problem\n\nTwo adjacent tokens can merge into a different token when written straight up against each other.\n`in` after an identifier becomes part of that identifier. `+` after `+` becomes `++`.\n\nDeciding whether to insert a space means knowing what was written last.\n\n### How Rust answers it\n\nRust just looks. Its output buffer is a `Vec<u8>`, so `code.last_byte()` is a pointer dereference - very cheap.\n\n### Why this doesn't work in JS\n\n**In JS, reading the last character is catastrophic for perf.** `state.output` is built by repeated `+=`,\nwhich V8 represents as a **cons string** - a tree of the pieces, never actually concatenated. Reading any\ncharacter of it forces V8 to flatten that tree.\n\nDo that after every append and the printer spends its time flattening ropes it is about to extend again.\nResult is a **4x slow-down**.\n\nSo `oxc-codegen` has to find a different solution. Every time it pushes a string to output, it also records\nin `state.last` what the output now ends with. `state.output` never needs to be read from.\n\nThe rope is flattened once, at the end. Whatever the caller does with the returned code will flatten it anyway\n(and `generateSourceMap` scans it), so `printSync` pre-flattens with the fastest method available.\nA large output is flattened in chunks along the way rather than all at once at the end -\nsee `print/flatten.ts` for the two reasons.\n\n### The answer: A category, not a character\n\nIt turns out the functions which decide whether spaces need to be inserted between keywords / identifiers\ndon't actually need to know what the last character was, they only need to know what _category_ of character\nit was. i.e. It doesn't matter if the last character was `\"a\"` or `\"Z\"` or `\"$\"` - they're all equivalent.\n\nAlmost everywhere in the printer, it knows statically what _category_ it's writing, even if it doesn't know\nthe exact bytes until runtime. e.g. When writing an identifier, it's the `IDENT` category.\n\n```ts\nwrite(state, node.name, CAT_IDENT);\n```\n\nThis has 3 advantages over storing the last character:\n\n1. Categories are just small integers, the cheapest data type there is.\n2. The categories are statically defined at call sites. No `node.name.at(-1)`.\n3. Checking the category of last write is cheap - `last === CAT_IDENT` not `isIdentifierPart(lastChar)`.\n\nThat one `last` field replaces all of this:\n\n| Rust                                  | Question it answers                 | JS                      |\n| :------------------------------------ | :---------------------------------- | :---------------------- |\n| `last_byte()` + `is_identifier_part`  | Would a following identifier merge? | `CAT_IDENT`             |\n| `last_byte() == Some(b'?')`           | Would a following `?` make `??`?    | `CAT_QUESTION`          |\n| `peek_nth_byte_back(1) == Some(b'<')` | Is this `!` the `!` of a `<!`?      | `CAT_OP_LT_THEN_UN_NOT` |\n\n### Extending this scheme to operators\n\nRust `oxc_codegen` also holds state about what operator was last written. It stores this info as a pair:\n\n- `prev_op` - Last operator that was written\n- `prev_op_end` - Position of the end of that operator\n\nEach time an operator is written, it sets these 2 fields. When other code wants to know \"am I writing this straight\nafter an operator, and which operator?\" it:\n\n1. Checks if `code.len() == prev_op_end`. If true, then yes, it's right after an operator.\n2. `prev_op` then says which operator.\n\nIn Rust printer, this makes sense. Checking these 2 fields, and comparing to `code.len()` costs 2 or 3 memory accesses,\nand 1 or 2 comparisons. But that's fine! It only reads or writes these 2 fields infrequently - only when an operator\nis being written. And operators are only a small fraction of code.\n\nIn JS codegen, we're in a different situation. We've been forced to update `state.last` after every write,\nto avoid the huge perf hit of reading the last character from `output`.\n\nSo we might as well use `last` to record operators too, and also some similar character classes:\n\n| Rust                                    | Question it answers                                     | JS                |\n| :-------------------------------------- | :------------------------------------------------------ | :---------------- |\n| `code.len() == prev_op_end` + `prev_op` | Which operator came last, and was it right before this? | `CAT_OP_*`        |\n| `code.len() == need_space_before_dot`   | Is space needed after digit? e.g. `0 .toExponential()`  | `CAT_INT_DIGIT`   |\n| `code.len() == prev_reg_exp_end`        | Did a flagless regex just close?                        | `CAT_REGEX_SLASH` |\n\nAll these Rust state fields, and every read of the output buffer, collapse into one field in the JS printer.\n\nIt costs no write barrier to store (because categories are represented by \"SMI\" small integers), and one compare\nto test. Being touched by every single write, it is the hottest field in `State`, reliably in L1 cache,\nand probably often also benefits from fast store-to-load forwarding.\n\n#### The `<!--` case, as an example\n\nRust decides whether a `!` is the `!` of a `<!--` hazard by peeking at the _second_-to-last byte,\nat the moment a `--` is about to be written.\n\nHere the question is answered when the `!` is written, where the preceding character is already known,\nand the answer is baked into which category gets stored. The reader has nothing left to look up.\n\n### Position marks\n\nSimilar to operators, Rust printer also maintains state about the _context_. For example:\n\n```js\n// Function declaration\nexport default function foo () {}\n// Function expression\nexport default (function bar () {});\n```\n\nThese 2 are semantically different - `foo` is available in outer scope, `bar` is not.\n\nWhen printing a `FunctionExpression`, print needs to know \"am I the value of an `export default`?\"\nto know whether it needs to wrap the function in parentheses.\n\nSimilar to operators, Rust printer stores this info in field `start_of_default_export`.\nWhen `code.len() == start_of_default_export` then, yes, what's currently being printed is the value of\nan `export default` statement.\n\nAgain, JS codegen uses `last` to store this info. Except here `last` doesn't describe what _was_ written,\nit says what the context is.\n\nNote that the node which reads the mark need not be the direct child of the `export default`.\nIn `export default (function bar() {})();` the function is the callee of a call expression, and the parens\nare still needed. It gets them because `printCallExpression` wrote nothing before descending into the callee,\nso the mark was still there when `printFunction` looked. Whatever is printed leftmost sees the mark,\nhowever deeply nested it is.\n\n| Rust                                    | Question it answers                   | JS                            |\n| :-------------------------------------- | :------------------------------------ | :---------------------------- |\n| `code.len() == start_of_stmt`           | At the start of a statement?          | `CAT_START_OF_STMT`           |\n| `code.len() == start_of_arrow_expr`     | At the start of a concise arrow body? | `CAT_START_OF_ARROW_EXPR`     |\n| `code.len() == start_of_default_export` | At the start of an `export default`?  | `CAT_START_OF_DEFAULT_EXPORT` |\n\nThe reason this works is that in every case the last character printed in these positions was a space, a line break,\nor the start of the file, which all fall into the `CAT_OTHER` category. So these values of `last` aren't ambiguous\nto any other code which judges what to do next based on what came last. They just treat these values as\n\"last character written was something I don't need to do anything special about\".\n\n#### Why the substitution is safe\n\nTwo facts, both checked across every printer file:\n\n1. **Nothing writes output on the way down from a mark to its reader.** Every path from a mark to a\n   reader either writes nothing, or writes a real `(` - never a `writeNoLast`, which would leave the\n   mark visible past output which should have killed it.\n2. **It can only ever add parentheses, never remove them.** A real `write` always grows the output\n   (empty writes are illegal), so the set of things which kill a mark under categories is a strict\n   subset of what killed the offsets.\n\nSo every possible failure is a spurious pair of parentheses - slightly longer output, but not _invalid_ output.\nThe byte-for-byte conformance tests would catch this, but even if there are gaps in the conformance suite\nfor crazy edge cases and a bug sneaks through, it can't produce invalid JavaScript.\n\n### Why category numbering is load bearing\n\nThe full table is at the top of [`print/categories.ts`] and is the authority.\n\nThree properties are relied on. Adding a code without preserving them will silently space output wrongly.\n\n1. **Identifier hazards are the lowest codes.** So `printSpaceBeforeIdentifier` is `last <= CAT_REGEX_SLASH` -\n   one compare, no table, no branch tree. The operators `printSpaceBeforeOperator` must distinguish are the highest,\n   for the same reason (`last >= CAT_OP_LT_THEN_UN_NOT`).\n2. **The `CAT_START_OF_*` codes sit between those two ranges**, which is what makes both range checks\n   treat them as \"nothing to separate\".\n3. **`CAT_START_OF_STMT` is odd, with the other two marks either side.** The five reader sites each ask\n   about a _pair_ of marks, never all three, so both pairs fall out as one `|`-and-compare:\n\n   ```\n   (last | 1) === CAT_START_OF_STMT          // Statement or `export default`\n   ((last - 1) | 1) === CAT_START_OF_STMT    // Statement or concise arrow body\n   ```\n\nEach property has an `if (DEBUG)` block which iterates every category and asserts the compare selects\nexactly the intended set and nothing else - in [`print/categories.ts`] for the `|` identities, and in [`print/space.ts`]\nfor the two range checks.\n\nRenumber freely. The assertions will tell you what you broke, and cost release builds nothing.\n\n**One deliberate exception**: `CAT_OP_UN_NOT` is an operator code but sits _below_ the operator range.\nNo operator merges with a plain `!`, so storing it must not cost `printSpaceBeforeOperator` a call into its slow path.\n\n### `writeNoLast`\n\nNot every write needs to update `last`.\n\nWhen one token is written in pieces - a string's opening quote, its contents, its closing quote -\nonly the last piece's category can possibly be read. Hence eleven write primitives in [`print/write.ts`]:\n\n| Function                     | Updates `last` | Records a source mapping       |\n| :--------------------------- | :------------- | :----------------------------- |\n| `write`                      | Yes            | No                             |\n| `writeIdent`                 | Yes            | No                             |\n| `writePrivate`               | Yes            | No                             |\n| `writeWithMap`               | Yes            | At the node's start            |\n| `writeWithMapNamed`          | Yes            | At the node's start, with name |\n| `writeWithMapNamedPrivate`   | Yes            | At the node's start, with name |\n| `writeWithMapEnd`            | Yes            | At the node's last character   |\n| `writeNoLast`                | No             | No                             |\n| `writeWithMapNoLast`         | No             | At the node's start            |\n| `writeWithMapNamedNoLast`    | No             | At the node's start, with name |\n| `writeWithMapNamedJSXNoLast` | No             | At the node's start, with name |\n\n- Every `writeWithMap*` and `markMap*` function takes the node's `start` and `end` offsets, extracted by the caller.\n  The node itself is still passed, last, but only debug asserts read it - which is what checks the offsets\n  against the node they claim to describe. The point is where the read happens - in a caller the node's type is known,\n  so `node.start` is a monomorphic load, whereas inside the write functions the same read is megamorphic,\n  because the printer reaches them from every node type there is. Release builds have the parameter and every argument\n  removed by the TSDown plugin.\n- The `*Named` functions record the name the node had in the source, and take a `NamedMappableNode`,\n  which covers nodes with a string `name`. What they record is the text they print -\n  debug builds assert the two are the same string.\n- The other `writeWithMap*` functions take an `UnnamedMappableNode` which covers nodes without a string `name`.\n- There is a `*Named` function per way of recovering the original name from the source, because the scan differs -\n  a JSX identifier may hold a `-`, and a private identifier's span covers a `#` which is not part of its name.\n  Which one applies is settled at the call site, where the node's type is known statically, rather than read back\n  off a `node` which is megamorphic by the time it arrives.\n- `writeIdent` is `write` with the category fixed at `CAT_IDENT` - an identifier, a keyword, or the trailing digits\n  of a number. It is the commonest category by far, so every call which passed it was spending an argument\n  on a value which could never be anything else.\n- `writeWithMapNamed` and `writeWithMapNamedPrivate` take no category for the same reason, which is why their\n  plain forms are `writeIdent` and `writePrivate` rather than `write`.\n- The two `Private` forms write the `#` themselves, so the name they are given is what follows it,\n  and `last` is always `CAT_IDENT` - the caller passes neither.\n- The three `markMap*` functions record a mapping without writing anything, so `last` is not theirs to update.\n- The rule is exact: **only sound where the value of `last` is provably dead**.\n  Another real write must follow before anything reads it.\n- JSX carries the longest runs. Printing `<a.b x=\"1\">hi</a.b>` updates `last` exactly once, on the final `>`.\n- `writeNoLast` accepts an empty string, `write` does not.\n  A `write` of `\"\"` would leave `last` claiming a category for a character that was never written.\n\n### How the invariants are enforced\n\nEvery rule above fails quietly - a missing space in one construct out of thousands. So three debug-only\nmechanisms guard them, all removed from release builds.\n\n1. **`debugAssertCategoryMatches`** runs on every `write`, checking the category truthfully describes\n   the final character written. It cannot be a simple lookup - a category encodes the merge hazard of\n   the trailing _token_, not the trailing character - a digit legitimately ends both a `CAT_IDENT` write\n   and a `CAT_INT_DIGIT` one - so each final character has a permitted set.\n2. **`lastIsStale` / `debugAssertLastFresh`** track the `writeNoLast` rule. `writeNoLast` sets the flag,\n   the next real write clears it, and every reader of `last` calls `debugAssertLastFresh` first,\n   to make sure they're not reading a stale value.\n   An author who was wrong about `last` being dead gets a clear failure, not a wrongly spaced construct.\n3. **The numbering assertions** described above.\n\nThis is why the debug build is the one to run the conformance suites against - `pnpm run build-test`.\n\n## Four builds from one source tree\n\n`dist` holds the printer compiled four times, plus an entry point which picks between them.\n\n| Build              | `TS`  | `SOURCEMAPS` |  Size |\n| :----------------- | :---- | :----------- | ----: |\n| `print_js.js`      | false | false        | 25 KB |\n| `print_js_maps.js` | false | true         | 26 KB |\n| `print_ts.js`      | true  | false        | 39 KB |\n| `print_ts_maps.js` | true  | true         | 40 KB |\n\n`index.ts` picks one from the caller's `ts` and `sourcemap` options and `require`s it on first use -\n`require` rather than `import()`, because `printSync` is synchronous.\n\nA caller printing only JavaScript never loads, parses or compiles the TypeScript printers at all.\n\n### Why separate TS and JS builds\n\n**The obvious reason is code size and complexity.** JS-only builds have all TS-related code removed,\nas it's all gated behind compile-time `TS` constant. Every `if (TS && node.declare) { ... }` block is removed\nby minifier, and every `case \"TSEnumDeclaration\":` arm by the `strip_ts` TSDown plugin. With the arms gone,\nthe TS-only printer functions lose their last references and are tree-shaken away. That is a third of the code.\nHowever, it has a surprisingly small impact on perf.\n\n**The bigger reason is monomorphism.** `oxc-parser` produces differently-_shaped_ node objects for a\nJS-shaped AST and a TS-shaped one - the TS shape carries `optional`, `declare`, `accessibility`,\n`override` and the rest.\n\nIf both shapes flow through one `printFunction`, V8 sees two hidden classes at every property access,\nthe inline caches go polymorphic, and the whole printer slows down.\n\nTwo builds means each one only ever sees one shape. JS printer sees only JS AST-shaped objects,\nTS printer sees only TS AST-shaped objects. This gives a perf boost of over 10%.\n\nAdditionally, the split helps branch prediction. If feeding a mix of JS and TS ASTs through a single printer,\nfor a JS AST branches for \"does this node have a type annotation?\" always find the answer is \"no\".\nBranch predictor believes there's a pattern and when that code runs again, it expects no type annotation again.\nBut this time, the AST is a TS AST, and the answer is \"yes\". Mispredict! Go back 100 instructions, go directly back,\ndo not pass Go, do not collect $200!\n\nWith 2 different builds for JS and TS, this doesn't arise. In the JS build the branch isn't there at all -\nit was compiled away. The TS build only ever sees TS ASTs, so what the branch predictor learns stays true.\n\n### Why `State` lives outside `print` directory\n\nThe same argument, one level up.\n\n`State` is defined in `src-js/state.ts`, which is **not** part of any printer build. The printers import\nit as a type only; the entry point constructs it. So all four builds share one class, and therefore one\nhidden class, for the object they thread through every single function.\n\nThis is why **a build-time flag must never add or remove a field on `State`**.\n\n- The source-map fields are present in every build, and set to `null` when unused.\n- The debug fields are present in every build, and only _initialised_ under `if (DEBUG)`.\n\nMaking either conditional would give each build a different object shape, undoing the whole arrangement.\n\n### The build plugins\n\nThere are 4 TSDown plugins in `tsdown_plugins`. Order matters - `strip_ts` is a text transform,\nso it must run before the plugins which parse.\n\n#### `strip_ts`\n\nDeletes regions fenced with `/* IF TS */` ... `/* END_IF */` from JS-only builds.\n\nThey exist because a minifier can fold `if (TS)` away, but cannot remove a `switch` `case` arm on the grounds\nthat its node type never occurs.\n\nIt fails the build if the fences are unbalanced - and also if it finds _no_ fences at all.\nA plugin silently doing nothing is a failure mode worth guarding against.\n\n#### `unmap_writes`\n\nRuns in two modes, one per build flavour (sourcemaps / no sourcemaps), in both cases stripping arguments\nnothing in that build reads.\n\n**Without source maps**, rewrites every mapped write into the plain one it becomes with no mapping to record -\nso `writeWithMap(state, code, cat, node.start, node.end, node)` turns into `write(state, code, cat)` -\nand rewrites the import to match.\n`writeWithMapEnd` becomes `write` too, and every `NoLast` form becomes `writeNoLast`.\n`writeWithMapNamed` and `writeWithMapNamedPrivate` become `writeIdent` and `writePrivate`, which fix the category\ninstead of taking it. `writePrivate` exists only for the rewrite to land on.\n\n`printString` and `printNonNegativeFloat` take the offsets and the node only to hand them on to a mapped write.\nThey keep their names, but the arguments come off every call, and the parameters off their declarations -\nwhich, unlike the mapped writes, survive into the build.\n\n**With source maps**, in release builds only, just the trailing `node` comes off - from every call,\nand from every declaration. The mappings are real in these builds so the offsets stay, but `node` isn't read\nexcept by debug asserts. Debug builds get neither mode - the `node` parameter stays for the debug asserts to read.\n\nWithout it, the offsets would still be read and held live across a call which ignores them.\n\nThe three `markMap*` calls write nothing, so they have no plain form to become, and keep their names -\nthe arguments come off, and a body which opens `if (!SOURCEMAPS) return` lets the minifier remove what is left.\n\nIt fails the build if a mapped write is called with the wrong number of arguments, if a declaration it transforms\nhas the wrong number of parameters, or if a call it rewrote had no matching import to rewrite.\n\n#### `const_functions`\n\nRewrites top-level `function foo(...) { ... }` into `const foo = (...) => { ... }`.\n\nA function declaration creates a binding which could be assigned later, so every call has to read\nthe current value out of module scope. A `const` cannot be, so V8 calls it directly.\n\n- **Measured at ~3%** across all benchmark fixtures.\n- It really is the `const`. The same rewrite with `let` measures ~4% _slower_ than this, and slower than\n  leaving the declarations alone.\n- The source keeps its declarations, because they hoist and read better. Which is why this is a build step\n  rather than a house style.\n\n#### `remove_asserts`\n\nRemoves `debugAssert` and `typeAssertIs` calls _and the expressions inside them_ from release builds.\n\nThe minifier can drop the calls on its own, but cannot prove `node.value` in `debugAssert(node.value !== null)`\nhas no getter, so it would leave `node.value !== null` behind as a bare expression statement.\n\nThis is what gives us Rust `debug_assert!`-style checks in tests and conformance, with zero cost in release build.\n\n## Other things which make it fast\n\n### Popularity-ordered dispatch\n\n`printExpression` (34 arms) and `printStatement` (33 arms) are megamorphic string switches.\n\nV8 lowers a switch on strings to what amounts to an `if / else if` chain, so arm order is execution order.\nBoth are ordered roughly by how often each node type occurs in real code - common variants like `Identifier`,\n`MemberExpression`, `CallExpression` first.\n\nThis sped up the printer by ~30%.\n\nIt also means **adding an arm near the top of either switch has a cost**, however rare the node type is.\n\nEven after ordering `case` arms, these \"dispatch\" functions remain a large part of runtime (~25%).\nMuch sweat and tokens were spilled trying to reduce it further:\n\n- Perfect hash table to convert `node.type` to an integer ID.\n- `switch`-ing on an ID to reduce the switch to a jump table.\n- Branching on first character to narrow down the range - `switch (node.type.charCodeAt(0)) { case 69: ... }`.\n- Many others...\n\nAll to no effect. Arm ordering worked, nothing else did.\n\nIf anyone else can crack this, it's probably the single largest unrealized perf gain on the table.\n\n### Monomorphism\n\nAs much as possible, call paths are structured so that functions receive the same object shape consistently.\nOnce it has gathered type feedback from the first runs, V8 tiers up hot functions to TurboFan, which generates\ncode along these lines (pseudo-code):\n\n```ts\nfunction printThing(state, node): void {\n  if (SHAPE_OF(node) !== K_EXPECTED_SHAPE) {\n    DE_OPTIMIZE(printThing);\n    return printThing(state, node);\n  }\n\n  // Now it's known exactly what shape `node` is, it becomes static-typed like Rust.\n  // Working with it is very cheap.\n\n  // Original code: `const { left } = node;`\n  const left: Expression = GET_UNCHECKED(node, K_LEFT_OFFSET);\n  // ... fast code from here on ...\n}\n```\n\nIn particular, it is **essential** that `State` remains the same shape _everywhere_. Every function touches it,\nso any inconsistency in shape would turn every access polymorphic and completely tank performance.\n\nThe obvious exception is the dispatch functions discussed above (`printExpression` etc), which are\nunavoidably megamorphic.\n\nWe have not done a full audit of all functions to check if any are receiving polymorphic/megamorphic inputs\nwhen that could be avoided. Such an audit might throw up potential improvements.\n\n#### Moral of the story\n\nConsistent object shapes are _really_ important for good perf in JS engines.\n\nWe should provide users with utility functions to construct AST nodes which produce the exact same object shape\nas what `oxc-parser` produces. This would speed up not just `oxc-codegen`, but also their own code that\noperates on ASTs.\n\n```ts\n// Good\nconst binary = oxc.binaryExpression(left, operator, right);\n// Bad - different field order, different object shape\nconst binary = { left, right, operator };\n```\n\n### Intended parser pairing\n\nThe printer is meant to be used with `oxc-parser` with `experimentalRawTransfer` enabled.\n\nRaw transfer builds the AST with a JS deserializer rather than via JSON. Node `type` strings then come out\nas source literals, so those big `switch`es compare interned strings by pointer, rather than character by character.\nThis gives a 10% - 30% perf boost versus a JSON AST.\n\n### Fast paths\n\n- Pretty mode always puts a single space either side of a binary or assignment operator, so the whole\n  token is one constant string - `\" + \"`, `\" ??= \"` - and one `write`, instead of space-operator-space.\n  It also makes the token-glue checks unobservable around those operators, so they are not made at all.\n  See `PADDED_BIN_OPERATORS` in [`print/operators.ts`].\n- `printNonNegativeFloat` prints integers below 1000 straight from `String()`. They are the overwhelming\n  majority, and three digits are too few for any shortening form to win.\n- `printString` tests the whole string against one regex of characters needing escapes, and appends it\n  whole when there are none. The escaping loop is a separate function because it almost never runs.\n- `printSpaceBeforeOperator` is one compare, with its three-clause slow path split out so the common path\n  stays small enough for V8 to inline.\n- `printLiteral` switches on `typeof value` first, identifying strings and numbers without touching the\n  `regex` or `bigint` properties.\n- `printNumericLiteral` tests `value > 0 && value < Infinity` first, reaching the same branch the full\n  chain would without the two builtin calls.\n\nEach of these produced a small perf bump individually, ~10% gain in aggregate.\n\n### Indent cache\n\n`printIndent` reads a pre-built string for the current level out of an array.\n\n- The cache is created once per _process_, alongside the module-level state in `state.ts`.\n  Every `State` carries a reference to it. So indent strings one printer run has grown are there for the next.\n  This cache is shared between JS and TS, sourcemap and no-sourcemap builds.\n- `growIndents` deliberately forces each new indent string flat (a throwaway `charCodeAt(0)`) before caching it,\n  paying the flatten cost once rather than on every append.\n\n### Source maps computed at the end\n\nDuring printing, a mapped write records only an output offset and the node's original UTF-16 offset\nfrom `start` / `end`. Nodes without offsets are not mapped, and `sourceText` is required to convert\nsource offsets to line/column positions.\n\nThe offset pairs go into an `Int32Array` which, like the indent cache, is created once per process\nand reused by every print - each `State` tracks how much of it that print has filled, and the buffer\ndoubles when a print fills it, so a steady-state print allocates nothing to record its mappings.\n\n`generateSourceMap` then walks the output once at the end, counting ECMAScript line terminators,\nbuilds the equivalent line table for `sourceText`, turns both sets of offsets into line/column,\nand encodes the mappings as base64 VLQ. Tracking lines and columns throughout would cost every write\nin every build.\n\n#### Potential future improvement\n\nProducing source maps is exactly the kind of raw number-crunching that native code excels at.\nAnd, unlike the AST, it's just small integers - the kind of data that transfers across the JS-native\nboundary cheaply.\n\nThe offsets are already assembled on the JS side in an `Int32Array`, so it might be worthwhile making\na single JS-Rust-JS round-trip with it, with the sourcemap generation (VLQ encoding etc) happening on Rust side.\n\nWASM might also be a good option for this.\n\n### No separate stack in the binary walk\n\n`printBinaryish` walks the left spine of a binary/logical chain iteratively. `a + b + c + d` nests as deep\nas it is long, so recursion would put the stack at the mercy of the input.\n\nRust does the same, but with an explicit `Stack` on `Codegen`. Here each level has a small visitor\nobject, and pending outer levels are threaded through its `parent` field rather than a separate stack array.\n\n### Lookup tables are `__proto__: null`\n\nSo a property miss cannot walk up to `Object.prototype`.\n\n## Where the ESTree AST differs\n\nThe two printers walk different ASTs, and some of the differences reach the output.\n\n### Node types\n\n- **One `Literal` node type** covers strings, numbers, booleans, `null`, regexes and bigints, where Rust\n  has six separate types. `printLiteral` tells them apart from `typeof value` and the presence of `regex` / `bigint`.\n- **`#x in obj` arrives as a `BinaryExpression`** with a `PrivateIdentifier` on the left, not as its own node type.\n  Hence the extra test in `printExpression`'s `BinaryExpression` arm.\n\n### Parenthesized function expressions\n\nOxc's Rust AST records that a function expression was parenthesized in a `pife` flag, and prints the parentheses\nback from it.\n\nESTree has no such flag. The information is only there when the AST was parsed with `preserveParens`,\nas a `ParenthesizedExpression` wrapper. `printExpression`'s arm for that node re-emits the parentheses\naround a function or arrow, and is transparent around anything else.\n\nThis is why `printBinaryish` prints its operands **unstripped**. `withoutParens` is used for\nthe structural type tests, but the node handed to `printExpression` keeps its wrapper,\nso that arm can run.\n\n### Things ESTree cannot express\n\n- `import \"m\";` versus `import {} from \"m\";`\n- An absent `with` clause versus an empty `with {}`\n- `assert {...}` versus `with {...}`\n- The span of a `with {...}` clause (only its individual attributes have ESTree locations)\n\nThe conformance harness normalizes the Rust AST down to what ESTree can express before printing,\nrather than expecting the JS side to reproduce information it was never given.\nSee `Normalize` in `tasks/codegen_conformance/src/lib.rs`.\n\n### Positions, and other producers\n\n- **Oxc offsets only.** `start` / `end` offsets are converted to UTF-16 line/column once at the end.\n  Source maps require `sourceText`; `loc` and `range` are not supported inputs.\n\n## How it is tested\n\n`oxc-codegen` is tested against all Test262, Acorn-JSX, and TypeScript test cases - about 62,000 fixtures.\n\nEvery fixture is printed three times:\n\n1. In Rust, with a source map, via the `oxc-codegen-conformance` NAPI addon (`tasks/codegen_conformance`).\n2. In JS, through the no-maps build.\n3. In JS, through the separately compiled maps build.\n\nAll three generated outputs must agree byte for byte. Every decoded generated/original line, column,\nand optional original name from the maps build must also agree with Rust, including ordering and\nduplicate suppression.\n\nBoth sides parse the same source text with the same `SourceType`, derived by the same function.\nFixtures which do not parse cleanly are skipped, rather than passing quietly.\n\nEvery fixture is checked in **both `preserveParens` modes**. They are different paths through the printer -\nwith the wrappers present it must decide what to re-emit, without them it must re-derive every parenthesis\nfrom precedence.\n\n## Rules to keep\n\nA checklist, all of it argued for above.\n\n1. **Never read a character of `state.output`.**\n2. **A `write` must declare a category which truthfully describes its final character**, and must never be\n   given an empty string.\n3. **Only use `writeNoLast` where `last` is provably dead.** A real write must follow before any reader runs.\n4. **Preserve the three numbering properties** when adding or renumbering a `CAT_*` code, and keep the\n   `if (DEBUG)` assertions which check them.\n5. **Never let a build-time flag add or remove a field on `State`.**\n6. **Do not add an arm near the top of `printExpression`, `printStatement` or other dispatch functions**\n   unless the node type really is that common.\n7. **Do not relax `const_functions` to `let`.**\n8. **Run the conformance suites against a debug build** (`pnpm run build-test`), so the assertions are live.\n9. **Benchmark against a release build** (`pnpm run bench` rebuilds one first). A debug build keeps\n   `debugAssert` calls and does not represent shipped performance.\n\n[`oxc_codegen`]: https://github.com/oxc-project/oxc/tree/main/crates/oxc_codegen\n[`print/categories.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/categories.ts\n[`print/write.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/write.ts\n[`print/space.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/space.ts\n[`print/operators.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/operators.ts\n[`print/types.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/types.ts\n","packages/codegen/README.md":"# oxc-codegen\n\nFast, synchronous code generation for JavaScript and TypeScript ASTs.\n\n`oxc-codegen` turns an [ESTree](https://github.com/estree/estree) or\n[TS-ESTree](https://typescript-eslint.io/packages/typescript-estree/) AST into formatted source\ncode. It supports JavaScript, JSX, TypeScript, and TSX.\n\nThe printer is a port of Oxc's Rust `oxc_codegen` crate. With the default options, both printers\nproduce byte-identical output: tab indentation, double-quoted strings, and no comments.\n\n## Installation\n\n```sh\nnpm install oxc-codegen\n```\n\n`oxc-codegen` is ESM-only and requires Node.js `^20.19.0` or `>=22.12.0`.\n\n## Quick start\n\nPair it with [`oxc-parser`](https://www.npmjs.com/package/oxc-parser) to parse and print source code:\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst { program } = parseSync(\"input.js\", \"const answer=6*7\");\nconst { code } = printSync(program);\n\nconsole.log(code);\n// const answer = 6 * 7;\n```\n\nYou can also print a manually constructed AST:\n\n```js\nconst program = {\n  type: \"Program\",\n  sourceType: \"script\",\n  body: [\n    {\n      type: \"ExpressionStatement\",\n      expression: {\n        type: \"CallExpression\",\n        callee: {\n          type: \"MemberExpression\",\n          object: { type: \"Identifier\", name: \"console\" },\n          property: { type: \"Identifier\", name: \"log\" },\n          computed: false,\n          optional: false,\n        },\n        arguments: [{ type: \"Literal\", value: \"Hello!\" }],\n        optional: false,\n      },\n    },\n  ],\n};\n\nconsole.log(printSync(program).code);\n// console.log(\"Hello!\");\n```\n\n### TypeScript and TSX\n\nSet `ts` when the AST can contain TypeScript nodes. For TSX, set both `ts` and `jsx`:\n\n```js\nconst { program } = parseSync(\"component.tsx\", \"const Box = <T,>(value: T) => <div>{value}</div>\");\n\nconst { code } = printSync(program, {\n  ts: true,\n  jsx: true,\n});\n```\n\n## API\n\n### `printSync(node, options?)`\n\n```ts\nfunction printSync(\n  node: ESTree.Program | ESTree.Statement,\n  options?: Options,\n): {\n  code: string;\n  map: SourceMap | null;\n};\n```\n\nPrints a complete `Program` or a single statement and returns the generated source code,\nand (when requested) a standard Source Map v3 object.\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst sourceText = \"const answer=6*7\";\nconst { program } = parseSync(\"input.js\", sourceText);\nconst { code, map } = printSync(program, {\n  sourcemap: true,\n  sourceFilename: \"input.js\",\n  sourceText,\n});\n```\n\nSource-map mappings require `sourceText` and nodes with valid Oxc `start` / `end` offsets.\nA manually constructed AST without offsets can still be printed, but its source map has\nan empty `mappings` string.\n\n### Options\n\n| Option                | Type      | Default | Description                                                      |\n| :-------------------- | :-------- | :------ | :--------------------------------------------------------------- |\n| `indent`              | `string`  | `\"\\t\"`  | Non-empty string of spaces and/or tabs used for one indent level |\n| `startingIndentLevel` | `number`  | `0`     | Starting indent level, from `0` to `1000`                        |\n| `jsx`                 | `boolean` | `false` | Enable TSX-safe printing for ambiguous TypeScript syntax         |\n| `ts`                  | `boolean` | `false` | Select the printer that supports TypeScript nodes                |\n| `sourcemap`           | `boolean` | `false` | Return a Source Map v3 object in `map`                           |\n| `sourceFilename`      | `string`  | `\"\"`    | Original source filename recorded in the source map              |\n| `sourceText`          | `string`  | -       | Original text required for source-map mappings and content       |\n\n## Why pure JavaScript?\n\nMost Oxc packages use native bindings. This package deliberately does not: when an AST already\nlives in JavaScript, passing the entire object graph across a JS/native boundary can cost more than\nprinting it in place. `oxc-codegen` avoids that serialization and uses specialized printer builds\nfor JavaScript and TypeScript workloads.\n\nSee [DESIGN.md](https://github.com/oxc-project/oxc/blob/main/packages/codegen/DESIGN.md) for the\nimplementation details and performance constraints.\n\n## Current limitations\n\n- Comments are not printed.\n- Minified output is not supported.\n\n## Benchmarks\n\nRepresentative time per `printSync` call:\n\n| Fixture                      |     Bytes |       Time |\n| :--------------------------- | --------: | ---------: |\n| `tiny.js`                    |        26 |  0.0001 ms |\n| `RadixUIAdoptionSection.jsx` |     2,518 |  0.0070 ms |\n| `react.development.js`       |    72,141 |  0.1518 ms |\n| `binder.ts`                  |   193,077 |  0.3364 ms |\n| `App.tsx`                    |   415,340 |  1.2912 ms |\n| `lodash.js`                  |   544,096 |  0.7977 ms |\n| `kitchen-sink.tsx`           |   732,222 |  4.2924 ms |\n| `antd.js`                    | 6,683,633 | 16.9652 ms |\n\nThese figures come from one machine and are illustrative, not a regression baseline.\nResults vary between runs, most noticeably for large fixtures such as `antd.js`.\n","packages/codegen/package.json":"# Codegen Skill\n\nPath: `packages/codegen/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/package.json)","packages/codegen/tsconfig.json":"# Codegen Skill\n\nPath: `packages/codegen/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/tsconfig.json)","packages/codegen/test/fixtures/README.md":"# fixtures Documentation\n\nPath: `packages/codegen/test/fixtures/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/test/fixtures/README.md)","apps/oxfmt/test/cli/extensions/0.snap.md":"# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/0.snap.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/0.snap.md)","apps/oxfmt/test/cli/extensions/options.json":"# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/options.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/options.json)","crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json)","crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json)","crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json":"# plugins Plugin Manifest\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json)","crates/oxc_linter/fixtures/extends_config/plugins/jest.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/jest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/jest.json)","crates/oxc_linter/fixtures/extends_config/plugins/parent.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent.json)","crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json)","crates/oxc_linter/fixtures/extends_config/plugins/react.json":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/react.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/react.json)","crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json":"# Nested-package Skill\n\nPath: `crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json)","apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json":"# Sub Skill\n\nPath: `apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json)",".cursorrules (typescript-expo-jest-detox-cursorrules-prompt-file.mdc)":"---\ndescription: \"Cursor rules for TypeScript development with Expo, Jest, and Detox integration.\"\nglobs: **/*\nalwaysApply: false\n---\nYou are an expert in TypeScript, React Native, Expo, and Mobile UI development.\n\nCode Style and Structure\n\nNaming Conventions\nTypeScript Usage\nSyntax and Formatting\nUI and Styling\nSafe Area Management\nPerformance Optimization\nNavigation\nState Management\nError Handling and Validation\nTesting\nSecurity\nInternationalization (i18n)\n\nKey Conventions\n\nAPI Documentation\n\nRefer to Expo's documentation for detailed information on Views, Blueprints, and Extensions for best practices.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md - AI Assistant Guide for Oxc\n\nOxc is a high-performance JavaScript/TypeScript toolchain written in Rust containing:\n\n- Parser (JS/TS with AST), Linter (oxlint), Formatter (oxfmt), Transformer, Minifier\n\n## AI Usage Policy for Contributors\n\n**IMPORTANT**: If you are an AI assistant helping a human contributor:\n\n- **Disclose AI usage** - Contributors must disclose when AI tools were used to reduce maintainer fatigue\n- **Full responsibility** - The human contributor is responsible for all AI-generated issues or PRs they submit\n- **Quality standards** - Low-quality or unreviewed AI content will be closed immediately\n\nAll AI-generated code must be thoroughly reviewed, tested, and understood by the contributor before submission. Code should meet Oxc's performance and quality standards.\n\n- **Ban policy** - Contributors who submit repeated low-quality (\"slop\") PRs will be banned without prior warning. Bans may be lifted if you commit to contributing to Oxc in accordance with the policy above. You may request an unban via our Discord.\n\n## Repository Structure\n\nRust workspace with key directories:\n\n- `crates/` - Core functionality (start here when exploring)\n- `apps/` - Application binaries (oxlint, oxfmt)\n- `napi/` - Node.js bindings\n- `npm/` - npm packages\n- `tasks/` - Development tools/automation\n\nAvoid editing `generated` subdirectories.\n\n### Core Crates\n\n- `oxc_parser` - JS/TS parser\n- `oxc_ast` - AST definitions/utilities\n- `oxc_semantic` - Semantic analysis/symbols/scopes\n- `oxc_linter` - Linting engine/rules\n- `oxc_formatter` - Code formatting (Prettier-like for JS)\n- `oxc_formatter_*` - Code formatting (Prettier-like for other languages)\n- `oxc_transformer` - Code transformation (Babel-like)\n- `oxc_minifier` - Code minification\n- `oxc_codegen` - Code generation\n- `oxc_isolated_declarations` - TypeScript declaration generation\n- `oxc_diagnostics` - Error reporting\n- `oxc_traverse` - AST traversal utilities\n- `oxc_allocator` - Memory management\n- `oxc_language_server` - LSP server for editor integration\n- `oxc` - Main crate\n\n## Development Commands\n\nPrerequisites: Rust (MSRV: 1.97), Node.js, pnpm, just\n\n**Setup Notes:**\n\n- All tools already installed (`cargo-insta`, `typos-cli`, `cargo-shear`, `ast-grep`)\n- Rust components already installed (`clippy`, `rust-docs`, `rustfmt`)\n- Use Conventional Commits for commit messages; `.github/workflows/pr.yml` requires a scoped title like `fix(parser): handle trailing comma`\n- Choose verification from the changed code; reserve `just ready` for an optional broad integration checkpoint as described below.\n- You run in an environment where `ast-grep` is available; whenever a search requires syntax-aware or structural matching, default to `ast-grep --lang rust -p '<pattern>'` (or set `--lang` appropriately) and avoid falling back to text-only tools like `rg` or `grep` unless I explicitly request a plain-text search.\n\n### Essential Commands\n\n```bash\njust fmt             # Format code (run after modifications)\njust test            # Run unit/integration tests\njust conformance     # Run conformance tests\njust ready           # Optional broad integration checkpoint (see below)\ncargo lintgen        # Regenerate linter rules enum and impls after adding/modifying rules\ncargo lint-timings   # Update linter timing data after changing linter rule codegen\n\n# Crate-specific updates\njust ast             # Update generated files (oxc_ast changes)\njust minsize         # Update size snapshots (oxc_minifier changes)\njust allocs          # Update allocation snapshots (oxc_parser changes)\n\n# Useful shortcuts\njust watch \"command\" # Watch files and re-run command\njust example tool    # Run tool example (e.g., just example linter)\n```\n\nMore commands can be found in `justfile`.\n\n### Local and stacked development\n\nBefore selecting verification checks, read the [verification guidance](.agents/verification.md).\n\n- Select checks from changed code and affected consumers; for a stack, use each PR's diff against its parent.\n- Reuse passing results while tested inputs remain unchanged. Reserve `just ready` for stable integration checkpoints when broad validation is needed.\n- Preserve required subsystem checks and per-PR CI; a passing stack tip does not establish that intermediate PRs pass.\n\n## Manual Testing & Examples\n\nRun crate examples for quick testing and debugging:\n\n```bash\ncargo run -p <crate_name> --example <example_name> -- [args]\n\n# Common examples:\ncargo run -p oxc_parser --example parser -- test.js\ncargo run -p oxc_linter --example linter -- src/\ncargo run -p oxc_transformer --example transformer -- input.js\ncargo run -p oxc --example compiler --features=\"full\" -- test.js\n```\n\nModify examples in `crates/<crate_name>/examples/` to test specific scenarios.\n\n## Code Navigation\n\n### Key Locations\n\n- AST: Start with `oxc_ast`, use `oxc_ast_visit` for traversal\n- Linting rules: `crates/oxc_linter/src/rules/` (visitor pattern)\n- Parser: `crates/oxc_parser/src/lib.rs`, lexer in `src/lexer/`\n- Tests: Co-located with source, integration in `tests/`, uses `insta` for snapshots\n\n### Conventions\n\n- Use `oxc_allocator` for memory management\n- Follow rustfmt config in `.rustfmt.toml`\n- Use `oxc_diagnostics` for errors with source locations\n- Performance-critical: avoid unnecessary allocations\n\n## Common Tasks\n\n### Adding Linting Rule\n\n1. Use rule generator: `just new-rule <name>` (ESLint rules)\n   - Or plugin-specific: `just new-ts-rule`, `just new-jest-rule`, etc.\n2. Implement using visitor pattern\n3. Add tests in same module\n4. Register in appropriate category\n\n### Parser Changes\n\n1. Research and test grammar changes thoroughly\n2. Update AST definitions in `oxc_ast` if needed\n3. Ensure existing tests pass\n4. Add tests for new features\n\n### Working with Transformations\n\n1. Understand AST structure first\n2. Use visitor pattern for traversal\n3. Handle node ownership/allocator carefully\n4. Test with various input patterns\n\n## Testing\n\nOxc uses multiple testing approaches tailored to each crate:\n\n- **Unit/Integration tests**: Standard Rust tests in `tests/` directories\n- **Conformance tests**: Against external suites (Test262, Babel, TypeScript)\n- **Snapshot tests**: Track failures and expected outputs using `insta`\n\n### Quick Test Commands\n\n```bash\njust test                              # Run all Rust tests\njust conformance                       # Run all conformance tests (alias: cargo coverage)\ncargo test -p <crate_name>             # Test specific crate\n\n# Conformance for specific tools\ncargo coverage parser                  # Parser conformance\ncargo coverage transformer             # Transformer conformance\ncargo run -p oxc_transform_conformance # Transformer Babel tests\n\n# NAPI packages\npnpm test                              # Test all Node.js bindings\n```\n\n### Crate-Specific Testing\n\nEach crate follows distinct testing patterns:\n\n#### oxc_parser\n\n- **Conformance only** via `tasks/coverage`\n- **Command**: `cargo coverage parser`\n- **Suites**: Test262, Babel, TypeScript\n- **Special**: `just allocs` for allocation tracking\n\n#### oxc_linter\n\n- **Inline tests** in rule files (`src/rules/**/*.rs`)\n- **Pattern**: Use `Tester` helper with pass/fail cases\n\n```rust\n#[test]\nfn test() {\n    Tester::new(RuleName::NAME, RuleName::PLUGIN, pass, fail)\n        .test_and_snapshot();\n}\n```\n\n#### oxc_formatter (and other `oxc_formatter_*` language crates)\n\n- Read the target crate's `AGENTS.md`; it includes the shared formatter policy plus language-specific rules and verification commands\n\n#### oxc_minifier\n\n- **Unit tests** in `tests/` subdirectories:\n  - `ecmascript/` - Operations\n  - `peephole/` - Optimizations\n  - `mangler/` - Name mangling\n- **Size tracking**: `just minsize`\n\n#### oxc_transformer\n\n- **Multiple approaches**:\n  - Unit tests: `tests/integrations/`\n  - Conformance: `tasks/transform_conformance/`\n  - Babel plugins: `tasks/transform_conformance/tests/babel-plugin-*/`\n- **Commands**:\n\n```bash\ncargo test -p oxc_transformer                    # Unit tests\ncargo run -p oxc_transform_conformance          # Conformance\njust test-transform --filter <path>             # Filter tests\n```\n\n#### oxc_codegen\n\n- **Integration tests** in `tests/integration/`\n- Test files: `js.rs`, `ts.rs`, `sourcemap.rs`, `comments.rs`\n\n#### oxc_isolated_declarations\n\n- **Snapshot testing** with `insta`\n- Input: `tests/fixtures/*.{ts,tsx}`\n- Output: `tests/snapshots/*.snap`\n- Update: `cargo insta review`\n\n#### oxc_semantic\n\n- **Multiple testing approaches**:\n  - **Conformance tests** (`tests/conformance/`) - Contract-as-code tests for symbols and identifier references\n  - **Integration tests** (`tests/integration/`) - Tests for scopes, symbols, modules, classes, CFG\n  - **Snapshot tests** (`tests/main.rs`) - Verifies scoping data correctness (scope trees, bindings, symbols, references) using `insta` snapshots from `fixtures/`\n  - **Coverage tests** - Via `tasks/coverage` using Test262, Babel, TypeScript suites\n- **Command**: `cargo test -p oxc_semantic`\n- **Update snapshots**: `cargo insta review`\n\n#### Other Crates\n\n- **oxc_traverse**: AST traversal - `cargo test -p oxc_traverse`\n- **oxc_ecmascript**: ECMAScript operations - `cargo test -p oxc_ecmascript`\n- **oxc_regular_expression**: Regex parsing - `cargo test -p oxc_regular_expression`\n- **oxc_syntax**: Syntax utilities - `cargo test -p oxc_syntax`\n- **oxc_language_server**: Editor integration - `cargo test -p oxc_language_server`\n\n### Conformance Testing Foundation\n\n**CRITICAL**: These external test suites are the CORE of Oxc's testing strategy, providing thousands of real-world test cases from mature JavaScript ecosystem projects. They ensure Oxc correctly handles the full complexity of JavaScript/TypeScript.\n\nSuites cloned via `just submodules`:\n\n| Submodule            | Description                                                                                                                                        | Location                            | Used by Crates                                           |\n| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------- |\n| `test262`            | **ECMAScript Conformance Suite**<br>Official JavaScript test suite from TC39, testing compliance with the ECMAScript specification                 | `tasks/coverage/test262`            | parser, semantic, codegen, transformer, minifier, estree |\n| `babel`              | **Babel Test Suite**<br>Comprehensive transformation and parsing tests from the Babel compiler, covering modern JavaScript features and edge cases | `tasks/coverage/babel`              | parser, semantic, codegen, transformer, minifier         |\n| `typescript`         | **TypeScript Test Suite**<br>Microsoft's TypeScript compiler tests, ensuring correct handling of TypeScript syntax and semantics                   | `tasks/coverage/typescript`         | parser, semantic, codegen, transformer, estree           |\n| `estree-conformance` | **ESTree Conformance Tests**<br>Test262, TypeScript, and acorn-jsx suites adapted for ESTree format validation, ensuring correct AST structure     | `tasks/coverage/estree-conformance` | estree                                                   |\n\n**These suites provide:**\n\n- **Thousands of battle-tested cases** from real-world usage\n- **Edge case coverage** that would be impossible to write manually\n- **Industry standard compliance** ensuring compatibility\n- **Continuous validation** against evolving JavaScript standards\n\nRun all conformance tests with `cargo coverage` or `just conformance`.\n\n### Searching Test Suites\n\nThese test suites are pre-cloned and ready to search:\n\n- **Test262** (`tasks/coverage/test262/`) - ECMAScript spec compliance\n- **Babel** (`tasks/coverage/babel/`) - Parsing and transformation edge cases\n- **TypeScript** (`tasks/coverage/typescript/`) - TypeScript syntax and semantics\n\nNOTE: These suites are script-cloned and fully gitignored. ripgrep respects `.gitignore`, so a plain `rg` inside them silently returns nothing.\nUse `rg --no-ignore` (or `-u`) when searching them.\n\n### Snapshot Testing\n\n- Uses `insta` crate for snapshot testing\n- Snapshots track **failing** tests, not passing ones\n- Located in `tasks/coverage/snapshots/` and conformance directories\n- Update with `cargo insta review` after changes\n- Formats: `.snap` (counts), `.snap.md` (detailed failures)\n\n### NAPI (Node.js Bindings) Testing\n\nNAPI packages use **Vitest** for testing Node.js bindings:\n\n```bash\npnpm run build-test   # Build all NAPI packages\npnpm test             # Run all NAPI tests\n```\n\n**Package-specific commands:**\n\n- `oxc-parser`: `cd napi/parser && pnpm run build-test && pnpm test` (also has `pnpm test-browser`)\n- `oxc-minify`: `cd napi/minify && pnpm run build-test && pnpm test`\n- `oxc-transform`: `cd napi/transform && pnpm run build-test && pnpm test`\n\nTests are TypeScript files in each package's `test/` directory.\n\n### Where to Add Tests\n\n| Crate                 | Location                                 |\n| --------------------- | ---------------------------------------- |\n| Parser                | `tasks/coverage/misc/pass/` or `fail/`   |\n| Linter                | Inline in rule files                     |\n| Formatter             | `tests/fixtures/` in the formatter crate |\n| Minifier              | `tests/` subdirectories                  |\n| Transformer           | `tests/integrations/` or Babel fixtures  |\n| Codegen               | `tests/integration/`                     |\n| Isolated Declarations | `tests/fixtures/*.ts`                    |\n| Semantic              | `tests/` directory                       |\n| NAPI packages         | `test/` directory (Vitest)               |\n| Language Server       | Inline and `/fixtures`                   |\n\n### Linter Codegen Changes\n\nWhen changing linter codegen files such as `tasks/linter_codegen/**` or generated linter rule runner files under `crates/oxc_linter/src/generated/`, run `cargo lintgen` and `cargo lint-timings` so generated rule metadata and linter timing data stay in sync.\n\n## Notes\n\n- Rapidly evolving project - APIs may change\n- Performance is critical for all changes\n- Maintain JS/TS standard compatibility\n- Breaking changes need documentation and discussion\n\n---\n\nFor human contributors see `CONTRIBUTING.md` and [oxc.rs](https://oxc.rs/docs/contribute/introduction.html)\n","isInternal":false,"tokens":3304,"sizeBytes":14545},{"name":"ARCHITECTURE.md","path":"ARCHITECTURE.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/ARCHITECTURE.md","title":"System Architecture & Module Boundaries","category":"project-spec","format":"markdown","content":"# Architecture\n\n## System Overview\n\nOxc (The Oxidation Compiler) is a collection of high-performance JavaScript and TypeScript tools written in Rust. The system is designed as a modular, composable set of compiler components that can be used independently or together to build complete toolchains for JavaScript/TypeScript development.\n\n### Core Mission\n\n- **Performance**: Deliver faster performance than existing JavaScript tools\n- **Correctness**: Maintain compatibility with JavaScript/TypeScript standards\n- **Modularity**: Enable users to compose tools according to their specific needs\n- **Developer Experience**: Provide excellent error messages and tooling integration\n\n## High-Level Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                          Applications                           │\n├─────────────────────────────────────────────────────────────────┤\n│  oxlint  │  Language Server  │  NAPI Bindings  │  Future Tools  │\n├─────────────────────────────────────────────────────────────────┤\n│                        Core Libraries                           │\n├─────────────────────────────────────────────────────────────────┤\n│ Parser │ Semantic │ Linter │ Transformer │ Minifier │ Codegen   │\n├─────────────────────────────────────────────────────────────────┤\n│                    Foundation Libraries                         │\n├─────────────────────────────────────────────────────────────────┤\n│    AST    │  Allocator  │  Diagnostics  │   Span   │  Syntax    │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Architecture Principles\n\n### 1. Zero-Copy Architecture\n\nThe system is built around an arena allocator (`oxc_allocator`) that enables zero-copy operations throughout the compilation pipeline. All AST nodes are allocated in a single arena, eliminating the need for reference counting or garbage collection.\n\n### 2. Visitor Pattern\n\nAST traversal is implemented using the visitor pattern (`oxc_ast_visit`) with automatic visitor generation through procedural macros. This ensures type safety and performance while maintaining code clarity.\n\n### 3. Shared Infrastructure\n\nCommon functionality like error reporting (`oxc_diagnostics`), source positions (`oxc_span`), and syntax definitions (`oxc_syntax`) are shared across all components to ensure consistency.\n\n## Core Components\n\n### Foundation Layer\n\n#### oxc_allocator\n\n- **Purpose**: Arena-based memory allocator for zero-copy operations\n- **Key Features**:\n  - Single allocation arena for entire compilation unit\n  - Eliminates need for Rc/Arc in hot paths\n  - Enables structural sharing of AST nodes\n- **Dependencies**: None (foundational)\n\n#### oxc_span\n\n- **Purpose**: Source position tracking and text manipulation\n- **Key Features**:\n  - Byte-based indexing for UTF-8 correctness\n  - Efficient span operations for source maps\n  - Integration with diagnostic reporting\n- **Dependencies**: None (foundational)\n\n#### oxc_syntax\n\n- **Purpose**: JavaScript/TypeScript language definitions\n- **Key Features**:\n  - Token definitions and keyword mappings\n  - Language feature flags and compatibility\n  - Shared syntax validation logic\n- **Dependencies**: oxc_span\n\n#### oxc_diagnostics\n\n- **Purpose**: Error reporting and diagnostic infrastructure\n- **Key Features**:\n  - Rich error messages with source context\n  - Multiple output formats (JSON, pretty-printed)\n  - Integration with language server protocol\n- **Dependencies**: oxc_span\n\n#### oxc_ast\n\n- **Purpose**: Abstract Syntax Tree definitions and utilities\n- **Key Features**:\n  - Complete JavaScript/TypeScript AST coverage\n  - Generated visitor traits for type safety\n  - Serialization support for caching\n- **Dependencies**: oxc_allocator, oxc_span, oxc_syntax\n\n##### AST Design Principles\n\nThe Oxc AST differs significantly from the [estree](https://github.com/estree/estree) AST specification by removing ambiguous nodes and introducing distinct types. While many existing JavaScript tools rely on estree as their AST specification, a notable drawback is its abundance of ambiguous nodes that often leads to confusion during development.\n\nFor example, instead of using a generic estree `Identifier`, the Oxc AST provides specific types such as:\n\n- `BindingIdentifier` - for variable declarations and bindings\n- `IdentifierReference` - for variable references\n- `IdentifierName` - for property names and labels\n\nThis clear distinction greatly enhances the development experience by aligning more closely with the ECMAScript specification and providing better type safety.\n\n### Core Processing Layer\n\n#### oxc_parser\n\n- **Purpose**: JavaScript/TypeScript parsing\n- **Key Features**:\n  - Hand-written recursive descent parser\n  - Full ES2024+ and TypeScript support\n  - Preservation of comments and trivia\n- **Dependencies**: oxc_allocator, oxc_ast, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_semantic\n\n- **Purpose**: Semantic analysis and symbol resolution\n- **Key Features**:\n  - Scope chain construction\n  - Symbol table generation\n  - Dead code detection\n- **Dependencies**: oxc_ast, oxc_cfg, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_linter\n\n- **Purpose**: ESLint-compatible linting engine\n- **Key Features**:\n  - 200+ built-in rules\n  - Plugin architecture for custom rules\n  - Automatic fixing for many rules\n  - Configuration compatibility with ESLint\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_diagnostics, oxc_cfg\n\n#### oxc_transformer\n\n- **Purpose**: Code transformation and transpilation\n- **Key Features**:\n  - TypeScript to JavaScript transformation\n  - Modern JavaScript feature transpilation\n  - React JSX transformation\n  - Babel plugin compatibility layer\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_allocator\n\n#### oxc_minifier\n\n- **Purpose**: Code size optimization\n- **Key Features**:\n  - Dead code elimination\n  - Constant folding and propagation\n  - Identifier mangling integration\n  - Statement and expression optimization\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_mangler\n\n#### oxc_codegen\n\n- **Purpose**: AST to source code generation\n- **Key Features**:\n  - Configurable output formatting\n  - Source map generation\n  - Comment preservation options\n  - Minified and pretty-printed output modes\n- **Dependencies**: oxc_ast, oxc_span\n\n### Application Layer\n\n#### oxlint (apps/oxlint)\n\n- **Purpose**: Command-line linter application\n- **Key Features**:\n  - File discovery and parallel processing\n  - Configuration file support\n  - Multiple output formats\n  - Integration with CI/CD systems\n- **Dependencies**: oxc_linter, oxc_parser, oxc_semantic\n\n#### Language Server (oxc_language_server)\n\n- **Purpose**: LSP implementation for editor integration\n- **Key Features**:\n  - Real-time diagnostics\n  - Go-to-definition and references\n  - Symbol search and completion\n- **Dependencies**: All core components\n\n#### NAPI Bindings (napi/\\*)\n\n- **Purpose**: Node.js integration layer\n- **Key Features**:\n  - Parser bindings for JavaScript tooling\n  - Linter integration for build tools\n  - Transform pipeline for bundlers\n  - Async processing support\n- **Dependencies**: Core components + Node.js FFI\n\n## Data Flow\n\n### Compilation Pipeline\n\n1. **Input**: Source text + configuration\n2. **Lexing/Parsing**: `oxc_parser` → AST + comments\n3. **Semantic Analysis**: `oxc_semantic` → Symbol table + scope info\n4. **Processing**: Tool-specific analysis (linting, transformation, etc.)\n5. **Output**: Results (diagnostics, transformed code, etc.)\n\n### Memory Management Flow\n\n```\nSource Text → Arena Allocator → AST Nodes → Visitors → Results\n     ↓              ↓              ↓           ↓          ↓\n   UTF-8          Arena         Borrowed    Zero-copy   Owned\n  String         Memory         References  Processing  Output\n```\n\n## Quality Attributes\n\n### Performance\n\n- **Target**: 10-100x faster than comparable tools\n- **Strategies**:\n  - Arena allocation for memory efficiency\n  - Zero-copy data structures\n  - Parallel processing where possible\n  - Minimal allocations in hot paths\n\n#### Parser Performance Implementation\n\n- AST is allocated in a memory arena (oxc_allocator) for fast AST memory allocation and deallocation\n- Short strings are inlined by [CompactString](https://crates.io/crates/compact_str)\n- No other heap allocations are done except the above two\n- Scope binding, symbol resolution and some syntax errors are not done in the parser, they are delegated to the semantic analyzer\n\n#### Linter Performance Implementation\n\n- Oxc parser is used for optimal performance\n- AST visit is a fast operation due to linear memory scan from the memory arena\n- Files are linted in a multi-threaded environment, so scales with the total number of CPU cores\n- Every single lint rule is tuned for performance\n\n### Correctness\n\n- **Target**: 100% compatibility with language standards\n- **Strategies**:\n  - Comprehensive test suites\n  - Real-world codebase testing\n  - Conformance testing against official specs\n  - Conservative error handling\n\n### Maintainability\n\n- **Target**: Clear, reviewable, extensible codebase\n- **Strategies**:\n  - Strong type system usage\n  - Procedural macro code generation\n  - Clear separation of concerns\n  - Comprehensive documentation\n\n### Usability\n\n- **Target**: Drop-in replacement for existing tools\n- **Strategies**:\n  - Configuration compatibility\n  - Familiar CLI interfaces\n  - Rich error messages\n  - Editor integration\n\n## Technical Constraints\n\n### Language Choice\n\n- **Rust**: Chosen for memory safety, performance, and zero-cost abstractions\n- **MSRV**: N-2 policy for stability\n\n### Memory Model\n\n- **Arena Allocation**: Single arena per compilation unit\n- **Lifetime Management**: Explicit lifetimes tied to arena\n- **No Garbage Collection**: Manual memory management for predictable performance\n\n### Threading Model\n\n- **File-level Parallelism**: Multiple files processed in parallel\n- **Single-threaded Pipeline**: Each file processed by single thread\n- **Shared State**: Minimal shared state to avoid synchronization overhead\n\n### Compatibility Requirements\n\n- **JavaScript**: ES2024+ compatibility\n- **TypeScript**: Latest TypeScript syntax support\n- **Node.js**: LTS versions through NAPI bindings\n- **Editors**: LSP compatibility for all major editors\n\n## Design Decisions\n\n### Arena Allocator Choice\n\n**Decision**: Use custom arena allocator instead of Rc/Arc\n**Rationale**:\n\n- Eliminates reference counting overhead\n- Enables zero-copy string operations\n- Simplifies memory management\n- Improves cache locality\n\n**Trade-offs**:\n\n- ✅ 10-50% performance improvement\n- ✅ Simplified ownership model\n- ❌ Requires lifetime management\n- ❌ Less flexible memory patterns\n\n### Hand-written Parser\n\n**Decision**: Implement recursive descent parser instead of parser generator\n**Rationale**:\n\n- Easier debugging and maintenance\n- More efficient generated code\n- Faster compilation times\n\n**Trade-offs**:\n\n- ✅ Better performance and error messages\n- ✅ More maintainable code\n- ❌ More manual implementation work\n- ❌ Higher risk of parser bugs\n\n### Visitor Pattern\n\n**Decision**: Use visitor pattern with procedural macros\n**Rationale**:\n\n- Type-safe AST traversal\n- Automatic visitor generation\n- Consistent patterns across tools\n- Efficient dispatch\n\n**Trade-offs**:\n\n- ✅ Type safety and performance\n- ✅ Reduced boilerplate code\n- ❌ Compile-time complexity\n- ❌ Learning curve for contributors\n\n## Future Considerations\n\n### Planned Extensions\n\n- **Formatter**: Complete code formatting tool\n- **Bundler**: Integration with bundling workflows\n- **Type Checker**: Full TypeScript type checking\n- **Plugin System**: User-defined transformations\n\n### Scalability Concerns\n\n- **Large Codebases**: Processing optimization improvements\n- **Memory Usage**: Streaming processing for huge files\n- **Parallel Processing**: Fine-grained parallelization\n\n### Technology Evolution\n\n- **Rust Evolution**: Leveraging new language features\n- **JavaScript Standards**: Keeping pace with TC39 proposals\n- **Editor Integration**: Advanced IDE features\n\n## Development Infrastructure\n\n### Test Infrastructure\n\nCorrectness and reliability are taken extremely seriously in Oxc. We spend significant effort on strengthening the test infrastructure to prevent problems from propagating to downstream tools:\n\n- **Conformance Testing**: Test262, Babel, and TypeScript conformance suites\n- **Fuzzing**: Extensive fuzzing to discover edge cases\n- **Snapshot Testing**: Linter diagnostic snapshots for regression prevention\n- **Ecosystem CI**: Testing against real-world codebases\n- **Idempotency Testing**: Ensuring transformations are stable\n- **Code Coverage**: Comprehensive coverage tracking\n- **End-to-End Testing**: Testing against top 3000 npm packages\n\n### Build and Development Tools\n\n- **Rust**: MSRV 1.86.0+ with clippy and rustfmt integration\n- **Just**: Command runner for development tasks (`just --list` for available commands)\n- **Performance Monitoring**: Continuous benchmarking and performance regression detection\n- **Cross-platform**: Support for Linux, macOS, and Windows\n- **CI/CD**: Automated testing, building, and publishing pipelines\n\nFor detailed development guidelines, see [CONTRIBUTING.md](./CONTRIBUTING.md) and [AGENTS.md](./AGENTS.md).\n\n---\n\nThis architecture document follows the [architecture.md](https://architecture.md/) format for documenting software architecture decisions and system design.\n","isInternal":false,"tokens":2862,"sizeBytes":14431},{"name":"SKILL.md","path":".agents/skills/insta-snapshots/SKILL.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/.agents/skills/insta-snapshots/SKILL.md","title":"insta-snapshots","category":"anthropic-skill","format":"markdown","content":"---\nname: insta-snapshots\ndescription: Review and update Oxc Insta snapshots non-interactively after expected output changes or snapshot test failures.\n---\n\n# Insta snapshots\n\nRun the affected test to generate pending snapshots. Use `cargo insta pending-snapshots` and read the old `.snap` and new `.snap.new` outputs to decide whether each change matches the intended behavior.\n\nUse the CLI instead of the interactive `cargo insta review` UI or manually editing expected output:\n\n```bash\ncargo insta pending-snapshots\ncargo insta accept --snapshot <snapshot_name>\n```\n\nScope acceptance to the reviewed snapshots from this task; check `cargo insta accept --help` if a name or package filter is ambiguous. Avoid workspace-wide acceptance or rejection when unrelated pending snapshots exist. Fix unexpected output in the implementation before accepting it.\n\nAfter accepting, inspect the snapshot diff and rerun the affected test. Finish with the intended `.snap` updates and no task-generated `.snap.new` files left pending. Updating snapshots does not itself call for a commit.\n\nSnapshots record expected output, including successful output in some crates. Linter diagnostic snapshots are under `crates/oxc_linter/src/snapshots/`; other crates have their own locations. Conformance `.snap.md` reports use a separate update mechanism; inspect the producing test harness rather than treating them as Insta pending snapshots.\n","frontmatter":{"name":"insta-snapshots","description":"Review and update Oxc Insta snapshots non-interactively after expected output changes or snapshot test failures."},"isInternal":false,"tokens":285,"sizeBytes":1422},{"name":"SKILL.md","path":".agents/skills/migrate-oxfmt/SKILL.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/.agents/skills/migrate-oxfmt/SKILL.md","title":"migrate-oxfmt","category":"anthropic-skill","format":"markdown","content":"---\nname: migrate-oxfmt\ndescription: Guide for migrating a project from Prettier or Biome to Oxfmt. Use when asked to migrate, convert, or switch a project's formatter from Prettier or Biome to Oxfmt.\n---\n\nThis skill guides you through migrating a project from Prettier or Biome to [Oxfmt](https://oxc.rs/docs/guide/usage/formatter).\n\n## Overview\n\nOxfmt is a high-performance, Prettier-compatible code formatter. Most Prettier options are supported directly.\n\nAn automated migration tool is built into Oxfmt, supporting both Prettier and Biome as migration sources.\n\n## Step 1: Run Automated Migration\n\nFirst, decide whether the automated tool fits:\n\n- Static config (JSON, or a JS/TS config that just exports a plain object): use `--migrate` below.\n- Dynamic JS/TS config (logic such as environment branches or computed values): `--migrate` writes only a resolved snapshot of the values, and the logic does not survive (a warning is printed). Port the logic to `oxfmt.config.ts` manually with the option mappings in this guide.\n- Nested per-directory configs: `--migrate` only handles the config in the current directory. Run it again in each nested directory (see \"Nested Config\" in Step 2).\n\n### From Prettier\n\n```bash\nnpx oxfmt@latest --migrate prettier\n```\n\nThis will:\n\n- Find your Prettier config in the current directory, in the same order as Prettier\n  - Supported: `package.json` (`prettier` field), `.prettierrc` (JSON only), `.prettierrc.json`, and JS/TS configs (`.prettierrc.*`, `prettier.config.*`)\n  - Shareable configs (e.g. `\"prettier\": \"@company/prettier-config\"`) are resolved\n  - YAML, JSON5 and TOML configs are not supported. Convert them to JSON or JS first\n- Create `.oxfmtrc.json` with migrated options\n  - Only Prettier-compatible options are migrated, others are skipped with a warning\n- Migrate `overrides`\n  - `overrides[].options.parser` is migrated to `associations[].language`\n- Migrate `.prettierignore` patterns to `ignorePatterns`\n- Migrate `prettier-plugin-tailwindcss` options to `sortTailwindcss`\n- Migrate `prettier-plugin-svelte` options to `svelte` (`svelteSortOrder` → `sortOrder`, `svelteAllowShorthand` → `allowShorthand`, `svelteIndentScriptAndStyle` → `indentScriptAndStyle`)\n- Migrate `prettier-plugin-astro` options to `astro` (`astroAllowShorthand` → `allowShorthand`, `astroSkipFrontmatter` → `skipFrontmatter`, `astroCompressHTML` → `compressHTML`)\n- Detect `prettier-plugin-packagejson` and enable `sortPackageJson`\n- Skip other plugins with a warning\n\nNotes (Prettier):\n\n- Fails if no Prettier config is found in the current directory. Use `--init` to create a blank `.oxfmtrc.json` instead.\n- The generated `.oxfmtrc.json` may not be formatted yet. Run `oxfmt` after migration.\n\n### From Biome\n\n```bash\nnpx oxfmt@latest --migrate biome\n```\n\nThis will:\n\n- Find and read `biome.json` or `biome.jsonc`\n- Create `.oxfmtrc.json` with migrated options\n- Migrate negated patterns from `files.includes` to `ignorePatterns`\n- Map Biome's two-level config (`formatter.*` and `javascript.formatter.*`) to Oxfmt options\n\nBiome option mapping:\n\n| Biome                                                       | Oxfmt                             |\n| ----------------------------------------------------------- | --------------------------------- |\n| `formatter.indentStyle` (`\"tab\"`/`\"space\"`)                 | `useTabs` (`true`/`false`)        |\n| `formatter.indentWidth`                                     | `tabWidth`                        |\n| `formatter.lineWidth`                                       | `printWidth`                      |\n| `javascript.formatter.quoteStyle`                           | `singleQuote`                     |\n| `javascript.formatter.jsxQuoteStyle`                        | `jsxSingleQuote`                  |\n| `javascript.formatter.quoteProperties` (`\"asNeeded\"`)       | `quoteProps` (`\"as-needed\"`)      |\n| `javascript.formatter.trailingCommas`                       | `trailingComma`                   |\n| `javascript.formatter.semicolons` (`\"always\"`/`\"asNeeded\"`) | `semi` (`true`/`false`)           |\n| `javascript.formatter.arrowParentheses` (`\"asNeeded\"`)      | `arrowParens` (`\"avoid\"`)         |\n| `javascript.formatter.bracketSameLine`                      | `bracketSameLine`                 |\n| `formatter.bracketSpacing`                                  | `bracketSpacing`                  |\n| `formatter.attributePosition` (`\"multiline\"`)               | `singleAttributePerLine` (`true`) |\n\nNotes (Biome):\n\n- For `formatter.*` options in the table, a `javascript.formatter.*` value of the same name takes precedence when present. `bracketSameLine` is read only from `javascript.formatter.bracketSameLine`.\n- Options not set in `biome.json` are written to `.oxfmtrc.json` with Biome's default values explicitly (e.g. `printWidth: 80`, `useTabs: true`), so the output preserves Biome's formatting behavior.\n- If no Biome config is found, creates a blank `.oxfmtrc.json` instead.\n- `overrides` cannot be auto-migrated and must be converted manually (a warning is printed if detected).\n\nNotes (both sources):\n\n- Fails if an Oxfmt config (`.oxfmtrc.json`, `.oxfmtrc.jsonc`, `oxfmt.config.ts` or `oxfmt.config.mts`) already exists. Delete it first if you want to re-run.\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxfmtrc.json` for these key differences:\n\n### printWidth\n\nPrettier and Biome default is 80, Oxfmt default is 100. The migration tool sets `printWidth: 80` if not specified in your source config. Decide whether to keep 80 or adopt 100.\n\n### Unsupported Options (Prettier only)\n\nAny option not in \"Prettier-Compatible Options\" below is skipped with a warning, including options of unsupported plugins (e.g. `importOrder`). Notable ones:\n\n| Option                          | Status                                                                    |\n| ------------------------------- | ------------------------------------------------------------------------- |\n| `endOfLine: \"auto\"`             | Not supported. Use `\"lf\"` or `\"crlf\"` explicitly                          |\n| `experimentalTernaries`         | Not supported                                                             |\n| `requirePragma`, `insertPragma` | Not supported                                                             |\n| `parser`                        | Skipped at top-level. In `overrides`, migrated to `associations` if known |\n| `plugins`                       | Only known plugins are migrated (see Step 1)                              |\n\nRegex values (e.g. `\"/^my-/\"`) in `tailwindFunctions` / `tailwindAttributes` are also skipped with a warning, Oxfmt only supports literal strings there.\n\n### sortPackageJson (Prettier only)\n\nEnabled by default in Oxfmt, but the migration tool disables it unless `prettier-plugin-packagejson` was detected. Review whether you want this enabled.\n\nNote: Oxfmt's sorting algorithm differs from `prettier-plugin-packagejson`.\n\n### embeddedLanguageFormatting (Prettier only)\n\nEmbedded language formatting (e.g., CSS-in-JS) generally works.\n\n### overrides\n\nPrettier `overrides` are auto-migrated. Unsupported options in them are skipped with a warning, so review the result.\n\n`overrides[].options.parser` is migrated to `associations`, which tells Oxfmt which language to use for matched files:\n\n```json\n{\n  \"associations\": [{ \"files\": [\"*.svg\"], \"language\": \"html\" }]\n}\n```\n\n`language` is a Shiki language id (as in Markdown code fences), not a Prettier parser name (e.g. `angular-html` instead of `angular`, `typescript` instead of `babel-ts`).\n\nBiome `overrides` cannot be auto-migrated. Convert manually:\n\n```json\n{\n  \"overrides\": [\n    {\n      \"files\": [\"*.md\"],\n      \"excludeFiles\": [\"CHANGELOG.md\"],\n      \"options\": { \"tabWidth\": 4 }\n    }\n  ]\n}\n```\n\n(`excludeFiles` is optional.)\n\n### Nested Config\n\nOxfmt supports nested configuration files: a config in a subdirectory applies to files under that directory. Pass `--disable-nested-config` to opt out and use only the root config.\n\nHowever, `--migrate` only migrates the config in the current directory, and does not walk subdirectories. If the project has per-directory Prettier or Biome configs, run `--migrate` in each of those directories.\n\n### Prettier-Compatible Options\n\nThese options transfer directly with the same behavior:\n`printWidth`, `tabWidth`, `useTabs`, `semi`, `singleQuote`, `jsxSingleQuote`, `quoteProps`, `trailingComma`, `arrowParens`, `bracketSpacing`, `bracketSameLine`, `objectWrap`, `experimentalOperatorPosition`, `endOfLine`, `proseWrap`, `htmlWhitespaceSensitivity`, `singleAttributePerLine`, `vueIndentScriptAndStyle`, `embeddedLanguageFormatting`\n\n## Step 3: Configure Oxfmt Extensions\n\nOxfmt offers features not available in Prettier:\n\n### sortImports\n\nSort import statements, inspired by `eslint-plugin-perfectionist/sort-imports` (disabled by default):\n\n```json\n{\n  \"sortImports\": {\n    \"partitionByNewline\": true,\n    \"newlinesBetween\": false\n  }\n}\n```\n\nOther options: `partitionByComment`, `sortSideEffects`, `order`, `ignoreCase`, `internalPattern`, `groups`, `customGroups`.\n\n### sortTailwindcss\n\nReplaces `prettier-plugin-tailwindcss`. Auto-migrated with renamed options:\n\n| Prettier (top-level)         | Oxfmt (`sortTailwindcss.*`) |\n| ---------------------------- | --------------------------- |\n| `tailwindConfig`             | `config`                    |\n| `tailwindStylesheet`         | `stylesheet`                |\n| `tailwindFunctions`          | `functions`                 |\n| `tailwindAttributes`         | `attributes`                |\n| `tailwindPreserveWhitespace` | `preserveWhitespace`        |\n| `tailwindPreserveDuplicates` | `preserveDuplicates`        |\n\n### Other Extensions\n\n| Option               | Default  | Description                                                                     |\n| -------------------- | -------- | ------------------------------------------------------------------------------- |\n| `insertFinalNewline` | `true`   | Whether to add a final newline at end of file                                   |\n| `sortPackageJson`    | `true`   | Sort `package.json` keys. Set `{ \"sortScripts\": true }` to also sort scripts    |\n| `jsdoc`              | disabled | Format JSDoc comments. Set `true` or an options object for fine-grained control |\n| `svelte`             | disabled | Svelte formatting options, replacing `prettier-plugin-svelte` (auto-migrated)   |\n| `astro`              | disabled | Astro formatting options, replacing `prettier-plugin-astro` (auto-migrated)     |\n| `associations`       | `[]`     | Format matched files as the given language (e.g. `*.svg` as `html`)             |\n\n## Step 4: Update CI and Scripts\n\nReplace formatter commands with Oxfmt:\n\n```bash\n# Before (Prettier)\nnpx prettier --write .\nnpx prettier --check .\n\n# Before (Biome)\nnpx biome format --write .\nnpx biome check .\n\n# After\nnpx oxfmt@latest\nnpx oxfmt@latest --check\n```\n\n### Common CLI Options\n\n| Prettier / Biome                                | Oxfmt                                        |\n| ----------------------------------------------- | -------------------------------------------- |\n| `prettier --write .` / `biome format --write .` | `oxfmt` (default: cwd, `--write` mode)       |\n| `prettier --check .` / `biome check .`          | `oxfmt --check`                              |\n| `prettier --list-different .`                   | `oxfmt --list-different`                     |\n| `prettier --config path`                        | `oxfmt --config path`                        |\n| `prettier --ignore-path .prettierignore`        | `oxfmt --ignore-path .prettierignore`        |\n| `cat file \\| prettier --stdin-filepath=file.ts` | `cat file \\| oxfmt --stdin-filepath=file.ts` |\n\n### File Type Coverage\n\n- JS/TS, JSON/JSONC/JSON5, CSS/SCSS/Less, GraphQL, YAML, Markdown, TOML: Formatted natively by Oxfmt\n- HTML, Vue, Svelte, Astro, etc.: Delegated to Prettier internally (when using `npx oxfmt`)\n\n## Tips\n\n- Config file: `.oxfmtrc.jsonc` and `oxfmt.config.ts` are also supported as auto-discovered config file names, in addition to `.oxfmtrc.json`. `--migrate` and `--init` only generate `.oxfmtrc.json`; keep it as-is unless the user explicitly asks for another format or the source config had logic to preserve (see Step 1).\n- EditorConfig: Oxfmt reads `.editorconfig` automatically for `useTabs`, `tabWidth`, `endOfLine`, `insertFinalNewline`, `printWidth`, and `singleQuote`. Options in `.oxfmtrc.json` take precedence.\n  - Nearest one only, does not respect `root`\n- CI: Use `npx oxfmt@latest --check` to enforce formatting in CI.\n- LSP: Run `oxfmt --lsp` for editor integration via Language Server Protocol.\n- Schema support: Add `\"$schema\": \"./node_modules/oxfmt/configuration_schema.json\"` to `.oxfmtrc.json` for editor autocompletion.\n- Init: Run `npx oxfmt@latest --init` to create a default `.oxfmtrc.json` without migration.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/formatter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/formatter/config-file-reference.html)\n- [Unsupported Features](https://oxc.rs/docs/guide/usage/formatter/unsupported-features.html)\n","frontmatter":{"name":"migrate-oxfmt","description":"Guide for migrating a project from Prettier or Biome to Oxfmt. Use when asked to migrate, convert, or switch a project's formatter from Prettier or Biome to Oxfmt."},"isInternal":false,"tokens":3188,"sizeBytes":13226},{"name":"SKILL.md","path":".agents/skills/migrate-oxlint/SKILL.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/.agents/skills/migrate-oxlint/SKILL.md","title":"migrate-oxlint","category":"anthropic-skill","format":"markdown","content":"---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                         | Oxlint Plugin Name |\n| ----------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                    | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks`   | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`     | `import`           |\n| `eslint-plugin-unicorn`                               | `unicorn`          |\n| `eslint-plugin-jsx-a11y` / `eslint-plugin-jsx-a11y-x` | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                            | `react-perf`       |\n| `eslint-plugin-promise`                               | `promise`          |\n| `eslint-plugin-jest`                                  | `jest`             |\n| `@vitest/eslint-plugin`                               | `vitest`           |\n| `eslint-plugin-jsdoc`                                 | `jsdoc`            |\n| `eslint-plugin-next`                                  | `nextjs`           |\n| `eslint-plugin-node`                                  | `node`             |\n| `eslint-plugin-vue`                                   | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n","frontmatter":{"name":"migrate-oxlint","description":"Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint."},"isInternal":false,"tokens":2176,"sizeBytes":9941},{"name":"SKILL.md","path":".agents/skills/performance-lint-rules/SKILL.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/.agents/skills/performance-lint-rules/SKILL.md","title":"performance-lint-rules","category":"anthropic-skill","format":"markdown","content":"---\nname: performance-lint-rules\ndescription: Performance review guidance for Oxc linter rule implementations. Use only when reviewing Rust rule code under crates/oxc_linter/src/rules/ or when explicitly auditing those rules for performance improvements.\n---\n\n## Performance Guidelines\n\n### Prefer top-level node kind checks\n\nPut node kind checks at the rule entry point. If a rule only handles a few syntactic forms, start `run` with an `AstKind` match and return for all other nodes, even when a helper filters again internally. This lets lintgen derive narrower `NODE_TYPES` and avoids dispatching the rule on unrelated AST nodes.\n\nAfter changing the relevant node kinds for a rule, regenerate the rule runner with `cargo lintgen` and consider adding or updating `assert_rule_runs_on_node_types` coverage in `crates/oxc_linter/src/rule.rs`.\n\nImplement only the needed entry point. If a rule is a whole-file pass over semantic indexes, use `run_once` by itself. Implementing both `run` and `run_once` prevents useful node-type narrowing.\n\n### Do cheaper checks first\n\nOrder checks from cheapest and most selective to most expensive. Return quickly for common non-matches before doing semantic lookups, allocations, or deeper traversal.\n\n- Matching a small fixed string set with `matches!` before semantic checks.\n- Rejecting lowercase identifiers before global-object checks when only constructors can match.\n- Checking whether a JSX attribute starts with `aria-` before lowercasing it.\n- Checking for required syntax such as a `key` prop before looking up callback parameter symbols.\n- Checking `source_range(span).contains(\"this\")` before running a visitor that only finds `this`.\n\n### Delay expensive context\n\nMost files do not contain lint errors. Do not prepare diagnostics, labels, help text, fix data, ancestors, symbols, JSX element types, or replacement strings until the rule has found a syntactic candidate that could actually report.\n\n### Iterate over the smallest set possible\n\n- Use `run_once` when the rule only needs a whole-file pass and does not need to run on every node.\n- Iterate over symbols instead of AST nodes when looking for references to specific names.\n- Prefer targeted lists or semantic data over broad AST traversal when available.\n- For name-based binding checks, use `ctx.scoping().get_binding(scope_id, name)` instead of scanning every binding in `get_bindings(scope_id)`.\n- For global or unresolved identifier checks, start from `ctx.scoping().root_unresolved_references().get(name)` for the small set of relevant names instead of visiting every `IdentifierReference`.\n- When iterating unresolved references, still verify the reference is the right kind: skip references with a symbol, type-only references, and nodes whose `AstKind` is not the expected identifier or member access.\n- When checking imported specifiers or exported names, iterate the concrete specifiers or precomputed export set rather than scanning all root bindings for every item.\n\nUse precomputed `FxHashSet`s only when many symbols need the same membership test. Prefer keyed semantic lookup when each lookup already has an exact name.\n\n### Avoid unnecessary regular expressions\n\nAvoid regular expressions when byte or string checks are enough: `contains`, `starts_with`, `ends_with`, or matching a small fixed set.\n\nFor hot comment or string scanning paths, prefer a cheap `memchr` or byte search to reject most inputs, then parse only candidates. Preserve regex semantics when replacing one, especially identifier boundaries, optional prefixes, and multiline whitespace.\n\n### Avoid heap allocations\n\n- Use copy-on-write utilities when a value usually does not need to change.\n- Avoid intermediate `Vec`s and `String`s when iteration or borrowed data is enough.\n- Keep temporary data on the stack when practical.\n- Delay allocation until a diagnostic, fix, or transformed value is actually needed.\n- Use allocation-free ASCII comparisons such as `starts_with_ignore_case` before calling `cow_to_ascii_lowercase`.\n- Use byte scans such as `as_bytes().array_windows()` for simple ASCII patterns like escape sequences.\n- Reserve hash maps or sets when the final size is known.\n- Avoid building a `HashSet` just to check names that can be looked up directly in scoping data.\n","frontmatter":{"name":"performance-lint-rules","description":"Performance review guidance for Oxc linter rule implementations. Use only when reviewing Rust rule code under crates/oxc_linter/src/rules/ or when explicitly auditing those rules for performance improvements."},"isInternal":false,"tokens":858,"sizeBytes":4282},{"name":"0.snap.md","path":"apps/oxfmt/test/cli/extensions/0.snap.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/0.snap.md","title":"Extensions Skill","category":"anthropic-skill","format":"markdown","content":"# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/0.snap.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/0.snap.md)","tokens":300,"sizeBytes":195},{"name":"options.json","path":"apps/oxfmt/test/cli/extensions/options.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/options.json","title":"Extensions Skill","category":"anthropic-skill","format":"json","content":"# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/options.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/options.json)","tokens":300,"sizeBytes":201},{"name":".oxfmtrc.json","path":"apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json","title":"Sub Skill","category":"anthropic-skill","format":"json","content":"# Sub Skill\n\nPath: `apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json)","tokens":300,"sizeBytes":280},{"name":"child_no_plugins.json","path":"crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json","title":"Plugins Skill","category":"anthropic-skill","format":"json","content":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json)","tokens":300,"sizeBytes":254},{"name":"child_with_plugins.json","path":"crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json","title":"Plugins Skill","category":"anthropic-skill","format":"json","content":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json)","tokens":300,"sizeBytes":258},{"name":"jest.json","path":"crates/oxc_linter/fixtures/extends_config/plugins/jest.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/jest.json","title":"Plugins Skill","category":"anthropic-skill","format":"json","content":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/jest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/jest.json)","tokens":300,"sizeBytes":230},{"name":"parent_default_plugins.json","path":"crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json","title":"Plugins Skill","category":"anthropic-skill","format":"json","content":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json)","tokens":300,"sizeBytes":266},{"name":"parent.json","path":"crates/oxc_linter/fixtures/extends_config/plugins/parent.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent.json","title":"Plugins Skill","category":"anthropic-skill","format":"json","content":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent.json)","tokens":300,"sizeBytes":234},{"name":"react.json","path":"crates/oxc_linter/fixtures/extends_config/plugins/react.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/react.json","title":"Plugins Skill","category":"anthropic-skill","format":"json","content":"# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/react.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/react.json)","tokens":300,"sizeBytes":232},{"name":"package.json","path":"crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json","title":"Nested-package Skill","category":"anthropic-skill","format":"json","content":"# Nested-package Skill\n\nPath: `crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json)","tokens":300,"sizeBytes":277},{"name":"CHANGELOG.md","path":"packages/codegen/CHANGELOG.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/CHANGELOG.md","title":"Codegen Skill","category":"anthropic-skill","format":"markdown","content":"# Changelog\n\nAll notable changes to this package will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0).\n\n## [0.155.0] - 2026-10-19\n\n### ⚡ Performance\n\n- 2ab6b63 packages/codegen: Remove redundant `printSpaceBeforeIdentifier` and `last` updates in `printParams` (#27652) (overlookmotel)\n- e533278 codegen: Reduce writes in `printDecorators` (#27651) (overlookmotel)\n\n### 📚 Documentation\n\n- 3de03c1 packages/codegen: Correct outdated comments (#27838) (overlookmotel)\n\n## [0.154.0] - 2026-10-13\n\n### 🐛 Bug Fixes\n\n- 760b60b codegen, packages/codegen: Remove extraneous space before `RestElement` in `ArrayPattern` (#27613) (overlookmotel)\n- 0353d37 packages/codegen: Support printing `V8IntrinsicExpression`s (#27597) (overlookmotel)\n- f50257e codegen: Avoid extra indent before empty do-while bodies (#27407) (camc314)\n- 5461c1a packages/codegen: Fix printing negative `BigIntLiteral` after `-` (#27377) (overlookmotel)\n- bebef88 packages/codegen: Fix debug assert fail printing `TSJSDocNonNullableType` (#27373) (overlookmotel)\n\n### ⚡ Performance\n\n- e1c97f8 packages/codegen: Remove all `printSpaceBeforeIdentifier` calls (#27620) (overlookmotel)\n- 70ecfdd packages/codegen: Skip redundant `printSpaceBeforeIdentifier` call (#27619) (overlookmotel)\n- 3c289f3 packages/codegen: Remove branches from loops printing arrays (#27614) (overlookmotel)\n- 27f5b45 packages/codegen: Remove branch printing `RestElement` in `ArrayPattern` (#27608) (overlookmotel)\n- adc90ed packages/codegen: Reduce branches in `printArguments` (#27607) (overlookmotel)\n- 262dd21 packages/codegen: Remove unnecessary `printSpaceBeforeIdentifier` calls (#27606) (overlookmotel)\n- 4bb9722 packages/codegen: Print `SpreadElement` arguments via `printExpression` (#27599) (overlookmotel)\n- 0c98dc5 packages/codegen: Reduce megamorphic `type` reads in `printParams` (#27595) (overlookmotel)\n- e3780b5 packages/codegen: Reduce writes in `printForOfStatement` (#27594) (overlookmotel)\n- 8d0ba20 packages/codegen: Reduce megamorphic `type` reads in `printForOfStatement` (#27593) (overlookmotel)\n- b29f35e packages/codegen: Reduce megamorphic `type` reads in `printDirectivesAndStatements` (#27592) (overlookmotel)\n- 256259d packages/codegen: Reduce megamorphic `type` reads in `printConditionalExpression` (#27591) (overlookmotel)\n- a9bd942 packages/codegen: Reduce megamorphic `type` reads in `printObjectProperty` (#27590) (overlookmotel)\n- 0a386ff packages/codegen: Ensure `shorthand` var is always a boolean (#27589) (overlookmotel)\n- 8d4c633 packages/codegen: Reduce branches in `printObjectProperty` (#27588) (overlookmotel)\n- b4c8c04 packages/codegen: Reduce megamorphic `type` reads in `printMemberExpression` (#27587) (overlookmotel)\n- ff5eae8 packages/codegen: Reduce megamorphic `type` reads (#27586) (overlookmotel)\n- 961f318 packages/codegen: Reduce megamorphic `type` reads in `binCheckAndPrepare` (#27460) (overlookmotel)\n- 3d2f93f packages/codegen: Reduce megamorphic property accesses for `BinaryExpression`s (#27459) (overlookmotel)\n- 675ec5e packages/codegen: Reduce megamorphic property accesses in `printBinaryish` (#27458) (overlookmotel)\n- b0dbfff packages/codegen: Reduce writes and branches printing functions (#27455) (overlookmotel)\n- 5f3865c packages/codegen: Reduce writes for optional `MemberExpression`s (#27454) (overlookmotel)\n\n### 📚 Documentation\n\n- 59472c5 packages/codegen: Add `@param`s to JSDoc comments which had none (#27601) (overlookmotel)\n- 5ee0fc9 packages/codegen: Complete `@param` lists in partly documented JSDoc comments (#27600) (overlookmotel)\n- 12dfa25 packages/codegen: Correct doc comment for `printBinaryish` (#27456) (overlookmotel)\n\n## [0.151.0] - 2026-09-21\n\n### 🚀 Features\n\n- 1fac655 coverage: Support new TypeScript fixture layout (#26226) (camc314)\n\n## [0.150.0] - 2026-09-14\n\n### 🐛 Bug Fixes\n\n- 32d00c5 codegen: Preserve instantiation expression precedence (#26424) (camc314)\n- 5986187 packages/codegen: Preserve private-in right operand precedence (#26420) (camc314)\n- f8e6c6c packages/codegen: Preserve in restriction through yield arguments (#26421) (camc314)\n- 6e15ad5 packages/codegen: Print matching quoted import names as identifiers (#26404) (camc314)\n- bbbb4bc packages/codegen: Preserve private-in left operand precedence (#26403) (camc314)\n- a111b5b packages/codegen: Print accessibility modifiers before abstract (#26402) (camc314)\n\n## [0.148.0] - 2026-08-31\n\n### ⚡ Performance\n\n- 07a0793 packages/codegen: Flatten output in chunks (#26109) (overlookmotel)\n- 487427a packages/codegen: Faster string flattening (#26108) (overlookmotel)\n- 7785583 packages/codegen: Ensure indent strings are flattened (#26107) (overlookmotel)\n- 0dd4db3 packages/codegen: Store mapping positions in an `Int32Array` (#26085) (overlookmotel)\n\n## [0.147.0] - 2026-08-24\n\n### 🐛 Bug Fixes\n\n- 6c5ad1b codegen: Only add mapping names for `PrivateIdentifier`s which have changed (#25958) (overlookmotel)\n- a185ac3 codegen: Print `#` before private identifier in TS signature key (#25938) (overlookmotel)\n\n### ⚡ Performance\n\n- 53f6270 packages/codegen: Eagerly initialize `mapNames` (#25979) (overlookmotel)\n- 5b43c60 packages/codegen: Split recording mappings into multiple functions (#25978) (overlookmotel)\n- cc947b5 packages/codegen: Reduce property lookups (#25977) (overlookmotel)\n- c22a9cc packages/codegen: Combine condition checks (#25976) (overlookmotel)\n- 215a6a5 packages/codegen: Add functions for writing/marking unnamed mappings (#25975) (overlookmotel)\n- 1004439 packages/codegen: Merge `mark` and `write` calls in `printImportAttributes` (#25972) (overlookmotel)\n- 94c8a34 packages/codegen: Remove `node` param from `printString` and `printNonNegativeFloat` (#25971) (overlookmotel)\n- 7e2b4b3 packages/codegen: `printNonNegativeFloat` write mapping directly (#25968) (overlookmotel)\n- 53ff080 packages/codegen: Remove `lastWasPostfixClose` (#25888) (overlookmotel)\n\n### 📚 Documentation\n\n- 07de646 packages/codegen: Fix JSDoc comments on `write*` methods (#25966) (overlookmotel)\n- 3b09c04 packages/codegen: Clarify comment (#25959) (overlookmotel)\n\n## [0.146.0] - 2026-08-19\n\n### 🐛 Bug Fixes\n\n- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)\n- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)\n\n### ⚡ Performance\n\n- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)\n- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)\n- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)\n- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)\n- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)\n- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)\n- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)\n\n### 📚 Documentation\n\n- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)\n\n## [0.145.0] - 2026-08-18\n\n### 💥 BREAKING CHANGES\n\n- 365274e packages/codegen: [**BREAKING**] `printSync` return an object (#25720) (overlookmotel)\n\n### 🚀 Features\n\n- 6a7eb60 packages/codegen: Alter capitalization of `sourceFilename` option (#25854) (overlookmotel)\n- 300763f packages/codegen: Add full source map support (#25585) (camc314)\n- a4478e9 codegen: Add `oxc-codegen` package (#25488) (overlookmotel)\n\n### 🐛 Bug Fixes\n\n- 0fbcf64 codegen: Validate sourcemap options (#25860) (camc314)\n- 4cc7ea4 codegen: Reject invalid indent options (#25807) (camc314)\n- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582) (camc314)\n- 59e8895 codegen: Validate starting indent level (#25550) (camc314)\n\n### ⚡ Performance\n\n- 5444cbf codegen: Skip escaping harmless `<` tokens (#25564) (camc314)\n\n### 📚 Documentation\n\n- 58f7ab9 packages/codegen: Reformat docs and comments (#25848) (overlookmotel)\n- ce03ac1 packages/codegen: Fix JSDoc comments for `printSync` (#25722) (overlookmotel)\n- 386a699 packages/codegen: Correct JSDoc comment (#25716) (overlookmotel)\n- fd62354 codegen: Revamp package readme (#25560) (camc314)\n- ffa3153 codegen: Clarify raw transfer Node requirement (#25565) (camc314)\n- c5d4063 codegen: Clarify binary walk allocations (#25562) (camc314)\n- 5703d7a codegen: Correct stale printer comments (#25561) (camc314)\n\n","isInternal":false,"tokens":2656,"sizeBytes":8545},{"name":"DESIGN.md","path":"packages/codegen/DESIGN.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/DESIGN.md","title":"Codegen Skill","category":"anthropic-skill","format":"markdown","content":"# Design\n\nWhy `oxc-codegen` is shaped the way it is.\n\nIt is a port of Oxc's Rust [`oxc_codegen`] crate, and its output has to be byte-identical to it.\nBut it is not a transliteration. A straight port would not take into account differences between Rust and JS,\nand would likely be several times slower.\n\nThis document is for someone about to change the printer, wondering which of its oddities are load bearing.\nEverything here is also documented at the point it applies. This is an overview.\n\n## Contents\n\n- [Why not native?](#why-not-native)\n- [What this is](#what-this-is)\n- [What it is not at present](#what-it-is-not-at-present)\n- [`state.last`](#statelast)\n- [Four builds from one source tree](#four-builds-from-one-source-tree)\n- [Other things which make it fast](#other-things-which-make-it-fast)\n- [Where the ESTree AST differs](#where-the-estree-ast-differs)\n- [How it is tested](#how-it-is-tested)\n- [Rules to keep](#rules-to-keep)\n\n## Why not native?\n\nOxc's other JS packages are bindings around a native library. This one deliberately is not.\n\nThe starting point was a hunch:\n\n> **When the AST is already in JS, no native printer can beat a JS one.**\n\n### The argument\n\n- **Printing is cheap.** It is a linear walk of the AST, emitting strings. There is no parsing, no analysis,\n  no search. It doesn't play to the strengths of a faster language the way parsing or minifying does.\n- **Crossing into native is not cheap.** A native printer cannot see a JS object graph.\n  The whole AST has to be serialized across the boundary before it can print a single character.\n- **The boundary dominates.** The serialization is proportional to the size of the AST, just as the printing is -\n  but it is work a JS printer can skip entirely. It is added to a total that was small to begin with.\n\nThe conclusion: A native printer loses the game before it's even started - still shovelling data over to native side,\nwhile the JS printer already finished work and has gone off to the pub.\n\n### Where this applies\n\nOnly when the AST is **already** in JS - it came from `oxc-parser`, or a plugin built it, or something\ntransformed it on JS side. That is the common use case this package is for.\n\nIt does **not** apply when you are printing source text you also parsed natively. Then stay on native side -\nthe AST never has to leave native memory, and Rust `oxc_codegen` is the right tool. The two are complementary.\n\nThe benchmarks in the [README](./README.md) measure `oxc-codegen` over representative JS-held ASTs.\n\n## What this is\n\nA printer from an ESTree AST to source text, written in TypeScript.\n\n### The output contract\n\nByte-identical to `oxc_codegen` with default options - tab indentation, double quotes, comments off.\n\nThe conformance suites check this on every fixture. It is what makes the two printers substitutable for one another.\n\n## What it is not at present\n\n`oxc_codegen` (Rust printer) is more capable than this one.\n\n- No minify mode.\n- No support for comments.\n- No symbol mangling.\n\nMany of these features can be added in future, and we should be able to do so without _much_ impact on performance.\n\n## `state.last`\n\nThis is the largest divergence from Rust, and most of the others follow from it.\n\n### The problem\n\nTwo adjacent tokens can merge into a different token when written straight up against each other.\n`in` after an identifier becomes part of that identifier. `+` after `+` becomes `++`.\n\nDeciding whether to insert a space means knowing what was written last.\n\n### How Rust answers it\n\nRust just looks. Its output buffer is a `Vec<u8>`, so `code.last_byte()` is a pointer dereference - very cheap.\n\n### Why this doesn't work in JS\n\n**In JS, reading the last character is catastrophic for perf.** `state.output` is built by repeated `+=`,\nwhich V8 represents as a **cons string** - a tree of the pieces, never actually concatenated. Reading any\ncharacter of it forces V8 to flatten that tree.\n\nDo that after every append and the printer spends its time flattening ropes it is about to extend again.\nResult is a **4x slow-down**.\n\nSo `oxc-codegen` has to find a different solution. Every time it pushes a string to output, it also records\nin `state.last` what the output now ends with. `state.output` never needs to be read from.\n\nThe rope is flattened once, at the end. Whatever the caller does with the returned code will flatten it anyway\n(and `generateSourceMap` scans it), so `printSync` pre-flattens with the fastest method available.\nA large output is flattened in chunks along the way rather than all at once at the end -\nsee `print/flatten.ts` for the two reasons.\n\n### The answer: A category, not a character\n\nIt turns out the functions which decide whether spaces need to be inserted between keywords / identifiers\ndon't actually need to know what the last character was, they only need to know what _category_ of character\nit was. i.e. It doesn't matter if the last character was `\"a\"` or `\"Z\"` or `\"$\"` - they're all equivalent.\n\nAlmost everywhere in the printer, it knows statically what _category_ it's writing, even if it doesn't know\nthe exact bytes until runtime. e.g. When writing an identifier, it's the `IDENT` category.\n\n```ts\nwrite(state, node.name, CAT_IDENT);\n```\n\nThis has 3 advantages over storing the last character:\n\n1. Categories are just small integers, the cheapest data type there is.\n2. The categories are statically defined at call sites. No `node.name.at(-1)`.\n3. Checking the category of last write is cheap - `last === CAT_IDENT` not `isIdentifierPart(lastChar)`.\n\nThat one `last` field replaces all of this:\n\n| Rust                                  | Question it answers                 | JS                      |\n| :------------------------------------ | :---------------------------------- | :---------------------- |\n| `last_byte()` + `is_identifier_part`  | Would a following identifier merge? | `CAT_IDENT`             |\n| `last_byte() == Some(b'?')`           | Would a following `?` make `??`?    | `CAT_QUESTION`          |\n| `peek_nth_byte_back(1) == Some(b'<')` | Is this `!` the `!` of a `<!`?      | `CAT_OP_LT_THEN_UN_NOT` |\n\n### Extending this scheme to operators\n\nRust `oxc_codegen` also holds state about what operator was last written. It stores this info as a pair:\n\n- `prev_op` - Last operator that was written\n- `prev_op_end` - Position of the end of that operator\n\nEach time an operator is written, it sets these 2 fields. When other code wants to know \"am I writing this straight\nafter an operator, and which operator?\" it:\n\n1. Checks if `code.len() == prev_op_end`. If true, then yes, it's right after an operator.\n2. `prev_op` then says which operator.\n\nIn Rust printer, this makes sense. Checking these 2 fields, and comparing to `code.len()` costs 2 or 3 memory accesses,\nand 1 or 2 comparisons. But that's fine! It only reads or writes these 2 fields infrequently - only when an operator\nis being written. And operators are only a small fraction of code.\n\nIn JS codegen, we're in a different situation. We've been forced to update `state.last` after every write,\nto avoid the huge perf hit of reading the last character from `output`.\n\nSo we might as well use `last` to record operators too, and also some similar character classes:\n\n| Rust                                    | Question it answers                                     | JS                |\n| :-------------------------------------- | :------------------------------------------------------ | :---------------- |\n| `code.len() == prev_op_end` + `prev_op` | Which operator came last, and was it right before this? | `CAT_OP_*`        |\n| `code.len() == need_space_before_dot`   | Is space needed after digit? e.g. `0 .toExponential()`  | `CAT_INT_DIGIT`   |\n| `code.len() == prev_reg_exp_end`        | Did a flagless regex just close?                        | `CAT_REGEX_SLASH` |\n\nAll these Rust state fields, and every read of the output buffer, collapse into one field in the JS printer.\n\nIt costs no write barrier to store (because categories are represented by \"SMI\" small integers), and one compare\nto test. Being touched by every single write, it is the hottest field in `State`, reliably in L1 cache,\nand probably often also benefits from fast store-to-load forwarding.\n\n#### The `<!--` case, as an example\n\nRust decides whether a `!` is the `!` of a `<!--` hazard by peeking at the _second_-to-last byte,\nat the moment a `--` is about to be written.\n\nHere the question is answered when the `!` is written, where the preceding character is already known,\nand the answer is baked into which category gets stored. The reader has nothing left to look up.\n\n### Position marks\n\nSimilar to operators, Rust printer also maintains state about the _context_. For example:\n\n```js\n// Function declaration\nexport default function foo () {}\n// Function expression\nexport default (function bar () {});\n```\n\nThese 2 are semantically different - `foo` is available in outer scope, `bar` is not.\n\nWhen printing a `FunctionExpression`, print needs to know \"am I the value of an `export default`?\"\nto know whether it needs to wrap the function in parentheses.\n\nSimilar to operators, Rust printer stores this info in field `start_of_default_export`.\nWhen `code.len() == start_of_default_export` then, yes, what's currently being printed is the value of\nan `export default` statement.\n\nAgain, JS codegen uses `last` to store this info. Except here `last` doesn't describe what _was_ written,\nit says what the context is.\n\nNote that the node which reads the mark need not be the direct child of the `export default`.\nIn `export default (function bar() {})();` the function is the callee of a call expression, and the parens\nare still needed. It gets them because `printCallExpression` wrote nothing before descending into the callee,\nso the mark was still there when `printFunction` looked. Whatever is printed leftmost sees the mark,\nhowever deeply nested it is.\n\n| Rust                                    | Question it answers                   | JS                            |\n| :-------------------------------------- | :------------------------------------ | :---------------------------- |\n| `code.len() == start_of_stmt`           | At the start of a statement?          | `CAT_START_OF_STMT`           |\n| `code.len() == start_of_arrow_expr`     | At the start of a concise arrow body? | `CAT_START_OF_ARROW_EXPR`     |\n| `code.len() == start_of_default_export` | At the start of an `export default`?  | `CAT_START_OF_DEFAULT_EXPORT` |\n\nThe reason this works is that in every case the last character printed in these positions was a space, a line break,\nor the start of the file, which all fall into the `CAT_OTHER` category. So these values of `last` aren't ambiguous\nto any other code which judges what to do next based on what came last. They just treat these values as\n\"last character written was something I don't need to do anything special about\".\n\n#### Why the substitution is safe\n\nTwo facts, both checked across every printer file:\n\n1. **Nothing writes output on the way down from a mark to its reader.** Every path from a mark to a\n   reader either writes nothing, or writes a real `(` - never a `writeNoLast`, which would leave the\n   mark visible past output which should have killed it.\n2. **It can only ever add parentheses, never remove them.** A real `write` always grows the output\n   (empty writes are illegal), so the set of things which kill a mark under categories is a strict\n   subset of what killed the offsets.\n\nSo every possible failure is a spurious pair of parentheses - slightly longer output, but not _invalid_ output.\nThe byte-for-byte conformance tests would catch this, but even if there are gaps in the conformance suite\nfor crazy edge cases and a bug sneaks through, it can't produce invalid JavaScript.\n\n### Why category numbering is load bearing\n\nThe full table is at the top of [`print/categories.ts`] and is the authority.\n\nThree properties are relied on. Adding a code without preserving them will silently space output wrongly.\n\n1. **Identifier hazards are the lowest codes.** So `printSpaceBeforeIdentifier` is `last <= CAT_REGEX_SLASH` -\n   one compare, no table, no branch tree. The operators `printSpaceBeforeOperator` must distinguish are the highest,\n   for the same reason (`last >= CAT_OP_LT_THEN_UN_NOT`).\n2. **The `CAT_START_OF_*` codes sit between those two ranges**, which is what makes both range checks\n   treat them as \"nothing to separate\".\n3. **`CAT_START_OF_STMT` is odd, with the other two marks either side.** The five reader sites each ask\n   about a _pair_ of marks, never all three, so both pairs fall out as one `|`-and-compare:\n\n   ```\n   (last | 1) === CAT_START_OF_STMT          // Statement or `export default`\n   ((last - 1) | 1) === CAT_START_OF_STMT    // Statement or concise arrow body\n   ```\n\nEach property has an `if (DEBUG)` block which iterates every category and asserts the compare selects\nexactly the intended set and nothing else - in [`print/categories.ts`] for the `|` identities, and in [`print/space.ts`]\nfor the two range checks.\n\nRenumber freely. The assertions will tell you what you broke, and cost release builds nothing.\n\n**One deliberate exception**: `CAT_OP_UN_NOT` is an operator code but sits _below_ the operator range.\nNo operator merges with a plain `!`, so storing it must not cost `printSpaceBeforeOperator` a call into its slow path.\n\n### `writeNoLast`\n\nNot every write needs to update `last`.\n\nWhen one token is written in pieces - a string's opening quote, its contents, its closing quote -\nonly the last piece's category can possibly be read. Hence eleven write primitives in [`print/write.ts`]:\n\n| Function                     | Updates `last` | Records a source mapping       |\n| :--------------------------- | :------------- | :----------------------------- |\n| `write`                      | Yes            | No                             |\n| `writeIdent`                 | Yes            | No                             |\n| `writePrivate`               | Yes            | No                             |\n| `writeWithMap`               | Yes            | At the node's start            |\n| `writeWithMapNamed`          | Yes            | At the node's start, with name |\n| `writeWithMapNamedPrivate`   | Yes            | At the node's start, with name |\n| `writeWithMapEnd`            | Yes            | At the node's last character   |\n| `writeNoLast`                | No             | No                             |\n| `writeWithMapNoLast`         | No             | At the node's start            |\n| `writeWithMapNamedNoLast`    | No             | At the node's start, with name |\n| `writeWithMapNamedJSXNoLast` | No             | At the node's start, with name |\n\n- Every `writeWithMap*` and `markMap*` function takes the node's `start` and `end` offsets, extracted by the caller.\n  The node itself is still passed, last, but only debug asserts read it - which is what checks the offsets\n  against the node they claim to describe. The point is where the read happens - in a caller the node's type is known,\n  so `node.start` is a monomorphic load, whereas inside the write functions the same read is megamorphic,\n  because the printer reaches them from every node type there is. Release builds have the parameter and every argument\n  removed by the TSDown plugin.\n- The `*Named` functions record the name the node had in the source, and take a `NamedMappableNode`,\n  which covers nodes with a string `name`. What they record is the text they print -\n  debug builds assert the two are the same string.\n- The other `writeWithMap*` functions take an `UnnamedMappableNode` which covers nodes without a string `name`.\n- There is a `*Named` function per way of recovering the original name from the source, because the scan differs -\n  a JSX identifier may hold a `-`, and a private identifier's span covers a `#` which is not part of its name.\n  Which one applies is settled at the call site, where the node's type is known statically, rather than read back\n  off a `node` which is megamorphic by the time it arrives.\n- `writeIdent` is `write` with the category fixed at `CAT_IDENT` - an identifier, a keyword, or the trailing digits\n  of a number. It is the commonest category by far, so every call which passed it was spending an argument\n  on a value which could never be anything else.\n- `writeWithMapNamed` and `writeWithMapNamedPrivate` take no category for the same reason, which is why their\n  plain forms are `writeIdent` and `writePrivate` rather than `write`.\n- The two `Private` forms write the `#` themselves, so the name they are given is what follows it,\n  and `last` is always `CAT_IDENT` - the caller passes neither.\n- The three `markMap*` functions record a mapping without writing anything, so `last` is not theirs to update.\n- The rule is exact: **only sound where the value of `last` is provably dead**.\n  Another real write must follow before anything reads it.\n- JSX carries the longest runs. Printing `<a.b x=\"1\">hi</a.b>` updates `last` exactly once, on the final `>`.\n- `writeNoLast` accepts an empty string, `write` does not.\n  A `write` of `\"\"` would leave `last` claiming a category for a character that was never written.\n\n### How the invariants are enforced\n\nEvery rule above fails quietly - a missing space in one construct out of thousands. So three debug-only\nmechanisms guard them, all removed from release builds.\n\n1. **`debugAssertCategoryMatches`** runs on every `write`, checking the category truthfully describes\n   the final character written. It cannot be a simple lookup - a category encodes the merge hazard of\n   the trailing _token_, not the trailing character - a digit legitimately ends both a `CAT_IDENT` write\n   and a `CAT_INT_DIGIT` one - so each final character has a permitted set.\n2. **`lastIsStale` / `debugAssertLastFresh`** track the `writeNoLast` rule. `writeNoLast` sets the flag,\n   the next real write clears it, and every reader of `last` calls `debugAssertLastFresh` first,\n   to make sure they're not reading a stale value.\n   An author who was wrong about `last` being dead gets a clear failure, not a wrongly spaced construct.\n3. **The numbering assertions** described above.\n\nThis is why the debug build is the one to run the conformance suites against - `pnpm run build-test`.\n\n## Four builds from one source tree\n\n`dist` holds the printer compiled four times, plus an entry point which picks between them.\n\n| Build              | `TS`  | `SOURCEMAPS` |  Size |\n| :----------------- | :---- | :----------- | ----: |\n| `print_js.js`      | false | false        | 25 KB |\n| `print_js_maps.js` | false | true         | 26 KB |\n| `print_ts.js`      | true  | false        | 39 KB |\n| `print_ts_maps.js` | true  | true         | 40 KB |\n\n`index.ts` picks one from the caller's `ts` and `sourcemap` options and `require`s it on first use -\n`require` rather than `import()`, because `printSync` is synchronous.\n\nA caller printing only JavaScript never loads, parses or compiles the TypeScript printers at all.\n\n### Why separate TS and JS builds\n\n**The obvious reason is code size and complexity.** JS-only builds have all TS-related code removed,\nas it's all gated behind compile-time `TS` constant. Every `if (TS && node.declare) { ... }` block is removed\nby minifier, and every `case \"TSEnumDeclaration\":` arm by the `strip_ts` TSDown plugin. With the arms gone,\nthe TS-only printer functions lose their last references and are tree-shaken away. That is a third of the code.\nHowever, it has a surprisingly small impact on perf.\n\n**The bigger reason is monomorphism.** `oxc-parser` produces differently-_shaped_ node objects for a\nJS-shaped AST and a TS-shaped one - the TS shape carries `optional`, `declare`, `accessibility`,\n`override` and the rest.\n\nIf both shapes flow through one `printFunction`, V8 sees two hidden classes at every property access,\nthe inline caches go polymorphic, and the whole printer slows down.\n\nTwo builds means each one only ever sees one shape. JS printer sees only JS AST-shaped objects,\nTS printer sees only TS AST-shaped objects. This gives a perf boost of over 10%.\n\nAdditionally, the split helps branch prediction. If feeding a mix of JS and TS ASTs through a single printer,\nfor a JS AST branches for \"does this node have a type annotation?\" always find the answer is \"no\".\nBranch predictor believes there's a pattern and when that code runs again, it expects no type annotation again.\nBut this time, the AST is a TS AST, and the answer is \"yes\". Mispredict! Go back 100 instructions, go directly back,\ndo not pass Go, do not collect $200!\n\nWith 2 different builds for JS and TS, this doesn't arise. In the JS build the branch isn't there at all -\nit was compiled away. The TS build only ever sees TS ASTs, so what the branch predictor learns stays true.\n\n### Why `State` lives outside `print` directory\n\nThe same argument, one level up.\n\n`State` is defined in `src-js/state.ts`, which is **not** part of any printer build. The printers import\nit as a type only; the entry point constructs it. So all four builds share one class, and therefore one\nhidden class, for the object they thread through every single function.\n\nThis is why **a build-time flag must never add or remove a field on `State`**.\n\n- The source-map fields are present in every build, and set to `null` when unused.\n- The debug fields are present in every build, and only _initialised_ under `if (DEBUG)`.\n\nMaking either conditional would give each build a different object shape, undoing the whole arrangement.\n\n### The build plugins\n\nThere are 4 TSDown plugins in `tsdown_plugins`. Order matters - `strip_ts` is a text transform,\nso it must run before the plugins which parse.\n\n#### `strip_ts`\n\nDeletes regions fenced with `/* IF TS */` ... `/* END_IF */` from JS-only builds.\n\nThey exist because a minifier can fold `if (TS)` away, but cannot remove a `switch` `case` arm on the grounds\nthat its node type never occurs.\n\nIt fails the build if the fences are unbalanced - and also if it finds _no_ fences at all.\nA plugin silently doing nothing is a failure mode worth guarding against.\n\n#### `unmap_writes`\n\nRuns in two modes, one per build flavour (sourcemaps / no sourcemaps), in both cases stripping arguments\nnothing in that build reads.\n\n**Without source maps**, rewrites every mapped write into the plain one it becomes with no mapping to record -\nso `writeWithMap(state, code, cat, node.start, node.end, node)` turns into `write(state, code, cat)` -\nand rewrites the import to match.\n`writeWithMapEnd` becomes `write` too, and every `NoLast` form becomes `writeNoLast`.\n`writeWithMapNamed` and `writeWithMapNamedPrivate` become `writeIdent` and `writePrivate`, which fix the category\ninstead of taking it. `writePrivate` exists only for the rewrite to land on.\n\n`printString` and `printNonNegativeFloat` take the offsets and the node only to hand them on to a mapped write.\nThey keep their names, but the arguments come off every call, and the parameters off their declarations -\nwhich, unlike the mapped writes, survive into the build.\n\n**With source maps**, in release builds only, just the trailing `node` comes off - from every call,\nand from every declaration. The mappings are real in these builds so the offsets stay, but `node` isn't read\nexcept by debug asserts. Debug builds get neither mode - the `node` parameter stays for the debug asserts to read.\n\nWithout it, the offsets would still be read and held live across a call which ignores them.\n\nThe three `markMap*` calls write nothing, so they have no plain form to become, and keep their names -\nthe arguments come off, and a body which opens `if (!SOURCEMAPS) return` lets the minifier remove what is left.\n\nIt fails the build if a mapped write is called with the wrong number of arguments, if a declaration it transforms\nhas the wrong number of parameters, or if a call it rewrote had no matching import to rewrite.\n\n#### `const_functions`\n\nRewrites top-level `function foo(...) { ... }` into `const foo = (...) => { ... }`.\n\nA function declaration creates a binding which could be assigned later, so every call has to read\nthe current value out of module scope. A `const` cannot be, so V8 calls it directly.\n\n- **Measured at ~3%** across all benchmark fixtures.\n- It really is the `const`. The same rewrite with `let` measures ~4% _slower_ than this, and slower than\n  leaving the declarations alone.\n- The source keeps its declarations, because they hoist and read better. Which is why this is a build step\n  rather than a house style.\n\n#### `remove_asserts`\n\nRemoves `debugAssert` and `typeAssertIs` calls _and the expressions inside them_ from release builds.\n\nThe minifier can drop the calls on its own, but cannot prove `node.value` in `debugAssert(node.value !== null)`\nhas no getter, so it would leave `node.value !== null` behind as a bare expression statement.\n\nThis is what gives us Rust `debug_assert!`-style checks in tests and conformance, with zero cost in release build.\n\n## Other things which make it fast\n\n### Popularity-ordered dispatch\n\n`printExpression` (34 arms) and `printStatement` (33 arms) are megamorphic string switches.\n\nV8 lowers a switch on strings to what amounts to an `if / else if` chain, so arm order is execution order.\nBoth are ordered roughly by how often each node type occurs in real code - common variants like `Identifier`,\n`MemberExpression`, `CallExpression` first.\n\nThis sped up the printer by ~30%.\n\nIt also means **adding an arm near the top of either switch has a cost**, however rare the node type is.\n\nEven after ordering `case` arms, these \"dispatch\" functions remain a large part of runtime (~25%).\nMuch sweat and tokens were spilled trying to reduce it further:\n\n- Perfect hash table to convert `node.type` to an integer ID.\n- `switch`-ing on an ID to reduce the switch to a jump table.\n- Branching on first character to narrow down the range - `switch (node.type.charCodeAt(0)) { case 69: ... }`.\n- Many others...\n\nAll to no effect. Arm ordering worked, nothing else did.\n\nIf anyone else can crack this, it's probably the single largest unrealized perf gain on the table.\n\n### Monomorphism\n\nAs much as possible, call paths are structured so that functions receive the same object shape consistently.\nOnce it has gathered type feedback from the first runs, V8 tiers up hot functions to TurboFan, which generates\ncode along these lines (pseudo-code):\n\n```ts\nfunction printThing(state, node): void {\n  if (SHAPE_OF(node) !== K_EXPECTED_SHAPE) {\n    DE_OPTIMIZE(printThing);\n    return printThing(state, node);\n  }\n\n  // Now it's known exactly what shape `node` is, it becomes static-typed like Rust.\n  // Working with it is very cheap.\n\n  // Original code: `const { left } = node;`\n  const left: Expression = GET_UNCHECKED(node, K_LEFT_OFFSET);\n  // ... fast code from here on ...\n}\n```\n\nIn particular, it is **essential** that `State` remains the same shape _everywhere_. Every function touches it,\nso any inconsistency in shape would turn every access polymorphic and completely tank performance.\n\nThe obvious exception is the dispatch functions discussed above (`printExpression` etc), which are\nunavoidably megamorphic.\n\nWe have not done a full audit of all functions to check if any are receiving polymorphic/megamorphic inputs\nwhen that could be avoided. Such an audit might throw up potential improvements.\n\n#### Moral of the story\n\nConsistent object shapes are _really_ important for good perf in JS engines.\n\nWe should provide users with utility functions to construct AST nodes which produce the exact same object shape\nas what `oxc-parser` produces. This would speed up not just `oxc-codegen`, but also their own code that\noperates on ASTs.\n\n```ts\n// Good\nconst binary = oxc.binaryExpression(left, operator, right);\n// Bad - different field order, different object shape\nconst binary = { left, right, operator };\n```\n\n### Intended parser pairing\n\nThe printer is meant to be used with `oxc-parser` with `experimentalRawTransfer` enabled.\n\nRaw transfer builds the AST with a JS deserializer rather than via JSON. Node `type` strings then come out\nas source literals, so those big `switch`es compare interned strings by pointer, rather than character by character.\nThis gives a 10% - 30% perf boost versus a JSON AST.\n\n### Fast paths\n\n- Pretty mode always puts a single space either side of a binary or assignment operator, so the whole\n  token is one constant string - `\" + \"`, `\" ??= \"` - and one `write`, instead of space-operator-space.\n  It also makes the token-glue checks unobservable around those operators, so they are not made at all.\n  See `PADDED_BIN_OPERATORS` in [`print/operators.ts`].\n- `printNonNegativeFloat` prints integers below 1000 straight from `String()`. They are the overwhelming\n  majority, and three digits are too few for any shortening form to win.\n- `printString` tests the whole string against one regex of characters needing escapes, and appends it\n  whole when there are none. The escaping loop is a separate function because it almost never runs.\n- `printSpaceBeforeOperator` is one compare, with its three-clause slow path split out so the common path\n  stays small enough for V8 to inline.\n- `printLiteral` switches on `typeof value` first, identifying strings and numbers without touching the\n  `regex` or `bigint` properties.\n- `printNumericLiteral` tests `value > 0 && value < Infinity` first, reaching the same branch the full\n  chain would without the two builtin calls.\n\nEach of these produced a small perf bump individually, ~10% gain in aggregate.\n\n### Indent cache\n\n`printIndent` reads a pre-built string for the current level out of an array.\n\n- The cache is created once per _process_, alongside the module-level state in `state.ts`.\n  Every `State` carries a reference to it. So indent strings one printer run has grown are there for the next.\n  This cache is shared between JS and TS, sourcemap and no-sourcemap builds.\n- `growIndents` deliberately forces each new indent string flat (a throwaway `charCodeAt(0)`) before caching it,\n  paying the flatten cost once rather than on every append.\n\n### Source maps computed at the end\n\nDuring printing, a mapped write records only an output offset and the node's original UTF-16 offset\nfrom `start` / `end`. Nodes without offsets are not mapped, and `sourceText` is required to convert\nsource offsets to line/column positions.\n\nThe offset pairs go into an `Int32Array` which, like the indent cache, is created once per process\nand reused by every print - each `State` tracks how much of it that print has filled, and the buffer\ndoubles when a print fills it, so a steady-state print allocates nothing to record its mappings.\n\n`generateSourceMap` then walks the output once at the end, counting ECMAScript line terminators,\nbuilds the equivalent line table for `sourceText`, turns both sets of offsets into line/column,\nand encodes the mappings as base64 VLQ. Tracking lines and columns throughout would cost every write\nin every build.\n\n#### Potential future improvement\n\nProducing source maps is exactly the kind of raw number-crunching that native code excels at.\nAnd, unlike the AST, it's just small integers - the kind of data that transfers across the JS-native\nboundary cheaply.\n\nThe offsets are already assembled on the JS side in an `Int32Array`, so it might be worthwhile making\na single JS-Rust-JS round-trip with it, with the sourcemap generation (VLQ encoding etc) happening on Rust side.\n\nWASM might also be a good option for this.\n\n### No separate stack in the binary walk\n\n`printBinaryish` walks the left spine of a binary/logical chain iteratively. `a + b + c + d` nests as deep\nas it is long, so recursion would put the stack at the mercy of the input.\n\nRust does the same, but with an explicit `Stack` on `Codegen`. Here each level has a small visitor\nobject, and pending outer levels are threaded through its `parent` field rather than a separate stack array.\n\n### Lookup tables are `__proto__: null`\n\nSo a property miss cannot walk up to `Object.prototype`.\n\n## Where the ESTree AST differs\n\nThe two printers walk different ASTs, and some of the differences reach the output.\n\n### Node types\n\n- **One `Literal` node type** covers strings, numbers, booleans, `null`, regexes and bigints, where Rust\n  has six separate types. `printLiteral` tells them apart from `typeof value` and the presence of `regex` / `bigint`.\n- **`#x in obj` arrives as a `BinaryExpression`** with a `PrivateIdentifier` on the left, not as its own node type.\n  Hence the extra test in `printExpression`'s `BinaryExpression` arm.\n\n### Parenthesized function expressions\n\nOxc's Rust AST records that a function expression was parenthesized in a `pife` flag, and prints the parentheses\nback from it.\n\nESTree has no such flag. The information is only there when the AST was parsed with `preserveParens`,\nas a `ParenthesizedExpression` wrapper. `printExpression`'s arm for that node re-emits the parentheses\naround a function or arrow, and is transparent around anything else.\n\nThis is why `printBinaryish` prints its operands **unstripped**. `withoutParens` is used for\nthe structural type tests, but the node handed to `printExpression` keeps its wrapper,\nso that arm can run.\n\n### Things ESTree cannot express\n\n- `import \"m\";` versus `import {} from \"m\";`\n- An absent `with` clause versus an empty `with {}`\n- `assert {...}` versus `with {...}`\n- The span of a `with {...}` clause (only its individual attributes have ESTree locations)\n\nThe conformance harness normalizes the Rust AST down to what ESTree can express before printing,\nrather than expecting the JS side to reproduce information it was never given.\nSee `Normalize` in `tasks/codegen_conformance/src/lib.rs`.\n\n### Positions, and other producers\n\n- **Oxc offsets only.** `start` / `end` offsets are converted to UTF-16 line/column once at the end.\n  Source maps require `sourceText`; `loc` and `range` are not supported inputs.\n\n## How it is tested\n\n`oxc-codegen` is tested against all Test262, Acorn-JSX, and TypeScript test cases - about 62,000 fixtures.\n\nEvery fixture is printed three times:\n\n1. In Rust, with a source map, via the `oxc-codegen-conformance` NAPI addon (`tasks/codegen_conformance`).\n2. In JS, through the no-maps build.\n3. In JS, through the separately compiled maps build.\n\nAll three generated outputs must agree byte for byte. Every decoded generated/original line, column,\nand optional original name from the maps build must also agree with Rust, including ordering and\nduplicate suppression.\n\nBoth sides parse the same source text with the same `SourceType`, derived by the same function.\nFixtures which do not parse cleanly are skipped, rather than passing quietly.\n\nEvery fixture is checked in **both `preserveParens` modes**. They are different paths through the printer -\nwith the wrappers present it must decide what to re-emit, without them it must re-derive every parenthesis\nfrom precedence.\n\n## Rules to keep\n\nA checklist, all of it argued for above.\n\n1. **Never read a character of `state.output`.**\n2. **A `write` must declare a category which truthfully describes its final character**, and must never be\n   given an empty string.\n3. **Only use `writeNoLast` where `last` is provably dead.** A real write must follow before any reader runs.\n4. **Preserve the three numbering properties** when adding or renumbering a `CAT_*` code, and keep the\n   `if (DEBUG)` assertions which check them.\n5. **Never let a build-time flag add or remove a field on `State`.**\n6. **Do not add an arm near the top of `printExpression`, `printStatement` or other dispatch functions**\n   unless the node type really is that common.\n7. **Do not relax `const_functions` to `let`.**\n8. **Run the conformance suites against a debug build** (`pnpm run build-test`), so the assertions are live.\n9. **Benchmark against a release build** (`pnpm run bench` rebuilds one first). A debug build keeps\n   `debugAssert` calls and does not represent shipped performance.\n\n[`oxc_codegen`]: https://github.com/oxc-project/oxc/tree/main/crates/oxc_codegen\n[`print/categories.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/categories.ts\n[`print/write.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/write.ts\n[`print/space.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/space.ts\n[`print/operators.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/operators.ts\n[`print/types.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/types.ts\n","isInternal":false,"tokens":8557,"sizeBytes":36613},{"name":"package.json","path":"packages/codegen/package.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/package.json","title":"Codegen Skill","category":"anthropic-skill","format":"json","content":"# Codegen Skill\n\nPath: `packages/codegen/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/package.json)","tokens":300,"sizeBytes":170},{"name":"tsconfig.json","path":"packages/codegen/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/tsconfig.json","title":"Codegen Skill","category":"anthropic-skill","format":"json","content":"# Codegen Skill\n\nPath: `packages/codegen/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/tsconfig.json)","tokens":300,"sizeBytes":172},{"name":"copilot-instructions.md","path":".github/copilot-instructions.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/.github/copilot-instructions.md","title":"GitHub Copilot Instructions","category":"copilot-instructions","format":"markdown","content":"# Copilot Review Instructions\n\nWhen reviewing pull requests in this repository:\n\n- Do not guess or infer compiler errors. Only report them when supported by actual compiler diagnostics.\n- Treat files inside any `fixtures` directories as test data, not production code.\n- Fixture files may intentionally contain buggy, unsafe, or syntactically invalid code to validate parser/linter/transformer behavior.\n- Do not report normal code-quality or correctness issues for fixture files.\n- You may report a fixture issue only when the test data appears incorrect or mislabeled:\n  - The folder or filename does not match what the test claims to cover.\n  - The fixture content does not actually test the described case.\n  - The fixture appears accidentally broken rather than intentionally crafted.\n","isInternal":false,"tokens":150,"sizeBytes":790},{"name":"child_with_plugins_parent_default_plugin.json","path":"crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json","title":"plugins Plugin Manifest","category":"plugin-manifest","format":"json","content":"# plugins Plugin Manifest\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json)","tokens":300,"sizeBytes":312},{"name":"README.md","path":"crates/oxc_transformer_plugins/README.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_transformer_plugins/README.md","title":"oxc_transformer_plugins Documentation","category":"plugin-manifest","format":"markdown","content":"# Oxc Transformer Plugins\n\nSpecialized transformation plugins for common JavaScript and TypeScript patterns.\n\n## Overview\n\nThis crate provides specific transformation plugins that can be used independently or as part of the broader oxc transformation pipeline. These plugins handle common transformation needs like global variable injection and module format conversion.\n\n## Key Features\n\n- **Global variable injection**: Add global variables to modules\n- **Module runner transforms**: Transform modules for different execution environments\n- **Global defines replacement**: Replace compile-time constants\n- **Composable plugins**: Use individually or combine multiple plugins\n\n## Architecture\n\n### Plugin System\n\nEach plugin implements the transformation logic for a specific use case:\n\n- **Focused responsibility**: Each plugin handles one type of transformation\n- **Traverse integration**: Uses oxc's traversal system for efficient AST walking\n- **Composable**: Multiple plugins can be applied in sequence\n- **Context-aware**: Access to semantic information when needed\n\n### Common Use Cases\n\n#### Build-time Optimizations\n\n- **Dead code elimination**: Remove unreachable code paths\n- **Constant folding**: Replace compile-time constants\n- **Environment variables**: Inject environment-specific values\n\n#### Module System Compatibility\n\n- **Polyfill injection**: Add necessary polyfills for target environments\n- **Global shimming**: Provide Node.js globals in browser environments\n- **Format conversion**: Transform between module formats\n\n#### Development Tools\n\n- **Debug injection**: Add development-time debugging code\n- **Hot module replacement**: Support for HMR systems\n- **Testing utilities**: Inject test-specific globals\n\n### Integration Points\n\nThese plugins integrate with:\n\n- **oxc_transformer**: Core transformation infrastructure\n- **oxc_traverse**: AST traversal and mutation\n- **Build tools**: Webpack, Vite, Rollup, and other bundlers\n- **CLI tools**: Direct usage in command-line transformations\n\nThe plugin architecture enables extensible, composable transformations while maintaining high performance through oxc's efficient traversal system.\n","isInternal":false,"tokens":391,"sizeBytes":2168},{"name":"README.md","path":"npm/oxlint-plugins/README.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/npm/oxlint-plugins/README.md","title":"oxlint-plugins Documentation","category":"plugin-manifest","format":"markdown","content":"# @oxlint/plugins\n\nPlugin utilities for [Oxlint](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n\nThis package provides optional functions to assist in creating Oxlint JS plugins and rules.\n\n## Installation\n\n```bash\nnpm install @oxlint/plugins\n```\n\n## Usage\n\n### Define functions\n\nUse `definePlugin` and `defineRule` if authoring your plugin in TypeScript for type safety.\n\n```typescript\nimport { definePlugin, defineRule } from \"@oxlint/plugins\";\n\nconst rule = defineRule({\n  create(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n});\n\nexport default definePlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n### Types\n\nThis package also includes types for plugins and rules.\n\n```typescript\nimport type { Context, Rule, ESTree } from \"@oxlint/plugins\";\n\nconst rule: Rule = {\n  create(context: Context) {\n    return {\n      Program(node: ESTree.Program) {\n        // Rule logic here\n      },\n    };\n  },\n};\n```\n\n### ESLint compatibility\n\nIf your plugin uses Oxlint's [alternative `createOnce` API](https://oxc.rs/docs/guide/usage/linter/js-plugins#alternative-api),\nuse `eslintCompatPlugin` to convert the plugin so it will also work with ESLint.\n\n```typescript\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nconst rule = {\n  createOnce(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n};\n\nexport default eslintCompatPlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n## Node.js version\n\nThis package requires Node.js 12.22.0+, 14.17.0+, 16.0.0+, or later.\nThis matches the minimum Node.js version required by ESLint 8.\n\nThis package provides both ESM and CommonJS entry points.\n\nSo a plugin which depends on `@oxlint/plugins` can be:\n\n- Used with any version of Oxlint.\n- Used with ESLint 8+.\n- Published as either ESM or CommonJS.\n\n## Docs\n\nFor full documentation, see [Oxlint JS Plugins docs](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n","isInternal":false,"tokens":507,"sizeBytes":2026},{"name":"README.md","path":"packages/codegen/README.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/README.md","title":"codegen Documentation","category":"plugin-manifest","format":"markdown","content":"# oxc-codegen\n\nFast, synchronous code generation for JavaScript and TypeScript ASTs.\n\n`oxc-codegen` turns an [ESTree](https://github.com/estree/estree) or\n[TS-ESTree](https://typescript-eslint.io/packages/typescript-estree/) AST into formatted source\ncode. It supports JavaScript, JSX, TypeScript, and TSX.\n\nThe printer is a port of Oxc's Rust `oxc_codegen` crate. With the default options, both printers\nproduce byte-identical output: tab indentation, double-quoted strings, and no comments.\n\n## Installation\n\n```sh\nnpm install oxc-codegen\n```\n\n`oxc-codegen` is ESM-only and requires Node.js `^20.19.0` or `>=22.12.0`.\n\n## Quick start\n\nPair it with [`oxc-parser`](https://www.npmjs.com/package/oxc-parser) to parse and print source code:\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst { program } = parseSync(\"input.js\", \"const answer=6*7\");\nconst { code } = printSync(program);\n\nconsole.log(code);\n// const answer = 6 * 7;\n```\n\nYou can also print a manually constructed AST:\n\n```js\nconst program = {\n  type: \"Program\",\n  sourceType: \"script\",\n  body: [\n    {\n      type: \"ExpressionStatement\",\n      expression: {\n        type: \"CallExpression\",\n        callee: {\n          type: \"MemberExpression\",\n          object: { type: \"Identifier\", name: \"console\" },\n          property: { type: \"Identifier\", name: \"log\" },\n          computed: false,\n          optional: false,\n        },\n        arguments: [{ type: \"Literal\", value: \"Hello!\" }],\n        optional: false,\n      },\n    },\n  ],\n};\n\nconsole.log(printSync(program).code);\n// console.log(\"Hello!\");\n```\n\n### TypeScript and TSX\n\nSet `ts` when the AST can contain TypeScript nodes. For TSX, set both `ts` and `jsx`:\n\n```js\nconst { program } = parseSync(\"component.tsx\", \"const Box = <T,>(value: T) => <div>{value}</div>\");\n\nconst { code } = printSync(program, {\n  ts: true,\n  jsx: true,\n});\n```\n\n## API\n\n### `printSync(node, options?)`\n\n```ts\nfunction printSync(\n  node: ESTree.Program | ESTree.Statement,\n  options?: Options,\n): {\n  code: string;\n  map: SourceMap | null;\n};\n```\n\nPrints a complete `Program` or a single statement and returns the generated source code,\nand (when requested) a standard Source Map v3 object.\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst sourceText = \"const answer=6*7\";\nconst { program } = parseSync(\"input.js\", sourceText);\nconst { code, map } = printSync(program, {\n  sourcemap: true,\n  sourceFilename: \"input.js\",\n  sourceText,\n});\n```\n\nSource-map mappings require `sourceText` and nodes with valid Oxc `start` / `end` offsets.\nA manually constructed AST without offsets can still be printed, but its source map has\nan empty `mappings` string.\n\n### Options\n\n| Option                | Type      | Default | Description                                                      |\n| :-------------------- | :-------- | :------ | :--------------------------------------------------------------- |\n| `indent`              | `string`  | `\"\\t\"`  | Non-empty string of spaces and/or tabs used for one indent level |\n| `startingIndentLevel` | `number`  | `0`     | Starting indent level, from `0` to `1000`                        |\n| `jsx`                 | `boolean` | `false` | Enable TSX-safe printing for ambiguous TypeScript syntax         |\n| `ts`                  | `boolean` | `false` | Select the printer that supports TypeScript nodes                |\n| `sourcemap`           | `boolean` | `false` | Return a Source Map v3 object in `map`                           |\n| `sourceFilename`      | `string`  | `\"\"`    | Original source filename recorded in the source map              |\n| `sourceText`          | `string`  | -       | Original text required for source-map mappings and content       |\n\n## Why pure JavaScript?\n\nMost Oxc packages use native bindings. This package deliberately does not: when an AST already\nlives in JavaScript, passing the entire object graph across a JS/native boundary can cost more than\nprinting it in place. `oxc-codegen` avoids that serialization and uses specialized printer builds\nfor JavaScript and TypeScript workloads.\n\nSee [DESIGN.md](https://github.com/oxc-project/oxc/blob/main/packages/codegen/DESIGN.md) for the\nimplementation details and performance constraints.\n\n## Current limitations\n\n- Comments are not printed.\n- Minified output is not supported.\n\n## Benchmarks\n\nRepresentative time per `printSync` call:\n\n| Fixture                      |     Bytes |       Time |\n| :--------------------------- | --------: | ---------: |\n| `tiny.js`                    |        26 |  0.0001 ms |\n| `RadixUIAdoptionSection.jsx` |     2,518 |  0.0070 ms |\n| `react.development.js`       |    72,141 |  0.1518 ms |\n| `binder.ts`                  |   193,077 |  0.3364 ms |\n| `App.tsx`                    |   415,340 |  1.2912 ms |\n| `lodash.js`                  |   544,096 |  0.7977 ms |\n| `kitchen-sink.tsx`           |   732,222 |  4.2924 ms |\n| `antd.js`                    | 6,683,633 | 16.9652 ms |\n\nThese figures come from one machine and are illustrative, not a regression baseline.\nResults vary between runs, most noticeably for large fixtures such as `antd.js`.\n","isInternal":false,"tokens":1322,"sizeBytes":5186},{"name":"README.md","path":"packages/codegen/test/fixtures/README.md","rawUrl":"https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/test/fixtures/README.md","title":"fixtures Documentation","category":"plugin-manifest","format":"markdown","content":"# fixtures Documentation\n\nPath: `packages/codegen/test/fixtures/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/test/fixtures/README.md)","tokens":300,"sizeBytes":201},{"name":".cursorrules","path":".cursorrules (typescript-expo-jest-detox-cursorrules-prompt-file.mdc)","rawUrl":"https://raw.githubusercontent.com/PatrickJS/awesome-cursorrules/main/rules/typescript-expo-jest-detox-cursorrules-prompt-file.mdc","title":"Awesome Cursorrules: typescript expo jest detox","category":"cursor-rule","format":"markdown","content":"---\ndescription: \"Cursor rules for TypeScript development with Expo, Jest, and Detox integration.\"\nglobs: **/*\nalwaysApply: false\n---\nYou are an expert in TypeScript, React Native, Expo, and Mobile UI development.\n\nCode Style and Structure\n\nNaming Conventions\nTypeScript Usage\nSyntax and Formatting\nUI and Styling\nSafe Area Management\nPerformance Optimization\nNavigation\nState Management\nError Handling and Validation\nTesting\nSecurity\nInternationalization (i18n)\n\nKey Conventions\n\nAPI Documentation\n\nRefer to Expo's documentation for detailed information on Views, Blueprints, and Extensions for best practices.\n","tokens":124,"sizeBytes":612}],"systemPromptSnippet":"<yakaai_skills repo=\"oxc-project/oxc\">\n<!-- File: AGENTS.md (Tokens: ~3304 | Category: root-instruction) -->\n# AGENTS.md - AI Assistant Guide for Oxc\n\nOxc is a high-performance JavaScript/TypeScript toolchain written in Rust containing:\n\n- Parser (JS/TS with AST), Linter (oxlint), Formatter (oxfmt), Transformer, Minifier\n\n## AI Usage Policy for Contributors\n\n**IMPORTANT**: If you are an AI assistant helping a human contributor:\n\n- **Disclose AI usage** - Contributors must disclose when AI tools were used to reduce maintainer fatigue\n- **Full responsibility** - The human contributor is responsible for all AI-generated issues or PRs they submit\n- **Quality standards** - Low-quality or unreviewed AI content will be closed immediately\n\nAll AI-generated code must be thoroughly reviewed, tested, and understood by the contributor before submission. Code should meet Oxc's performance and quality standards.\n\n- **Ban policy** - Contributors who submit repeated low-quality (\"slop\") PRs will be banned without prior warning. Bans may be lifted if you commit to contributing to Oxc in accordance with the policy above. You may request an unban via our Discord.\n\n## Repository Structure\n\nRust workspace with key directories:\n\n- `crates/` - Core functionality (start here when exploring)\n- `apps/` - Application binaries (oxlint, oxfmt)\n- `napi/` - Node.js bindings\n- `npm/` - npm packages\n- `tasks/` - Development tools/automation\n\nAvoid editing `generated` subdirectories.\n\n### Core Crates\n\n- `oxc_parser` - JS/TS parser\n- `oxc_ast` - AST definitions/utilities\n- `oxc_semantic` - Semantic analysis/symbols/scopes\n- `oxc_linter` - Linting engine/rules\n- `oxc_formatter` - Code formatting (Prettier-like for JS)\n- `oxc_formatter_*` - Code formatting (Prettier-like for other languages)\n- `oxc_transformer` - Code transformation (Babel-like)\n- `oxc_minifier` - Code minification\n- `oxc_codegen` - Code generation\n- `oxc_isolated_declarations` - TypeScript declaration generation\n- `oxc_diagnostics` - Error reporting\n- `oxc_traverse` - AST traversal utilities\n- `oxc_allocator` - Memory management\n- `oxc_language_server` - LSP server for editor integration\n- `oxc` - Main crate\n\n## Development Commands\n\nPrerequisites: Rust (MSRV: 1.97), Node.js, pnpm, just\n\n**Setup Notes:**\n\n- All tools already installed (`cargo-insta`, `typos-cli`, `cargo-shear`, `ast-grep`)\n- Rust components already installed (`clippy`, `rust-docs`, `rustfmt`)\n- Use Conventional Commits for commit messages; `.github/workflows/pr.yml` requires a scoped title like `fix(parser): handle trailing comma`\n- Choose verification from the changed code; reserve `just ready` for an optional broad integration checkpoint as described below.\n- You run in an environment where `ast-grep` is available; whenever a search requires syntax-aware or structural matching, default to `ast-grep --lang rust -p '<pattern>'` (or set `--lang` appropriately) and avoid falling back to text-only tools like `rg` or `grep` unless I explicitly request a plain-text search.\n\n### Essential Commands\n\n```bash\njust fmt             # Format code (run after modifications)\njust test            # Run unit/integration tests\njust conformance     # Run conformance tests\njust ready           # Optional broad integration checkpoint (see below)\ncargo lintgen        # Regenerate linter rules enum and impls after adding/modifying rules\ncargo lint-timings   # Update linter timing data after changing linter rule codegen\n\n# Crate-specific updates\njust ast             # Update generated files (oxc_ast changes)\njust minsize         # Update size snapshots (oxc_minifier changes)\njust allocs          # Update allocation snapshots (oxc_parser changes)\n\n# Useful shortcuts\njust watch \"command\" # Watch files and re-run command\njust example tool    # Run tool example (e.g., just example linter)\n```\n\nMore commands can be found in `justfile`.\n\n### Local and stacked development\n\nBefore selecting verification checks, read the [verification guidance](.agents/verification.md).\n\n- Select checks from changed code and affected consumers; for a stack, use each PR's diff against its parent.\n- Reuse passing results while tested inputs remain unchanged. Reserve `just ready` for stable integration checkpoints when broad validation is needed.\n- Preserve required subsystem checks and per-PR CI; a passing stack tip does not establish that intermediate PRs pass.\n\n## Manual Testing & Examples\n\nRun crate examples for quick testing and debugging:\n\n```bash\ncargo run -p <crate_name> --example <example_name> -- [args]\n\n# Common examples:\ncargo run -p oxc_parser --example parser -- test.js\ncargo run -p oxc_linter --example linter -- src/\ncargo run -p oxc_transformer --example transformer -- input.js\ncargo run -p oxc --example compiler --features=\"full\" -- test.js\n```\n\nModify examples in `crates/<crate_name>/examples/` to test specific scenarios.\n\n## Code Navigation\n\n### Key Locations\n\n- AST: Start with `oxc_ast`, use `oxc_ast_visit` for traversal\n- Linting rules: `crates/oxc_linter/src/rules/` (visitor pattern)\n- Parser: `crates/oxc_parser/src/lib.rs`, lexer in `src/lexer/`\n- Tests: Co-located with source, integration in `tests/`, uses `insta` for snapshots\n\n### Conventions\n\n- Use `oxc_allocator` for memory management\n- Follow rustfmt config in `.rustfmt.toml`\n- Use `oxc_diagnostics` for errors with source locations\n- Performance-critical: avoid unnecessary allocations\n\n## Common Tasks\n\n### Adding Linting Rule\n\n1. Use rule generator: `just new-rule <name>` (ESLint rules)\n   - Or plugin-specific: `just new-ts-rule`, `just new-jest-rule`, etc.\n2. Implement using visitor pattern\n3. Add tests in same module\n4. Register in appropriate category\n\n### Parser Changes\n\n1. Research and test grammar changes thoroughly\n2. Update AST definitions in `oxc_ast` if needed\n3. Ensure existing tests pass\n4. Add tests for new features\n\n### Working with Transformations\n\n1. Understand AST structure first\n2. Use visitor pattern for traversal\n3. Handle node ownership/allocator carefully\n4. Test with various input patterns\n\n## Testing\n\nOxc uses multiple testing approaches tailored to each crate:\n\n- **Unit/Integration tests**: Standard Rust tests in `tests/` directories\n- **Conformance tests**: Against external suites (Test262, Babel, TypeScript)\n- **Snapshot tests**: Track failures and expected outputs using `insta`\n\n### Quick Test Commands\n\n```bash\njust test                              # Run all Rust tests\njust conformance                       # Run all conformance tests (alias: cargo coverage)\ncargo test -p <crate_name>             # Test specific crate\n\n# Conformance for specific tools\ncargo coverage parser                  # Parser conformance\ncargo coverage transformer             # Transformer conformance\ncargo run -p oxc_transform_conformance # Transformer Babel tests\n\n# NAPI packages\npnpm test                              # Test all Node.js bindings\n```\n\n### Crate-Specific Testing\n\nEach crate follows distinct testing patterns:\n\n#### oxc_parser\n\n- **Conformance only** via `tasks/coverage`\n- **Command**: `cargo coverage parser`\n- **Suites**: Test262, Babel, TypeScript\n- **Special**: `just allocs` for allocation tracking\n\n#### oxc_linter\n\n- **Inline tests** in rule files (`src/rules/**/*.rs`)\n- **Pattern**: Use `Tester` helper with pass/fail cases\n\n```rust\n#[test]\nfn test() {\n    Tester::new(RuleName::NAME, RuleName::PLUGIN, pass, fail)\n        .test_and_snapshot();\n}\n```\n\n#### oxc_formatter (and other `oxc_formatter_*` language crates)\n\n- Read the target crate's `AGENTS.md`; it includes the shared formatter policy plus language-specific rules and verification commands\n\n#### oxc_minifier\n\n- **Unit tests** in `tests/` subdirectories:\n  - `ecmascript/` - Operations\n  - `peephole/` - Optimizations\n  - `mangler/` - Name mangling\n- **Size tracking**: `just minsize`\n\n#### oxc_transformer\n\n- **Multiple approaches**:\n  - Unit tests: `tests/integrations/`\n  - Conformance: `tasks/transform_conformance/`\n  - Babel plugins: `tasks/transform_conformance/tests/babel-plugin-*/`\n- **Commands**:\n\n```bash\ncargo test -p oxc_transformer                    # Unit tests\ncargo run -p oxc_transform_conformance          # Conformance\njust test-transform --filter <path>             # Filter tests\n```\n\n#### oxc_codegen\n\n- **Integration tests** in `tests/integration/`\n- Test files: `js.rs`, `ts.rs`, `sourcemap.rs`, `comments.rs`\n\n#### oxc_isolated_declarations\n\n- **Snapshot testing** with `insta`\n- Input: `tests/fixtures/*.{ts,tsx}`\n- Output: `tests/snapshots/*.snap`\n- Update: `cargo insta review`\n\n#### oxc_semantic\n\n- **Multiple testing approaches**:\n  - **Conformance tests** (`tests/conformance/`) - Contract-as-code tests for symbols and identifier references\n  - **Integration tests** (`tests/integration/`) - Tests for scopes, symbols, modules, classes, CFG\n  - **Snapshot tests** (`tests/main.rs`) - Verifies scoping data correctness (scope trees, bindings, symbols, references) using `insta` snapshots from `fixtures/`\n  - **Coverage tests** - Via `tasks/coverage` using Test262, Babel, TypeScript suites\n- **Command**: `cargo test -p oxc_semantic`\n- **Update snapshots**: `cargo insta review`\n\n#### Other Crates\n\n- **oxc_traverse**: AST traversal - `cargo test -p oxc_traverse`\n- **oxc_ecmascript**: ECMAScript operations - `cargo test -p oxc_ecmascript`\n- **oxc_regular_expression**: Regex parsing - `cargo test -p oxc_regular_expression`\n- **oxc_syntax**: Syntax utilities - `cargo test -p oxc_syntax`\n- **oxc_language_server**: Editor integration - `cargo test -p oxc_language_server`\n\n### Conformance Testing Foundation\n\n**CRITICAL**: These external test suites are the CORE of Oxc's testing strategy, providing thousands of real-world test cases from mature JavaScript ecosystem projects. They ensure Oxc correctly handles the full complexity of JavaScript/TypeScript.\n\nSuites cloned via `just submodules`:\n\n| Submodule            | Description                                                                                                                                        | Location                            | Used by Crates                                           |\n| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | -------------------------------------------------------- |\n| `test262`            | **ECMAScript Conformance Suite**<br>Official JavaScript test suite from TC39, testing compliance with the ECMAScript specification                 | `tasks/coverage/test262`            | parser, semantic, codegen, transformer, minifier, estree |\n| `babel`              | **Babel Test Suite**<br>Comprehensive transformation and parsing tests from the Babel compiler, covering modern JavaScript features and edge cases | `tasks/coverage/babel`              | parser, semantic, codegen, transformer, minifier         |\n| `typescript`         | **TypeScript Test Suite**<br>Microsoft's TypeScript compiler tests, ensuring correct handling of TypeScript syntax and semantics                   | `tasks/coverage/typescript`         | parser, semantic, codegen, transformer, estree           |\n| `estree-conformance` | **ESTree Conformance Tests**<br>Test262, TypeScript, and acorn-jsx suites adapted for ESTree format validation, ensuring correct AST structure     | `tasks/coverage/estree-conformance` | estree                                                   |\n\n**These suites provide:**\n\n- **Thousands of battle-tested cases** from real-world usage\n- **Edge case coverage** that would be impossible to write manually\n- **Industry standard compliance** ensuring compatibility\n- **Continuous validation** against evolving JavaScript standards\n\nRun all conformance tests with `cargo coverage` or `just conformance`.\n\n### Searching Test Suites\n\nThese test suites are pre-cloned and ready to search:\n\n- **Test262** (`tasks/coverage/test262/`) - ECMAScript spec compliance\n- **Babel** (`tasks/coverage/babel/`) - Parsing and transformation edge cases\n- **TypeScript** (`tasks/coverage/typescript/`) - TypeScript syntax and semantics\n\nNOTE: These suites are script-cloned and fully gitignored. ripgrep respects `.gitignore`, so a plain `rg` inside them silently returns nothing.\nUse `rg --no-ignore` (or `-u`) when searching them.\n\n### Snapshot Testing\n\n- Uses `insta` crate for snapshot testing\n- Snapshots track **failing** tests, not passing ones\n- Located in `tasks/coverage/snapshots/` and conformance directories\n- Update with `cargo insta review` after changes\n- Formats: `.snap` (counts), `.snap.md` (detailed failures)\n\n### NAPI (Node.js Bindings) Testing\n\nNAPI packages use **Vitest** for testing Node.js bindings:\n\n```bash\npnpm run build-test   # Build all NAPI packages\npnpm test             # Run all NAPI tests\n```\n\n**Package-specific commands:**\n\n- `oxc-parser`: `cd napi/parser && pnpm run build-test && pnpm test` (also has `pnpm test-browser`)\n- `oxc-minify`: `cd napi/minify && pnpm run build-test && pnpm test`\n- `oxc-transform`: `cd napi/transform && pnpm run build-test && pnpm test`\n\nTests are TypeScript files in each package's `test/` directory.\n\n### Where to Add Tests\n\n| Crate                 | Location                                 |\n| --------------------- | ---------------------------------------- |\n| Parser                | `tasks/coverage/misc/pass/` or `fail/`   |\n| Linter                | Inline in rule files                     |\n| Formatter             | `tests/fixtures/` in the formatter crate |\n| Minifier              | `tests/` subdirectories                  |\n| Transformer           | `tests/integrations/` or Babel fixtures  |\n| Codegen               | `tests/integration/`                     |\n| Isolated Declarations | `tests/fixtures/*.ts`                    |\n| Semantic              | `tests/` directory                       |\n| NAPI packages         | `test/` directory (Vitest)               |\n| Language Server       | Inline and `/fixtures`                   |\n\n### Linter Codegen Changes\n\nWhen changing linter codegen files such as `tasks/linter_codegen/**` or generated linter rule runner files under `crates/oxc_linter/src/generated/`, run `cargo lintgen` and `cargo lint-timings` so generated rule metadata and linter timing data stay in sync.\n\n## Notes\n\n- Rapidly evolving project - APIs may change\n- Performance is critical for all changes\n- Maintain JS/TS standard compatibility\n- Breaking changes need documentation and discussion\n\n---\n\nFor human contributors see `CONTRIBUTING.md` and [oxc.rs](https://oxc.rs/docs/contribute/introduction.html)\n\n<!-- File: ARCHITECTURE.md (Tokens: ~2862 | Category: project-spec) -->\n# Architecture\n\n## System Overview\n\nOxc (The Oxidation Compiler) is a collection of high-performance JavaScript and TypeScript tools written in Rust. The system is designed as a modular, composable set of compiler components that can be used independently or together to build complete toolchains for JavaScript/TypeScript development.\n\n### Core Mission\n\n- **Performance**: Deliver faster performance than existing JavaScript tools\n- **Correctness**: Maintain compatibility with JavaScript/TypeScript standards\n- **Modularity**: Enable users to compose tools according to their specific needs\n- **Developer Experience**: Provide excellent error messages and tooling integration\n\n## High-Level Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                          Applications                           │\n├─────────────────────────────────────────────────────────────────┤\n│  oxlint  │  Language Server  │  NAPI Bindings  │  Future Tools  │\n├─────────────────────────────────────────────────────────────────┤\n│                        Core Libraries                           │\n├─────────────────────────────────────────────────────────────────┤\n│ Parser │ Semantic │ Linter │ Transformer │ Minifier │ Codegen   │\n├─────────────────────────────────────────────────────────────────┤\n│                    Foundation Libraries                         │\n├─────────────────────────────────────────────────────────────────┤\n│    AST    │  Allocator  │  Diagnostics  │   Span   │  Syntax    │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Architecture Principles\n\n### 1. Zero-Copy Architecture\n\nThe system is built around an arena allocator (`oxc_allocator`) that enables zero-copy operations throughout the compilation pipeline. All AST nodes are allocated in a single arena, eliminating the need for reference counting or garbage collection.\n\n### 2. Visitor Pattern\n\nAST traversal is implemented using the visitor pattern (`oxc_ast_visit`) with automatic visitor generation through procedural macros. This ensures type safety and performance while maintaining code clarity.\n\n### 3. Shared Infrastructure\n\nCommon functionality like error reporting (`oxc_diagnostics`), source positions (`oxc_span`), and syntax definitions (`oxc_syntax`) are shared across all components to ensure consistency.\n\n## Core Components\n\n### Foundation Layer\n\n#### oxc_allocator\n\n- **Purpose**: Arena-based memory allocator for zero-copy operations\n- **Key Features**:\n  - Single allocation arena for entire compilation unit\n  - Eliminates need for Rc/Arc in hot paths\n  - Enables structural sharing of AST nodes\n- **Dependencies**: None (foundational)\n\n#### oxc_span\n\n- **Purpose**: Source position tracking and text manipulation\n- **Key Features**:\n  - Byte-based indexing for UTF-8 correctness\n  - Efficient span operations for source maps\n  - Integration with diagnostic reporting\n- **Dependencies**: None (foundational)\n\n#### oxc_syntax\n\n- **Purpose**: JavaScript/TypeScript language definitions\n- **Key Features**:\n  - Token definitions and keyword mappings\n  - Language feature flags and compatibility\n  - Shared syntax validation logic\n- **Dependencies**: oxc_span\n\n#### oxc_diagnostics\n\n- **Purpose**: Error reporting and diagnostic infrastructure\n- **Key Features**:\n  - Rich error messages with source context\n  - Multiple output formats (JSON, pretty-printed)\n  - Integration with language server protocol\n- **Dependencies**: oxc_span\n\n#### oxc_ast\n\n- **Purpose**: Abstract Syntax Tree definitions and utilities\n- **Key Features**:\n  - Complete JavaScript/TypeScript AST coverage\n  - Generated visitor traits for type safety\n  - Serialization support for caching\n- **Dependencies**: oxc_allocator, oxc_span, oxc_syntax\n\n##### AST Design Principles\n\nThe Oxc AST differs significantly from the [estree](https://github.com/estree/estree) AST specification by removing ambiguous nodes and introducing distinct types. While many existing JavaScript tools rely on estree as their AST specification, a notable drawback is its abundance of ambiguous nodes that often leads to confusion during development.\n\nFor example, instead of using a generic estree `Identifier`, the Oxc AST provides specific types such as:\n\n- `BindingIdentifier` - for variable declarations and bindings\n- `IdentifierReference` - for variable references\n- `IdentifierName` - for property names and labels\n\nThis clear distinction greatly enhances the development experience by aligning more closely with the ECMAScript specification and providing better type safety.\n\n### Core Processing Layer\n\n#### oxc_parser\n\n- **Purpose**: JavaScript/TypeScript parsing\n- **Key Features**:\n  - Hand-written recursive descent parser\n  - Full ES2024+ and TypeScript support\n  - Preservation of comments and trivia\n- **Dependencies**: oxc_allocator, oxc_ast, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_semantic\n\n- **Purpose**: Semantic analysis and symbol resolution\n- **Key Features**:\n  - Scope chain construction\n  - Symbol table generation\n  - Dead code detection\n- **Dependencies**: oxc_ast, oxc_cfg, oxc_diagnostics, oxc_span, oxc_syntax\n\n#### oxc_linter\n\n- **Purpose**: ESLint-compatible linting engine\n- **Key Features**:\n  - 200+ built-in rules\n  - Plugin architecture for custom rules\n  - Automatic fixing for many rules\n  - Configuration compatibility with ESLint\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_diagnostics, oxc_cfg\n\n#### oxc_transformer\n\n- **Purpose**: Code transformation and transpilation\n- **Key Features**:\n  - TypeScript to JavaScript transformation\n  - Modern JavaScript feature transpilation\n  - React JSX transformation\n  - Babel plugin compatibility layer\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_allocator\n\n#### oxc_minifier\n\n- **Purpose**: Code size optimization\n- **Key Features**:\n  - Dead code elimination\n  - Constant folding and propagation\n  - Identifier mangling integration\n  - Statement and expression optimization\n- **Dependencies**: oxc_ast, oxc_semantic, oxc_mangler\n\n#### oxc_codegen\n\n- **Purpose**: AST to source code generation\n- **Key Features**:\n  - Configurable output formatting\n  - Source map generation\n  - Comment preservation options\n  - Minified and pretty-printed output modes\n- **Dependencies**: oxc_ast, oxc_span\n\n### Application Layer\n\n#### oxlint (apps/oxlint)\n\n- **Purpose**: Command-line linter application\n- **Key Features**:\n  - File discovery and parallel processing\n  - Configuration file support\n  - Multiple output formats\n  - Integration with CI/CD systems\n- **Dependencies**: oxc_linter, oxc_parser, oxc_semantic\n\n#### Language Server (oxc_language_server)\n\n- **Purpose**: LSP implementation for editor integration\n- **Key Features**:\n  - Real-time diagnostics\n  - Go-to-definition and references\n  - Symbol search and completion\n- **Dependencies**: All core components\n\n#### NAPI Bindings (napi/\\*)\n\n- **Purpose**: Node.js integration layer\n- **Key Features**:\n  - Parser bindings for JavaScript tooling\n  - Linter integration for build tools\n  - Transform pipeline for bundlers\n  - Async processing support\n- **Dependencies**: Core components + Node.js FFI\n\n## Data Flow\n\n### Compilation Pipeline\n\n1. **Input**: Source text + configuration\n2. **Lexing/Parsing**: `oxc_parser` → AST + comments\n3. **Semantic Analysis**: `oxc_semantic` → Symbol table + scope info\n4. **Processing**: Tool-specific analysis (linting, transformation, etc.)\n5. **Output**: Results (diagnostics, transformed code, etc.)\n\n### Memory Management Flow\n\n```\nSource Text → Arena Allocator → AST Nodes → Visitors → Results\n     ↓              ↓              ↓           ↓          ↓\n   UTF-8          Arena         Borrowed    Zero-copy   Owned\n  String         Memory         References  Processing  Output\n```\n\n## Quality Attributes\n\n### Performance\n\n- **Target**: 10-100x faster than comparable tools\n- **Strategies**:\n  - Arena allocation for memory efficiency\n  - Zero-copy data structures\n  - Parallel processing where possible\n  - Minimal allocations in hot paths\n\n#### Parser Performance Implementation\n\n- AST is allocated in a memory arena (oxc_allocator) for fast AST memory allocation and deallocation\n- Short strings are inlined by [CompactString](https://crates.io/crates/compact_str)\n- No other heap allocations are done except the above two\n- Scope binding, symbol resolution and some syntax errors are not done in the parser, they are delegated to the semantic analyzer\n\n#### Linter Performance Implementation\n\n- Oxc parser is used for optimal performance\n- AST visit is a fast operation due to linear memory scan from the memory arena\n- Files are linted in a multi-threaded environment, so scales with the total number of CPU cores\n- Every single lint rule is tuned for performance\n\n### Correctness\n\n- **Target**: 100% compatibility with language standards\n- **Strategies**:\n  - Comprehensive test suites\n  - Real-world codebase testing\n  - Conformance testing against official specs\n  - Conservative error handling\n\n### Maintainability\n\n- **Target**: Clear, reviewable, extensible codebase\n- **Strategies**:\n  - Strong type system usage\n  - Procedural macro code generation\n  - Clear separation of concerns\n  - Comprehensive documentation\n\n### Usability\n\n- **Target**: Drop-in replacement for existing tools\n- **Strategies**:\n  - Configuration compatibility\n  - Familiar CLI interfaces\n  - Rich error messages\n  - Editor integration\n\n## Technical Constraints\n\n### Language Choice\n\n- **Rust**: Chosen for memory safety, performance, and zero-cost abstractions\n- **MSRV**: N-2 policy for stability\n\n### Memory Model\n\n- **Arena Allocation**: Single arena per compilation unit\n- **Lifetime Management**: Explicit lifetimes tied to arena\n- **No Garbage Collection**: Manual memory management for predictable performance\n\n### Threading Model\n\n- **File-level Parallelism**: Multiple files processed in parallel\n- **Single-threaded Pipeline**: Each file processed by single thread\n- **Shared State**: Minimal shared state to avoid synchronization overhead\n\n### Compatibility Requirements\n\n- **JavaScript**: ES2024+ compatibility\n- **TypeScript**: Latest TypeScript syntax support\n- **Node.js**: LTS versions through NAPI bindings\n- **Editors**: LSP compatibility for all major editors\n\n## Design Decisions\n\n### Arena Allocator Choice\n\n**Decision**: Use custom arena allocator instead of Rc/Arc\n**Rationale**:\n\n- Eliminates reference counting overhead\n- Enables zero-copy string operations\n- Simplifies memory management\n- Improves cache locality\n\n**Trade-offs**:\n\n- ✅ 10-50% performance improvement\n- ✅ Simplified ownership model\n- ❌ Requires lifetime management\n- ❌ Less flexible memory patterns\n\n### Hand-written Parser\n\n**Decision**: Implement recursive descent parser instead of parser generator\n**Rationale**:\n\n- Easier debugging and maintenance\n- More efficient generated code\n- Faster compilation times\n\n**Trade-offs**:\n\n- ✅ Better performance and error messages\n- ✅ More maintainable code\n- ❌ More manual implementation work\n- ❌ Higher risk of parser bugs\n\n### Visitor Pattern\n\n**Decision**: Use visitor pattern with procedural macros\n**Rationale**:\n\n- Type-safe AST traversal\n- Automatic visitor generation\n- Consistent patterns across tools\n- Efficient dispatch\n\n**Trade-offs**:\n\n- ✅ Type safety and performance\n- ✅ Reduced boilerplate code\n- ❌ Compile-time complexity\n- ❌ Learning curve for contributors\n\n## Future Considerations\n\n### Planned Extensions\n\n- **Formatter**: Complete code formatting tool\n- **Bundler**: Integration with bundling workflows\n- **Type Checker**: Full TypeScript type checking\n- **Plugin System**: User-defined transformations\n\n### Scalability Concerns\n\n- **Large Codebases**: Processing optimization improvements\n- **Memory Usage**: Streaming processing for huge files\n- **Parallel Processing**: Fine-grained parallelization\n\n### Technology Evolution\n\n- **Rust Evolution**: Leveraging new language features\n- **JavaScript Standards**: Keeping pace with TC39 proposals\n- **Editor Integration**: Advanced IDE features\n\n## Development Infrastructure\n\n### Test Infrastructure\n\nCorrectness and reliability are taken extremely seriously in Oxc. We spend significant effort on strengthening the test infrastructure to prevent problems from propagating to downstream tools:\n\n- **Conformance Testing**: Test262, Babel, and TypeScript conformance suites\n- **Fuzzing**: Extensive fuzzing to discover edge cases\n- **Snapshot Testing**: Linter diagnostic snapshots for regression prevention\n- **Ecosystem CI**: Testing against real-world codebases\n- **Idempotency Testing**: Ensuring transformations are stable\n- **Code Coverage**: Comprehensive coverage tracking\n- **End-to-End Testing**: Testing against top 3000 npm packages\n\n### Build and Development Tools\n\n- **Rust**: MSRV 1.86.0+ with clippy and rustfmt integration\n- **Just**: Command runner for development tasks (`just --list` for available commands)\n- **Performance Monitoring**: Continuous benchmarking and performance regression detection\n- **Cross-platform**: Support for Linux, macOS, and Windows\n- **CI/CD**: Automated testing, building, and publishing pipelines\n\nFor detailed development guidelines, see [CONTRIBUTING.md](./CONTRIBUTING.md) and [AGENTS.md](./AGENTS.md).\n\n---\n\nThis architecture document follows the [architecture.md](https://architecture.md/) format for documenting software architecture decisions and system design.\n\n<!-- File: .agents/skills/insta-snapshots/SKILL.md (Tokens: ~285 | Category: anthropic-skill) -->\n---\nname: insta-snapshots\ndescription: Review and update Oxc Insta snapshots non-interactively after expected output changes or snapshot test failures.\n---\n\n# Insta snapshots\n\nRun the affected test to generate pending snapshots. Use `cargo insta pending-snapshots` and read the old `.snap` and new `.snap.new` outputs to decide whether each change matches the intended behavior.\n\nUse the CLI instead of the interactive `cargo insta review` UI or manually editing expected output:\n\n```bash\ncargo insta pending-snapshots\ncargo insta accept --snapshot <snapshot_name>\n```\n\nScope acceptance to the reviewed snapshots from this task; check `cargo insta accept --help` if a name or package filter is ambiguous. Avoid workspace-wide acceptance or rejection when unrelated pending snapshots exist. Fix unexpected output in the implementation before accepting it.\n\nAfter accepting, inspect the snapshot diff and rerun the affected test. Finish with the intended `.snap` updates and no task-generated `.snap.new` files left pending. Updating snapshots does not itself call for a commit.\n\nSnapshots record expected output, including successful output in some crates. Linter diagnostic snapshots are under `crates/oxc_linter/src/snapshots/`; other crates have their own locations. Conformance `.snap.md` reports use a separate update mechanism; inspect the producing test harness rather than treating them as Insta pending snapshots.\n\n<!-- File: .agents/skills/migrate-oxfmt/SKILL.md (Tokens: ~3188 | Category: anthropic-skill) -->\n---\nname: migrate-oxfmt\ndescription: Guide for migrating a project from Prettier or Biome to Oxfmt. Use when asked to migrate, convert, or switch a project's formatter from Prettier or Biome to Oxfmt.\n---\n\nThis skill guides you through migrating a project from Prettier or Biome to [Oxfmt](https://oxc.rs/docs/guide/usage/formatter).\n\n## Overview\n\nOxfmt is a high-performance, Prettier-compatible code formatter. Most Prettier options are supported directly.\n\nAn automated migration tool is built into Oxfmt, supporting both Prettier and Biome as migration sources.\n\n## Step 1: Run Automated Migration\n\nFirst, decide whether the automated tool fits:\n\n- Static config (JSON, or a JS/TS config that just exports a plain object): use `--migrate` below.\n- Dynamic JS/TS config (logic such as environment branches or computed values): `--migrate` writes only a resolved snapshot of the values, and the logic does not survive (a warning is printed). Port the logic to `oxfmt.config.ts` manually with the option mappings in this guide.\n- Nested per-directory configs: `--migrate` only handles the config in the current directory. Run it again in each nested directory (see \"Nested Config\" in Step 2).\n\n### From Prettier\n\n```bash\nnpx oxfmt@latest --migrate prettier\n```\n\nThis will:\n\n- Find your Prettier config in the current directory, in the same order as Prettier\n  - Supported: `package.json` (`prettier` field), `.prettierrc` (JSON only), `.prettierrc.json`, and JS/TS configs (`.prettierrc.*`, `prettier.config.*`)\n  - Shareable configs (e.g. `\"prettier\": \"@company/prettier-config\"`) are resolved\n  - YAML, JSON5 and TOML configs are not supported. Convert them to JSON or JS first\n- Create `.oxfmtrc.json` with migrated options\n  - Only Prettier-compatible options are migrated, others are skipped with a warning\n- Migrate `overrides`\n  - `overrides[].options.parser` is migrated to `associations[].language`\n- Migrate `.prettierignore` patterns to `ignorePatterns`\n- Migrate `prettier-plugin-tailwindcss` options to `sortTailwindcss`\n- Migrate `prettier-plugin-svelte` options to `svelte` (`svelteSortOrder` → `sortOrder`, `svelteAllowShorthand` → `allowShorthand`, `svelteIndentScriptAndStyle` → `indentScriptAndStyle`)\n- Migrate `prettier-plugin-astro` options to `astro` (`astroAllowShorthand` → `allowShorthand`, `astroSkipFrontmatter` → `skipFrontmatter`, `astroCompressHTML` → `compressHTML`)\n- Detect `prettier-plugin-packagejson` and enable `sortPackageJson`\n- Skip other plugins with a warning\n\nNotes (Prettier):\n\n- Fails if no Prettier config is found in the current directory. Use `--init` to create a blank `.oxfmtrc.json` instead.\n- The generated `.oxfmtrc.json` may not be formatted yet. Run `oxfmt` after migration.\n\n### From Biome\n\n```bash\nnpx oxfmt@latest --migrate biome\n```\n\nThis will:\n\n- Find and read `biome.json` or `biome.jsonc`\n- Create `.oxfmtrc.json` with migrated options\n- Migrate negated patterns from `files.includes` to `ignorePatterns`\n- Map Biome's two-level config (`formatter.*` and `javascript.formatter.*`) to Oxfmt options\n\nBiome option mapping:\n\n| Biome                                                       | Oxfmt                             |\n| ----------------------------------------------------------- | --------------------------------- |\n| `formatter.indentStyle` (`\"tab\"`/`\"space\"`)                 | `useTabs` (`true`/`false`)        |\n| `formatter.indentWidth`                                     | `tabWidth`                        |\n| `formatter.lineWidth`                                       | `printWidth`                      |\n| `javascript.formatter.quoteStyle`                           | `singleQuote`                     |\n| `javascript.formatter.jsxQuoteStyle`                        | `jsxSingleQuote`                  |\n| `javascript.formatter.quoteProperties` (`\"asNeeded\"`)       | `quoteProps` (`\"as-needed\"`)      |\n| `javascript.formatter.trailingCommas`                       | `trailingComma`                   |\n| `javascript.formatter.semicolons` (`\"always\"`/`\"asNeeded\"`) | `semi` (`true`/`false`)           |\n| `javascript.formatter.arrowParentheses` (`\"asNeeded\"`)      | `arrowParens` (`\"avoid\"`)         |\n| `javascript.formatter.bracketSameLine`                      | `bracketSameLine`                 |\n| `formatter.bracketSpacing`                                  | `bracketSpacing`                  |\n| `formatter.attributePosition` (`\"multiline\"`)               | `singleAttributePerLine` (`true`) |\n\nNotes (Biome):\n\n- For `formatter.*` options in the table, a `javascript.formatter.*` value of the same name takes precedence when present. `bracketSameLine` is read only from `javascript.formatter.bracketSameLine`.\n- Options not set in `biome.json` are written to `.oxfmtrc.json` with Biome's default values explicitly (e.g. `printWidth: 80`, `useTabs: true`), so the output preserves Biome's formatting behavior.\n- If no Biome config is found, creates a blank `.oxfmtrc.json` instead.\n- `overrides` cannot be auto-migrated and must be converted manually (a warning is printed if detected).\n\nNotes (both sources):\n\n- Fails if an Oxfmt config (`.oxfmtrc.json`, `.oxfmtrc.jsonc`, `oxfmt.config.ts` or `oxfmt.config.mts`) already exists. Delete it first if you want to re-run.\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxfmtrc.json` for these key differences:\n\n### printWidth\n\nPrettier and Biome default is 80, Oxfmt default is 100. The migration tool sets `printWidth: 80` if not specified in your source config. Decide whether to keep 80 or adopt 100.\n\n### Unsupported Options (Prettier only)\n\nAny option not in \"Prettier-Compatible Options\" below is skipped with a warning, including options of unsupported plugins (e.g. `importOrder`). Notable ones:\n\n| Option                          | Status                                                                    |\n| ------------------------------- | ------------------------------------------------------------------------- |\n| `endOfLine: \"auto\"`             | Not supported. Use `\"lf\"` or `\"crlf\"` explicitly                          |\n| `experimentalTernaries`         | Not supported                                                             |\n| `requirePragma`, `insertPragma` | Not supported                                                             |\n| `parser`                        | Skipped at top-level. In `overrides`, migrated to `associations` if known |\n| `plugins`                       | Only known plugins are migrated (see Step 1)                              |\n\nRegex values (e.g. `\"/^my-/\"`) in `tailwindFunctions` / `tailwindAttributes` are also skipped with a warning, Oxfmt only supports literal strings there.\n\n### sortPackageJson (Prettier only)\n\nEnabled by default in Oxfmt, but the migration tool disables it unless `prettier-plugin-packagejson` was detected. Review whether you want this enabled.\n\nNote: Oxfmt's sorting algorithm differs from `prettier-plugin-packagejson`.\n\n### embeddedLanguageFormatting (Prettier only)\n\nEmbedded language formatting (e.g., CSS-in-JS) generally works.\n\n### overrides\n\nPrettier `overrides` are auto-migrated. Unsupported options in them are skipped with a warning, so review the result.\n\n`overrides[].options.parser` is migrated to `associations`, which tells Oxfmt which language to use for matched files:\n\n```json\n{\n  \"associations\": [{ \"files\": [\"*.svg\"], \"language\": \"html\" }]\n}\n```\n\n`language` is a Shiki language id (as in Markdown code fences), not a Prettier parser name (e.g. `angular-html` instead of `angular`, `typescript` instead of `babel-ts`).\n\nBiome `overrides` cannot be auto-migrated. Convert manually:\n\n```json\n{\n  \"overrides\": [\n    {\n      \"files\": [\"*.md\"],\n      \"excludeFiles\": [\"CHANGELOG.md\"],\n      \"options\": { \"tabWidth\": 4 }\n    }\n  ]\n}\n```\n\n(`excludeFiles` is optional.)\n\n### Nested Config\n\nOxfmt supports nested configuration files: a config in a subdirectory applies to files under that directory. Pass `--disable-nested-config` to opt out and use only the root config.\n\nHowever, `--migrate` only migrates the config in the current directory, and does not walk subdirectories. If the project has per-directory Prettier or Biome configs, run `--migrate` in each of those directories.\n\n### Prettier-Compatible Options\n\nThese options transfer directly with the same behavior:\n`printWidth`, `tabWidth`, `useTabs`, `semi`, `singleQuote`, `jsxSingleQuote`, `quoteProps`, `trailingComma`, `arrowParens`, `bracketSpacing`, `bracketSameLine`, `objectWrap`, `experimentalOperatorPosition`, `endOfLine`, `proseWrap`, `htmlWhitespaceSensitivity`, `singleAttributePerLine`, `vueIndentScriptAndStyle`, `embeddedLanguageFormatting`\n\n## Step 3: Configure Oxfmt Extensions\n\nOxfmt offers features not available in Prettier:\n\n### sortImports\n\nSort import statements, inspired by `eslint-plugin-perfectionist/sort-imports` (disabled by default):\n\n```json\n{\n  \"sortImports\": {\n    \"partitionByNewline\": true,\n    \"newlinesBetween\": false\n  }\n}\n```\n\nOther options: `partitionByComment`, `sortSideEffects`, `order`, `ignoreCase`, `internalPattern`, `groups`, `customGroups`.\n\n### sortTailwindcss\n\nReplaces `prettier-plugin-tailwindcss`. Auto-migrated with renamed options:\n\n| Prettier (top-level)         | Oxfmt (`sortTailwindcss.*`) |\n| ---------------------------- | --------------------------- |\n| `tailwindConfig`             | `config`                    |\n| `tailwindStylesheet`         | `stylesheet`                |\n| `tailwindFunctions`          | `functions`                 |\n| `tailwindAttributes`         | `attributes`                |\n| `tailwindPreserveWhitespace` | `preserveWhitespace`        |\n| `tailwindPreserveDuplicates` | `preserveDuplicates`        |\n\n### Other Extensions\n\n| Option               | Default  | Description                                                                     |\n| -------------------- | -------- | ------------------------------------------------------------------------------- |\n| `insertFinalNewline` | `true`   | Whether to add a final newline at end of file                                   |\n| `sortPackageJson`    | `true`   | Sort `package.json` keys. Set `{ \"sortScripts\": true }` to also sort scripts    |\n| `jsdoc`              | disabled | Format JSDoc comments. Set `true` or an options object for fine-grained control |\n| `svelte`             | disabled | Svelte formatting options, replacing `prettier-plugin-svelte` (auto-migrated)   |\n| `astro`              | disabled | Astro formatting options, replacing `prettier-plugin-astro` (auto-migrated)     |\n| `associations`       | `[]`     | Format matched files as the given language (e.g. `*.svg` as `html`)             |\n\n## Step 4: Update CI and Scripts\n\nReplace formatter commands with Oxfmt:\n\n```bash\n# Before (Prettier)\nnpx prettier --write .\nnpx prettier --check .\n\n# Before (Biome)\nnpx biome format --write .\nnpx biome check .\n\n# After\nnpx oxfmt@latest\nnpx oxfmt@latest --check\n```\n\n### Common CLI Options\n\n| Prettier / Biome                                | Oxfmt                                        |\n| ----------------------------------------------- | -------------------------------------------- |\n| `prettier --write .` / `biome format --write .` | `oxfmt` (default: cwd, `--write` mode)       |\n| `prettier --check .` / `biome check .`          | `oxfmt --check`                              |\n| `prettier --list-different .`                   | `oxfmt --list-different`                     |\n| `prettier --config path`                        | `oxfmt --config path`                        |\n| `prettier --ignore-path .prettierignore`        | `oxfmt --ignore-path .prettierignore`        |\n| `cat file \\| prettier --stdin-filepath=file.ts` | `cat file \\| oxfmt --stdin-filepath=file.ts` |\n\n### File Type Coverage\n\n- JS/TS, JSON/JSONC/JSON5, CSS/SCSS/Less, GraphQL, YAML, Markdown, TOML: Formatted natively by Oxfmt\n- HTML, Vue, Svelte, Astro, etc.: Delegated to Prettier internally (when using `npx oxfmt`)\n\n## Tips\n\n- Config file: `.oxfmtrc.jsonc` and `oxfmt.config.ts` are also supported as auto-discovered config file names, in addition to `.oxfmtrc.json`. `--migrate` and `--init` only generate `.oxfmtrc.json`; keep it as-is unless the user explicitly asks for another format or the source config had logic to preserve (see Step 1).\n- EditorConfig: Oxfmt reads `.editorconfig` automatically for `useTabs`, `tabWidth`, `endOfLine`, `insertFinalNewline`, `printWidth`, and `singleQuote`. Options in `.oxfmtrc.json` take precedence.\n  - Nearest one only, does not respect `root`\n- CI: Use `npx oxfmt@latest --check` to enforce formatting in CI.\n- LSP: Run `oxfmt --lsp` for editor integration via Language Server Protocol.\n- Schema support: Add `\"$schema\": \"./node_modules/oxfmt/configuration_schema.json\"` to `.oxfmtrc.json` for editor autocompletion.\n- Init: Run `npx oxfmt@latest --init` to create a default `.oxfmtrc.json` without migration.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/formatter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/formatter/config-file-reference.html)\n- [Unsupported Features](https://oxc.rs/docs/guide/usage/formatter/unsupported-features.html)\n\n<!-- File: .agents/skills/migrate-oxlint/SKILL.md (Tokens: ~2176 | Category: anthropic-skill) -->\n---\nname: migrate-oxlint\ndescription: Guide for migrating a project from ESLint to Oxlint. Use when asked to migrate, convert, or switch a JavaScript/TypeScript project's linter from ESLint to Oxlint.\n---\n\nThis skill guides you through migrating a JavaScript/TypeScript project from ESLint to [Oxlint](https://oxc.rs/docs/guide/usage/linter/).\n\n## Overview\n\nOxlint is a high-performance linter that implements many popular ESLint rules natively in Rust. It can be used alongside ESLint or as a full replacement.\n\nAn official migration tool is available, and will be used by this skill: [`@oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate)\n\n## Step 1: Run Automated Migration\n\nRun the migration tool in the project root:\n\n```bash\nnpx @oxlint/migrate\n```\n\nThis reads your ESLint flat config (`eslint.config.js` for example) and generates a `.oxlintrc.json` file from it. It will find your ESLint config file automatically in most cases.\n\nSee options below for more info.\n\n### Key Options\n\n| Option                      | Description                                                                                                                     |\n| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `--type-aware`              | Include type-aware rules from `@typescript-eslint` (will require the `oxlint-tsgolint` package to be installed after migrating) |\n| `--with-nursery`            | Include experimental rules still under development, may not be fully stable or consistent with ESLint equivalents               |\n| `--js-plugins [bool]`       | Enable/disable ESLint plugin migration via `jsPlugins` (default: enabled)                                                       |\n| `--details`                 | List rules that could not be migrated                                                                                           |\n| `--replace-eslint-comments` | Convert all `// eslint-disable` comments to `// oxlint-disable`                                                                 |\n| `--output-file <file>`      | Specify a different output path (default: `.oxlintrc.json`)                                                                     |\n\nIf your ESLint config is not at the default location, pass the path explicitly:\n\n```bash\nnpx @oxlint/migrate ./path/to/eslint.config.js\n```\n\n## Step 2: Review Generated Config\n\nAfter migration, review the generated `.oxlintrc.json`.\n\n### Plugin Mapping\n\nThe migration tool automatically maps ESLint plugins to oxlint's built-in equivalents. The following table is for reference when reviewing the generated config:\n\n| ESLint Plugin                                         | Oxlint Plugin Name |\n| ----------------------------------------------------- | ------------------ |\n| `@typescript-eslint/eslint-plugin`                    | `typescript`       |\n| `eslint-plugin-react` / `eslint-plugin-react-hooks`   | `react`            |\n| `eslint-plugin-import` / `eslint-plugin-import-x`     | `import`           |\n| `eslint-plugin-unicorn`                               | `unicorn`          |\n| `eslint-plugin-jsx-a11y` / `eslint-plugin-jsx-a11y-x` | `jsx-a11y`         |\n| `eslint-plugin-react-perf`                            | `react-perf`       |\n| `eslint-plugin-promise`                               | `promise`          |\n| `eslint-plugin-jest`                                  | `jest`             |\n| `@vitest/eslint-plugin`                               | `vitest`           |\n| `eslint-plugin-jsdoc`                                 | `jsdoc`            |\n| `eslint-plugin-next`                                  | `nextjs`           |\n| `eslint-plugin-node`                                  | `node`             |\n| `eslint-plugin-vue`                                   | `vue`              |\n\nDefault plugins (enabled when `plugins` field is omitted): `unicorn`, `typescript`, `oxc`.\nSetting the `plugins` array explicitly overrides these defaults.\n\nESLint core rules are usable in oxlint without needing to configure a plugin in the config file.\n\n### Rule Categories\n\nOxlint groups rules into categories for bulk configuration, though only `correctness` is enabled by default:\n\n```json\n{\n  \"categories\": {\n    \"correctness\": \"error\",\n    \"suspicious\": \"warn\"\n  }\n}\n```\n\nAvailable categories: `correctness` (default: enabled), `suspicious`, `pedantic`, `perf`, `style`, `restriction`, `nursery`.\n\nIndividual rule settings in `rules` override category settings.\n\n`@oxlint/migrate` will turn `correctness` off to avoid enabling additional rules that weren't enabled by your ESLint config. You can choose to enable additional categories after migration if desired.\n\n### Check Unmigrated Rules\n\nRun with `--details` to see which ESLint rules could not be migrated:\n\n```bash\nnpx @oxlint/migrate --details\n```\n\nReview the output and decide whether to keep ESLint for those rules or not. Some rules may be mentioned in the output from `--details` as having equivalents in oxlint that were not automatically mapped by the migration tool. In those cases, consider enabling the equivalent oxlint rule manually after migration.\n\n## Step 3: Install Oxlint\n\nInstall the core oxlint package (use `yarn install`, `pnpm install`, `vp install`, `bun install`, etc. depending on your package manager):\n\n```bash\nnpm install -D oxlint\n```\n\nIf you want to add the `oxlint-tsgolint` package, if you intend to use type-aware rules that require TypeScript type information:\n\n```bash\nnpm install -D oxlint-tsgolint\n```\n\nNo other packages besides the above are needed by default, though you will need to keep/install any additional ESLint plugins that were migrated into `jsPlugins`. Do not add `@oxlint/migrate` to the package.json, it is meant for one-off usage.\n\n## Step 4: Handle Unsupported Features\n\nSome features require manual attention:\n\n- Local plugins (relative path imports): Must be migrated manually to `jsPlugins`\n- `eslint-plugin-prettier`: Supported, but very slow. It is recommended to use [oxfmt](https://oxc.rs/docs/guide/usage/formatter) instead, or switch to `prettier --check` as a separate step alongside oxlint.\n- `settings` in override configs: Oxlint does not support `settings` inside `overrides` blocks.\n- ESLint v9+ plugins: Not all work with oxlint's JS Plugins API, but the majority will.\n\n### Local Plugins\n\nIf you have any custom ESLint rules in the project repo itself, you can migrate them manually after running the migration tool by adding them to the `jsPlugins` field in `.oxlintrc.json`:\n\n```json\n{\n  \"jsPlugins\": [\"./path/to/my-plugin.js\"],\n  \"rules\": {\n    \"local-plugin/rule-name\": \"error\"\n  }\n}\n```\n\n### External ESLint Plugins\n\nFor ESLint plugins without a built-in oxlint equivalent, use the `jsPlugins` field to load them:\n\n```json\n{\n  \"jsPlugins\": [\"eslint-plugin-custom\"],\n  \"rules\": {\n    \"custom/my-rule\": \"warn\"\n  }\n}\n```\n\n## Step 5: Update CI and Scripts\n\nReplace ESLint commands with oxlint. Path arguments are optional; oxlint defaults to the current working directory.\n\n```bash\n# Before\nnpx eslint src/\nnpx eslint --fix src/\n\n# After\nnpx oxlint src/\nnpx oxlint --fix src/\n```\n\n### Common CLI Options\n\n| ESLint                    | oxlint equivalent                              |\n| ------------------------- | ---------------------------------------------- |\n| `eslint .`                | `oxlint` (default: lints the cwd)              |\n| `eslint src/`             | `oxlint src/`                                  |\n| `eslint --fix`            | `oxlint --fix`                                 |\n| `eslint --max-warnings 0` | `oxlint --deny-warnings` or `--max-warnings 0` |\n| `eslint --format json`    | `oxlint --format json`                         |\n\nAdditional oxlint options:\n\n- `--tsconfig <path>`: Specify tsconfig.json path, likely unnecessary unless you have a non-standard name for `tsconfig.json`.\n\n## Tips\n\n- You can run alongside ESLint if necessary: Oxlint is designed to complement ESLint during migration, but with JS Plugins many projects can switch over fully without losing many rules.\n- Disable comments work: `// eslint-disable` and `// eslint-disable-next-line` comments are supported by oxlint. Use `--replace-eslint-comments` when running @oxlint/migrate to convert them to `// oxlint-disable` equivalents if desired.\n- List available rules: Run `npx oxlint --rules` to see all supported rules, or refer to the [rule documentation](https://oxc.rs/docs/guide/usage/linter/rules.html).\n- Schema support: Add `\"$schema\": \"./node_modules/oxlint/configuration_schema.json\"` to `.oxlintrc.json` for editor autocompletion if the migration tool didn't do it automatically.\n- Output formats: `default`, `stylish`, `json`, `github`, `gitlab`, `junit`, `checkstyle`, `unix`\n- Ignore files: `.eslintignore` is supported by oxlint if you have it, but it's recommended to move any ignore patterns into the `ignorePatterns` field in `.oxlintrc.json` for consistency and simplicity. All files and paths ignored via a `.gitignore` file will be ignored by oxlint by default as well.\n- If you ran the migration tool multiple times, remove the `.oxlintrc.json.bak` backup file created by the migration tool once you've finished migrating.\n- If you are not using any JS Plugins and have replaced your ESLint configuration, you can remove all ESLint packages from your project dependencies.\n- Ensure your editor is configured to use oxlint instead of ESLint for linting and error reporting. You may want to install the Oxc extension for your preferred editor. See https://oxc.rs/docs/guide/usage/linter/editors.html for more details.\n\n## References\n\n- [CLI Reference](https://oxc.rs/docs/guide/usage/linter/cli.html)\n- [Config File Reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference.html)\n- [Complete Oxlint rule list and docs](https://oxc.rs/docs/guide/usage/linter/rules.html)\n\n<!-- File: .agents/skills/performance-lint-rules/SKILL.md (Tokens: ~858 | Category: anthropic-skill) -->\n---\nname: performance-lint-rules\ndescription: Performance review guidance for Oxc linter rule implementations. Use only when reviewing Rust rule code under crates/oxc_linter/src/rules/ or when explicitly auditing those rules for performance improvements.\n---\n\n## Performance Guidelines\n\n### Prefer top-level node kind checks\n\nPut node kind checks at the rule entry point. If a rule only handles a few syntactic forms, start `run` with an `AstKind` match and return for all other nodes, even when a helper filters again internally. This lets lintgen derive narrower `NODE_TYPES` and avoids dispatching the rule on unrelated AST nodes.\n\nAfter changing the relevant node kinds for a rule, regenerate the rule runner with `cargo lintgen` and consider adding or updating `assert_rule_runs_on_node_types` coverage in `crates/oxc_linter/src/rule.rs`.\n\nImplement only the needed entry point. If a rule is a whole-file pass over semantic indexes, use `run_once` by itself. Implementing both `run` and `run_once` prevents useful node-type narrowing.\n\n### Do cheaper checks first\n\nOrder checks from cheapest and most selective to most expensive. Return quickly for common non-matches before doing semantic lookups, allocations, or deeper traversal.\n\n- Matching a small fixed string set with `matches!` before semantic checks.\n- Rejecting lowercase identifiers before global-object checks when only constructors can match.\n- Checking whether a JSX attribute starts with `aria-` before lowercasing it.\n- Checking for required syntax such as a `key` prop before looking up callback parameter symbols.\n- Checking `source_range(span).contains(\"this\")` before running a visitor that only finds `this`.\n\n### Delay expensive context\n\nMost files do not contain lint errors. Do not prepare diagnostics, labels, help text, fix data, ancestors, symbols, JSX element types, or replacement strings until the rule has found a syntactic candidate that could actually report.\n\n### Iterate over the smallest set possible\n\n- Use `run_once` when the rule only needs a whole-file pass and does not need to run on every node.\n- Iterate over symbols instead of AST nodes when looking for references to specific names.\n- Prefer targeted lists or semantic data over broad AST traversal when available.\n- For name-based binding checks, use `ctx.scoping().get_binding(scope_id, name)` instead of scanning every binding in `get_bindings(scope_id)`.\n- For global or unresolved identifier checks, start from `ctx.scoping().root_unresolved_references().get(name)` for the small set of relevant names instead of visiting every `IdentifierReference`.\n- When iterating unresolved references, still verify the reference is the right kind: skip references with a symbol, type-only references, and nodes whose `AstKind` is not the expected identifier or member access.\n- When checking imported specifiers or exported names, iterate the concrete specifiers or precomputed export set rather than scanning all root bindings for every item.\n\nUse precomputed `FxHashSet`s only when many symbols need the same membership test. Prefer keyed semantic lookup when each lookup already has an exact name.\n\n### Avoid unnecessary regular expressions\n\nAvoid regular expressions when byte or string checks are enough: `contains`, `starts_with`, `ends_with`, or matching a small fixed set.\n\nFor hot comment or string scanning paths, prefer a cheap `memchr` or byte search to reject most inputs, then parse only candidates. Preserve regex semantics when replacing one, especially identifier boundaries, optional prefixes, and multiline whitespace.\n\n### Avoid heap allocations\n\n- Use copy-on-write utilities when a value usually does not need to change.\n- Avoid intermediate `Vec`s and `String`s when iteration or borrowed data is enough.\n- Keep temporary data on the stack when practical.\n- Delay allocation until a diagnostic, fix, or transformed value is actually needed.\n- Use allocation-free ASCII comparisons such as `starts_with_ignore_case` before calling `cow_to_ascii_lowercase`.\n- Use byte scans such as `as_bytes().array_windows()` for simple ASCII patterns like escape sequences.\n- Reserve hash maps or sets when the final size is known.\n- Avoid building a `HashSet` just to check names that can be looked up directly in scoping data.\n\n<!-- File: apps/oxfmt/test/cli/extensions/0.snap.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/0.snap.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/0.snap.md)\n\n<!-- File: apps/oxfmt/test/cli/extensions/options.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Extensions Skill\n\nPath: `apps/oxfmt/test/cli/extensions/options.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/extensions/options.json)\n\n<!-- File: apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Sub Skill\n\nPath: `apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/apps/oxfmt/test/cli/nested_config_ignore_scope/fixtures/lib/packages/sub/.oxfmtrc.json)\n\n<!-- File: crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_no_plugins.json)\n\n<!-- File: crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins.json)\n\n<!-- File: crates/oxc_linter/fixtures/extends_config/plugins/jest.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/jest.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/jest.json)\n\n<!-- File: crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent_default_plugins.json)\n\n<!-- File: crates/oxc_linter/fixtures/extends_config/plugins/parent.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/parent.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/parent.json)\n\n<!-- File: crates/oxc_linter/fixtures/extends_config/plugins/react.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Plugins Skill\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/react.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/react.json)\n\n<!-- File: crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Nested-package Skill\n\nPath: `crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/import/monorepo/packages/nested-package/package.json)\n\n<!-- File: packages/codegen/CHANGELOG.md (Tokens: ~2656 | Category: anthropic-skill) -->\n# Changelog\n\nAll notable changes to this package will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0).\n\n## [0.155.0] - 2026-10-19\n\n### ⚡ Performance\n\n- 2ab6b63 packages/codegen: Remove redundant `printSpaceBeforeIdentifier` and `last` updates in `printParams` (#27652) (overlookmotel)\n- e533278 codegen: Reduce writes in `printDecorators` (#27651) (overlookmotel)\n\n### 📚 Documentation\n\n- 3de03c1 packages/codegen: Correct outdated comments (#27838) (overlookmotel)\n\n## [0.154.0] - 2026-10-13\n\n### 🐛 Bug Fixes\n\n- 760b60b codegen, packages/codegen: Remove extraneous space before `RestElement` in `ArrayPattern` (#27613) (overlookmotel)\n- 0353d37 packages/codegen: Support printing `V8IntrinsicExpression`s (#27597) (overlookmotel)\n- f50257e codegen: Avoid extra indent before empty do-while bodies (#27407) (camc314)\n- 5461c1a packages/codegen: Fix printing negative `BigIntLiteral` after `-` (#27377) (overlookmotel)\n- bebef88 packages/codegen: Fix debug assert fail printing `TSJSDocNonNullableType` (#27373) (overlookmotel)\n\n### ⚡ Performance\n\n- e1c97f8 packages/codegen: Remove all `printSpaceBeforeIdentifier` calls (#27620) (overlookmotel)\n- 70ecfdd packages/codegen: Skip redundant `printSpaceBeforeIdentifier` call (#27619) (overlookmotel)\n- 3c289f3 packages/codegen: Remove branches from loops printing arrays (#27614) (overlookmotel)\n- 27f5b45 packages/codegen: Remove branch printing `RestElement` in `ArrayPattern` (#27608) (overlookmotel)\n- adc90ed packages/codegen: Reduce branches in `printArguments` (#27607) (overlookmotel)\n- 262dd21 packages/codegen: Remove unnecessary `printSpaceBeforeIdentifier` calls (#27606) (overlookmotel)\n- 4bb9722 packages/codegen: Print `SpreadElement` arguments via `printExpression` (#27599) (overlookmotel)\n- 0c98dc5 packages/codegen: Reduce megamorphic `type` reads in `printParams` (#27595) (overlookmotel)\n- e3780b5 packages/codegen: Reduce writes in `printForOfStatement` (#27594) (overlookmotel)\n- 8d0ba20 packages/codegen: Reduce megamorphic `type` reads in `printForOfStatement` (#27593) (overlookmotel)\n- b29f35e packages/codegen: Reduce megamorphic `type` reads in `printDirectivesAndStatements` (#27592) (overlookmotel)\n- 256259d packages/codegen: Reduce megamorphic `type` reads in `printConditionalExpression` (#27591) (overlookmotel)\n- a9bd942 packages/codegen: Reduce megamorphic `type` reads in `printObjectProperty` (#27590) (overlookmotel)\n- 0a386ff packages/codegen: Ensure `shorthand` var is always a boolean (#27589) (overlookmotel)\n- 8d4c633 packages/codegen: Reduce branches in `printObjectProperty` (#27588) (overlookmotel)\n- b4c8c04 packages/codegen: Reduce megamorphic `type` reads in `printMemberExpression` (#27587) (overlookmotel)\n- ff5eae8 packages/codegen: Reduce megamorphic `type` reads (#27586) (overlookmotel)\n- 961f318 packages/codegen: Reduce megamorphic `type` reads in `binCheckAndPrepare` (#27460) (overlookmotel)\n- 3d2f93f packages/codegen: Reduce megamorphic property accesses for `BinaryExpression`s (#27459) (overlookmotel)\n- 675ec5e packages/codegen: Reduce megamorphic property accesses in `printBinaryish` (#27458) (overlookmotel)\n- b0dbfff packages/codegen: Reduce writes and branches printing functions (#27455) (overlookmotel)\n- 5f3865c packages/codegen: Reduce writes for optional `MemberExpression`s (#27454) (overlookmotel)\n\n### 📚 Documentation\n\n- 59472c5 packages/codegen: Add `@param`s to JSDoc comments which had none (#27601) (overlookmotel)\n- 5ee0fc9 packages/codegen: Complete `@param` lists in partly documented JSDoc comments (#27600) (overlookmotel)\n- 12dfa25 packages/codegen: Correct doc comment for `printBinaryish` (#27456) (overlookmotel)\n\n## [0.151.0] - 2026-09-21\n\n### 🚀 Features\n\n- 1fac655 coverage: Support new TypeScript fixture layout (#26226) (camc314)\n\n## [0.150.0] - 2026-09-14\n\n### 🐛 Bug Fixes\n\n- 32d00c5 codegen: Preserve instantiation expression precedence (#26424) (camc314)\n- 5986187 packages/codegen: Preserve private-in right operand precedence (#26420) (camc314)\n- f8e6c6c packages/codegen: Preserve in restriction through yield arguments (#26421) (camc314)\n- 6e15ad5 packages/codegen: Print matching quoted import names as identifiers (#26404) (camc314)\n- bbbb4bc packages/codegen: Preserve private-in left operand precedence (#26403) (camc314)\n- a111b5b packages/codegen: Print accessibility modifiers before abstract (#26402) (camc314)\n\n## [0.148.0] - 2026-08-31\n\n### ⚡ Performance\n\n- 07a0793 packages/codegen: Flatten output in chunks (#26109) (overlookmotel)\n- 487427a packages/codegen: Faster string flattening (#26108) (overlookmotel)\n- 7785583 packages/codegen: Ensure indent strings are flattened (#26107) (overlookmotel)\n- 0dd4db3 packages/codegen: Store mapping positions in an `Int32Array` (#26085) (overlookmotel)\n\n## [0.147.0] - 2026-08-24\n\n### 🐛 Bug Fixes\n\n- 6c5ad1b codegen: Only add mapping names for `PrivateIdentifier`s which have changed (#25958) (overlookmotel)\n- a185ac3 codegen: Print `#` before private identifier in TS signature key (#25938) (overlookmotel)\n\n### ⚡ Performance\n\n- 53f6270 packages/codegen: Eagerly initialize `mapNames` (#25979) (overlookmotel)\n- 5b43c60 packages/codegen: Split recording mappings into multiple functions (#25978) (overlookmotel)\n- cc947b5 packages/codegen: Reduce property lookups (#25977) (overlookmotel)\n- c22a9cc packages/codegen: Combine condition checks (#25976) (overlookmotel)\n- 215a6a5 packages/codegen: Add functions for writing/marking unnamed mappings (#25975) (overlookmotel)\n- 1004439 packages/codegen: Merge `mark` and `write` calls in `printImportAttributes` (#25972) (overlookmotel)\n- 94c8a34 packages/codegen: Remove `node` param from `printString` and `printNonNegativeFloat` (#25971) (overlookmotel)\n- 7e2b4b3 packages/codegen: `printNonNegativeFloat` write mapping directly (#25968) (overlookmotel)\n- 53ff080 packages/codegen: Remove `lastWasPostfixClose` (#25888) (overlookmotel)\n\n### 📚 Documentation\n\n- 07de646 packages/codegen: Fix JSDoc comments on `write*` methods (#25966) (overlookmotel)\n- 3b09c04 packages/codegen: Clarify comment (#25959) (overlookmotel)\n\n## [0.146.0] - 2026-08-19\n\n### 🐛 Bug Fixes\n\n- 526c2b3 codegen: Remove deprecated test APIs (#25889) (camc314)\n- 344a9b2 codegen: Align enum template sourcemaps (#25872) (camc314)\n\n### ⚡ Performance\n\n- 8783524 packages/codegen: Remove loop from `growMappingBuffer` (#25886) (overlookmotel)\n- b6b14c0 packages/codegen: Remove redundant branch (#25885) (overlookmotel)\n- be6adf0 packages/sourcemap: Make `location` passed to `recordSourceMapping` always a number (#25884) (overlookmotel)\n- f720f15 packages/codegen: Take named-mapping only code off hot path in `recordSourceMapping` (#25883) (overlookmotel)\n- a734af5 packages/codegen: Remove redundant setting of `last` in `printImportAttributes` and `printIf` (#25882) (overlookmotel)\n- c6e44c6 packages/codegen: Combine 2 writes in `printJSXElement` (#25881) (overlookmotel)\n- 8801662 packages/codegen: Remove redundant mapping from `printParams` (#25880) (overlookmotel)\n\n### 📚 Documentation\n\n- 5ad330e packages/codegen: Reformat comments (#25876) (overlookmotel)\n\n## [0.145.0] - 2026-08-18\n\n### 💥 BREAKING CHANGES\n\n- 365274e packages/codegen: [**BREAKING**] `printSync` return an object (#25720) (overlookmotel)\n\n### 🚀 Features\n\n- 6a7eb60 packages/codegen: Alter capitalization of `sourceFilename` option (#25854) (overlookmotel)\n- 300763f packages/codegen: Add full source map support (#25585) (camc314)\n- a4478e9 codegen: Add `oxc-codegen` package (#25488) (overlookmotel)\n\n### 🐛 Bug Fixes\n\n- 0fbcf64 codegen: Validate sourcemap options (#25860) (camc314)\n- 4cc7ea4 codegen: Reject invalid indent options (#25807) (camc314)\n- 0c68b7f estree: Emit `decorators` on `FormalParameterRest` (#25582) (camc314)\n- 59e8895 codegen: Validate starting indent level (#25550) (camc314)\n\n### ⚡ Performance\n\n- 5444cbf codegen: Skip escaping harmless `<` tokens (#25564) (camc314)\n\n### 📚 Documentation\n\n- 58f7ab9 packages/codegen: Reformat docs and comments (#25848) (overlookmotel)\n- ce03ac1 packages/codegen: Fix JSDoc comments for `printSync` (#25722) (overlookmotel)\n- 386a699 packages/codegen: Correct JSDoc comment (#25716) (overlookmotel)\n- fd62354 codegen: Revamp package readme (#25560) (camc314)\n- ffa3153 codegen: Clarify raw transfer Node requirement (#25565) (camc314)\n- c5d4063 codegen: Clarify binary walk allocations (#25562) (camc314)\n- 5703d7a codegen: Correct stale printer comments (#25561) (camc314)\n\n<!-- File: packages/codegen/DESIGN.md (Tokens: ~8557 | Category: anthropic-skill) -->\n# Design\n\nWhy `oxc-codegen` is shaped the way it is.\n\nIt is a port of Oxc's Rust [`oxc_codegen`] crate, and its output has to be byte-identical to it.\nBut it is not a transliteration. A straight port would not take into account differences between Rust and JS,\nand would likely be several times slower.\n\nThis document is for someone about to change the printer, wondering which of its oddities are load bearing.\nEverything here is also documented at the point it applies. This is an overview.\n\n## Contents\n\n- [Why not native?](#why-not-native)\n- [What this is](#what-this-is)\n- [What it is not at present](#what-it-is-not-at-present)\n- [`state.last`](#statelast)\n- [Four builds from one source tree](#four-builds-from-one-source-tree)\n- [Other things which make it fast](#other-things-which-make-it-fast)\n- [Where the ESTree AST differs](#where-the-estree-ast-differs)\n- [How it is tested](#how-it-is-tested)\n- [Rules to keep](#rules-to-keep)\n\n## Why not native?\n\nOxc's other JS packages are bindings around a native library. This one deliberately is not.\n\nThe starting point was a hunch:\n\n> **When the AST is already in JS, no native printer can beat a JS one.**\n\n### The argument\n\n- **Printing is cheap.** It is a linear walk of the AST, emitting strings. There is no parsing, no analysis,\n  no search. It doesn't play to the strengths of a faster language the way parsing or minifying does.\n- **Crossing into native is not cheap.** A native printer cannot see a JS object graph.\n  The whole AST has to be serialized across the boundary before it can print a single character.\n- **The boundary dominates.** The serialization is proportional to the size of the AST, just as the printing is -\n  but it is work a JS printer can skip entirely. It is added to a total that was small to begin with.\n\nThe conclusion: A native printer loses the game before it's even started - still shovelling data over to native side,\nwhile the JS printer already finished work and has gone off to the pub.\n\n### Where this applies\n\nOnly when the AST is **already** in JS - it came from `oxc-parser`, or a plugin built it, or something\ntransformed it on JS side. That is the common use case this package is for.\n\nIt does **not** apply when you are printing source text you also parsed natively. Then stay on native side -\nthe AST never has to leave native memory, and Rust `oxc_codegen` is the right tool. The two are complementary.\n\nThe benchmarks in the [README](./README.md) measure `oxc-codegen` over representative JS-held ASTs.\n\n## What this is\n\nA printer from an ESTree AST to source text, written in TypeScript.\n\n### The output contract\n\nByte-identical to `oxc_codegen` with default options - tab indentation, double quotes, comments off.\n\nThe conformance suites check this on every fixture. It is what makes the two printers substitutable for one another.\n\n## What it is not at present\n\n`oxc_codegen` (Rust printer) is more capable than this one.\n\n- No minify mode.\n- No support for comments.\n- No symbol mangling.\n\nMany of these features can be added in future, and we should be able to do so without _much_ impact on performance.\n\n## `state.last`\n\nThis is the largest divergence from Rust, and most of the others follow from it.\n\n### The problem\n\nTwo adjacent tokens can merge into a different token when written straight up against each other.\n`in` after an identifier becomes part of that identifier. `+` after `+` becomes `++`.\n\nDeciding whether to insert a space means knowing what was written last.\n\n### How Rust answers it\n\nRust just looks. Its output buffer is a `Vec<u8>`, so `code.last_byte()` is a pointer dereference - very cheap.\n\n### Why this doesn't work in JS\n\n**In JS, reading the last character is catastrophic for perf.** `state.output` is built by repeated `+=`,\nwhich V8 represents as a **cons string** - a tree of the pieces, never actually concatenated. Reading any\ncharacter of it forces V8 to flatten that tree.\n\nDo that after every append and the printer spends its time flattening ropes it is about to extend again.\nResult is a **4x slow-down**.\n\nSo `oxc-codegen` has to find a different solution. Every time it pushes a string to output, it also records\nin `state.last` what the output now ends with. `state.output` never needs to be read from.\n\nThe rope is flattened once, at the end. Whatever the caller does with the returned code will flatten it anyway\n(and `generateSourceMap` scans it), so `printSync` pre-flattens with the fastest method available.\nA large output is flattened in chunks along the way rather than all at once at the end -\nsee `print/flatten.ts` for the two reasons.\n\n### The answer: A category, not a character\n\nIt turns out the functions which decide whether spaces need to be inserted between keywords / identifiers\ndon't actually need to know what the last character was, they only need to know what _category_ of character\nit was. i.e. It doesn't matter if the last character was `\"a\"` or `\"Z\"` or `\"$\"` - they're all equivalent.\n\nAlmost everywhere in the printer, it knows statically what _category_ it's writing, even if it doesn't know\nthe exact bytes until runtime. e.g. When writing an identifier, it's the `IDENT` category.\n\n```ts\nwrite(state, node.name, CAT_IDENT);\n```\n\nThis has 3 advantages over storing the last character:\n\n1. Categories are just small integers, the cheapest data type there is.\n2. The categories are statically defined at call sites. No `node.name.at(-1)`.\n3. Checking the category of last write is cheap - `last === CAT_IDENT` not `isIdentifierPart(lastChar)`.\n\nThat one `last` field replaces all of this:\n\n| Rust                                  | Question it answers                 | JS                      |\n| :------------------------------------ | :---------------------------------- | :---------------------- |\n| `last_byte()` + `is_identifier_part`  | Would a following identifier merge? | `CAT_IDENT`             |\n| `last_byte() == Some(b'?')`           | Would a following `?` make `??`?    | `CAT_QUESTION`          |\n| `peek_nth_byte_back(1) == Some(b'<')` | Is this `!` the `!` of a `<!`?      | `CAT_OP_LT_THEN_UN_NOT` |\n\n### Extending this scheme to operators\n\nRust `oxc_codegen` also holds state about what operator was last written. It stores this info as a pair:\n\n- `prev_op` - Last operator that was written\n- `prev_op_end` - Position of the end of that operator\n\nEach time an operator is written, it sets these 2 fields. When other code wants to know \"am I writing this straight\nafter an operator, and which operator?\" it:\n\n1. Checks if `code.len() == prev_op_end`. If true, then yes, it's right after an operator.\n2. `prev_op` then says which operator.\n\nIn Rust printer, this makes sense. Checking these 2 fields, and comparing to `code.len()` costs 2 or 3 memory accesses,\nand 1 or 2 comparisons. But that's fine! It only reads or writes these 2 fields infrequently - only when an operator\nis being written. And operators are only a small fraction of code.\n\nIn JS codegen, we're in a different situation. We've been forced to update `state.last` after every write,\nto avoid the huge perf hit of reading the last character from `output`.\n\nSo we might as well use `last` to record operators too, and also some similar character classes:\n\n| Rust                                    | Question it answers                                     | JS                |\n| :-------------------------------------- | :------------------------------------------------------ | :---------------- |\n| `code.len() == prev_op_end` + `prev_op` | Which operator came last, and was it right before this? | `CAT_OP_*`        |\n| `code.len() == need_space_before_dot`   | Is space needed after digit? e.g. `0 .toExponential()`  | `CAT_INT_DIGIT`   |\n| `code.len() == prev_reg_exp_end`        | Did a flagless regex just close?                        | `CAT_REGEX_SLASH` |\n\nAll these Rust state fields, and every read of the output buffer, collapse into one field in the JS printer.\n\nIt costs no write barrier to store (because categories are represented by \"SMI\" small integers), and one compare\nto test. Being touched by every single write, it is the hottest field in `State`, reliably in L1 cache,\nand probably often also benefits from fast store-to-load forwarding.\n\n#### The `<!--` case, as an example\n\nRust decides whether a `!` is the `!` of a `<!--` hazard by peeking at the _second_-to-last byte,\nat the moment a `--` is about to be written.\n\nHere the question is answered when the `!` is written, where the preceding character is already known,\nand the answer is baked into which category gets stored. The reader has nothing left to look up.\n\n### Position marks\n\nSimilar to operators, Rust printer also maintains state about the _context_. For example:\n\n```js\n// Function declaration\nexport default function foo () {}\n// Function expression\nexport default (function bar () {});\n```\n\nThese 2 are semantically different - `foo` is available in outer scope, `bar` is not.\n\nWhen printing a `FunctionExpression`, print needs to know \"am I the value of an `export default`?\"\nto know whether it needs to wrap the function in parentheses.\n\nSimilar to operators, Rust printer stores this info in field `start_of_default_export`.\nWhen `code.len() == start_of_default_export` then, yes, what's currently being printed is the value of\nan `export default` statement.\n\nAgain, JS codegen uses `last` to store this info. Except here `last` doesn't describe what _was_ written,\nit says what the context is.\n\nNote that the node which reads the mark need not be the direct child of the `export default`.\nIn `export default (function bar() {})();` the function is the callee of a call expression, and the parens\nare still needed. It gets them because `printCallExpression` wrote nothing before descending into the callee,\nso the mark was still there when `printFunction` looked. Whatever is printed leftmost sees the mark,\nhowever deeply nested it is.\n\n| Rust                                    | Question it answers                   | JS                            |\n| :-------------------------------------- | :------------------------------------ | :---------------------------- |\n| `code.len() == start_of_stmt`           | At the start of a statement?          | `CAT_START_OF_STMT`           |\n| `code.len() == start_of_arrow_expr`     | At the start of a concise arrow body? | `CAT_START_OF_ARROW_EXPR`     |\n| `code.len() == start_of_default_export` | At the start of an `export default`?  | `CAT_START_OF_DEFAULT_EXPORT` |\n\nThe reason this works is that in every case the last character printed in these positions was a space, a line break,\nor the start of the file, which all fall into the `CAT_OTHER` category. So these values of `last` aren't ambiguous\nto any other code which judges what to do next based on what came last. They just treat these values as\n\"last character written was something I don't need to do anything special about\".\n\n#### Why the substitution is safe\n\nTwo facts, both checked across every printer file:\n\n1. **Nothing writes output on the way down from a mark to its reader.** Every path from a mark to a\n   reader either writes nothing, or writes a real `(` - never a `writeNoLast`, which would leave the\n   mark visible past output which should have killed it.\n2. **It can only ever add parentheses, never remove them.** A real `write` always grows the output\n   (empty writes are illegal), so the set of things which kill a mark under categories is a strict\n   subset of what killed the offsets.\n\nSo every possible failure is a spurious pair of parentheses - slightly longer output, but not _invalid_ output.\nThe byte-for-byte conformance tests would catch this, but even if there are gaps in the conformance suite\nfor crazy edge cases and a bug sneaks through, it can't produce invalid JavaScript.\n\n### Why category numbering is load bearing\n\nThe full table is at the top of [`print/categories.ts`] and is the authority.\n\nThree properties are relied on. Adding a code without preserving them will silently space output wrongly.\n\n1. **Identifier hazards are the lowest codes.** So `printSpaceBeforeIdentifier` is `last <= CAT_REGEX_SLASH` -\n   one compare, no table, no branch tree. The operators `printSpaceBeforeOperator` must distinguish are the highest,\n   for the same reason (`last >= CAT_OP_LT_THEN_UN_NOT`).\n2. **The `CAT_START_OF_*` codes sit between those two ranges**, which is what makes both range checks\n   treat them as \"nothing to separate\".\n3. **`CAT_START_OF_STMT` is odd, with the other two marks either side.** The five reader sites each ask\n   about a _pair_ of marks, never all three, so both pairs fall out as one `|`-and-compare:\n\n   ```\n   (last | 1) === CAT_START_OF_STMT          // Statement or `export default`\n   ((last - 1) | 1) === CAT_START_OF_STMT    // Statement or concise arrow body\n   ```\n\nEach property has an `if (DEBUG)` block which iterates every category and asserts the compare selects\nexactly the intended set and nothing else - in [`print/categories.ts`] for the `|` identities, and in [`print/space.ts`]\nfor the two range checks.\n\nRenumber freely. The assertions will tell you what you broke, and cost release builds nothing.\n\n**One deliberate exception**: `CAT_OP_UN_NOT` is an operator code but sits _below_ the operator range.\nNo operator merges with a plain `!`, so storing it must not cost `printSpaceBeforeOperator` a call into its slow path.\n\n### `writeNoLast`\n\nNot every write needs to update `last`.\n\nWhen one token is written in pieces - a string's opening quote, its contents, its closing quote -\nonly the last piece's category can possibly be read. Hence eleven write primitives in [`print/write.ts`]:\n\n| Function                     | Updates `last` | Records a source mapping       |\n| :--------------------------- | :------------- | :----------------------------- |\n| `write`                      | Yes            | No                             |\n| `writeIdent`                 | Yes            | No                             |\n| `writePrivate`               | Yes            | No                             |\n| `writeWithMap`               | Yes            | At the node's start            |\n| `writeWithMapNamed`          | Yes            | At the node's start, with name |\n| `writeWithMapNamedPrivate`   | Yes            | At the node's start, with name |\n| `writeWithMapEnd`            | Yes            | At the node's last character   |\n| `writeNoLast`                | No             | No                             |\n| `writeWithMapNoLast`         | No             | At the node's start            |\n| `writeWithMapNamedNoLast`    | No             | At the node's start, with name |\n| `writeWithMapNamedJSXNoLast` | No             | At the node's start, with name |\n\n- Every `writeWithMap*` and `markMap*` function takes the node's `start` and `end` offsets, extracted by the caller.\n  The node itself is still passed, last, but only debug asserts read it - which is what checks the offsets\n  against the node they claim to describe. The point is where the read happens - in a caller the node's type is known,\n  so `node.start` is a monomorphic load, whereas inside the write functions the same read is megamorphic,\n  because the printer reaches them from every node type there is. Release builds have the parameter and every argument\n  removed by the TSDown plugin.\n- The `*Named` functions record the name the node had in the source, and take a `NamedMappableNode`,\n  which covers nodes with a string `name`. What they record is the text they print -\n  debug builds assert the two are the same string.\n- The other `writeWithMap*` functions take an `UnnamedMappableNode` which covers nodes without a string `name`.\n- There is a `*Named` function per way of recovering the original name from the source, because the scan differs -\n  a JSX identifier may hold a `-`, and a private identifier's span covers a `#` which is not part of its name.\n  Which one applies is settled at the call site, where the node's type is known statically, rather than read back\n  off a `node` which is megamorphic by the time it arrives.\n- `writeIdent` is `write` with the category fixed at `CAT_IDENT` - an identifier, a keyword, or the trailing digits\n  of a number. It is the commonest category by far, so every call which passed it was spending an argument\n  on a value which could never be anything else.\n- `writeWithMapNamed` and `writeWithMapNamedPrivate` take no category for the same reason, which is why their\n  plain forms are `writeIdent` and `writePrivate` rather than `write`.\n- The two `Private` forms write the `#` themselves, so the name they are given is what follows it,\n  and `last` is always `CAT_IDENT` - the caller passes neither.\n- The three `markMap*` functions record a mapping without writing anything, so `last` is not theirs to update.\n- The rule is exact: **only sound where the value of `last` is provably dead**.\n  Another real write must follow before anything reads it.\n- JSX carries the longest runs. Printing `<a.b x=\"1\">hi</a.b>` updates `last` exactly once, on the final `>`.\n- `writeNoLast` accepts an empty string, `write` does not.\n  A `write` of `\"\"` would leave `last` claiming a category for a character that was never written.\n\n### How the invariants are enforced\n\nEvery rule above fails quietly - a missing space in one construct out of thousands. So three debug-only\nmechanisms guard them, all removed from release builds.\n\n1. **`debugAssertCategoryMatches`** runs on every `write`, checking the category truthfully describes\n   the final character written. It cannot be a simple lookup - a category encodes the merge hazard of\n   the trailing _token_, not the trailing character - a digit legitimately ends both a `CAT_IDENT` write\n   and a `CAT_INT_DIGIT` one - so each final character has a permitted set.\n2. **`lastIsStale` / `debugAssertLastFresh`** track the `writeNoLast` rule. `writeNoLast` sets the flag,\n   the next real write clears it, and every reader of `last` calls `debugAssertLastFresh` first,\n   to make sure they're not reading a stale value.\n   An author who was wrong about `last` being dead gets a clear failure, not a wrongly spaced construct.\n3. **The numbering assertions** described above.\n\nThis is why the debug build is the one to run the conformance suites against - `pnpm run build-test`.\n\n## Four builds from one source tree\n\n`dist` holds the printer compiled four times, plus an entry point which picks between them.\n\n| Build              | `TS`  | `SOURCEMAPS` |  Size |\n| :----------------- | :---- | :----------- | ----: |\n| `print_js.js`      | false | false        | 25 KB |\n| `print_js_maps.js` | false | true         | 26 KB |\n| `print_ts.js`      | true  | false        | 39 KB |\n| `print_ts_maps.js` | true  | true         | 40 KB |\n\n`index.ts` picks one from the caller's `ts` and `sourcemap` options and `require`s it on first use -\n`require` rather than `import()`, because `printSync` is synchronous.\n\nA caller printing only JavaScript never loads, parses or compiles the TypeScript printers at all.\n\n### Why separate TS and JS builds\n\n**The obvious reason is code size and complexity.** JS-only builds have all TS-related code removed,\nas it's all gated behind compile-time `TS` constant. Every `if (TS && node.declare) { ... }` block is removed\nby minifier, and every `case \"TSEnumDeclaration\":` arm by the `strip_ts` TSDown plugin. With the arms gone,\nthe TS-only printer functions lose their last references and are tree-shaken away. That is a third of the code.\nHowever, it has a surprisingly small impact on perf.\n\n**The bigger reason is monomorphism.** `oxc-parser` produces differently-_shaped_ node objects for a\nJS-shaped AST and a TS-shaped one - the TS shape carries `optional`, `declare`, `accessibility`,\n`override` and the rest.\n\nIf both shapes flow through one `printFunction`, V8 sees two hidden classes at every property access,\nthe inline caches go polymorphic, and the whole printer slows down.\n\nTwo builds means each one only ever sees one shape. JS printer sees only JS AST-shaped objects,\nTS printer sees only TS AST-shaped objects. This gives a perf boost of over 10%.\n\nAdditionally, the split helps branch prediction. If feeding a mix of JS and TS ASTs through a single printer,\nfor a JS AST branches for \"does this node have a type annotation?\" always find the answer is \"no\".\nBranch predictor believes there's a pattern and when that code runs again, it expects no type annotation again.\nBut this time, the AST is a TS AST, and the answer is \"yes\". Mispredict! Go back 100 instructions, go directly back,\ndo not pass Go, do not collect $200!\n\nWith 2 different builds for JS and TS, this doesn't arise. In the JS build the branch isn't there at all -\nit was compiled away. The TS build only ever sees TS ASTs, so what the branch predictor learns stays true.\n\n### Why `State` lives outside `print` directory\n\nThe same argument, one level up.\n\n`State` is defined in `src-js/state.ts`, which is **not** part of any printer build. The printers import\nit as a type only; the entry point constructs it. So all four builds share one class, and therefore one\nhidden class, for the object they thread through every single function.\n\nThis is why **a build-time flag must never add or remove a field on `State`**.\n\n- The source-map fields are present in every build, and set to `null` when unused.\n- The debug fields are present in every build, and only _initialised_ under `if (DEBUG)`.\n\nMaking either conditional would give each build a different object shape, undoing the whole arrangement.\n\n### The build plugins\n\nThere are 4 TSDown plugins in `tsdown_plugins`. Order matters - `strip_ts` is a text transform,\nso it must run before the plugins which parse.\n\n#### `strip_ts`\n\nDeletes regions fenced with `/* IF TS */` ... `/* END_IF */` from JS-only builds.\n\nThey exist because a minifier can fold `if (TS)` away, but cannot remove a `switch` `case` arm on the grounds\nthat its node type never occurs.\n\nIt fails the build if the fences are unbalanced - and also if it finds _no_ fences at all.\nA plugin silently doing nothing is a failure mode worth guarding against.\n\n#### `unmap_writes`\n\nRuns in two modes, one per build flavour (sourcemaps / no sourcemaps), in both cases stripping arguments\nnothing in that build reads.\n\n**Without source maps**, rewrites every mapped write into the plain one it becomes with no mapping to record -\nso `writeWithMap(state, code, cat, node.start, node.end, node)` turns into `write(state, code, cat)` -\nand rewrites the import to match.\n`writeWithMapEnd` becomes `write` too, and every `NoLast` form becomes `writeNoLast`.\n`writeWithMapNamed` and `writeWithMapNamedPrivate` become `writeIdent` and `writePrivate`, which fix the category\ninstead of taking it. `writePrivate` exists only for the rewrite to land on.\n\n`printString` and `printNonNegativeFloat` take the offsets and the node only to hand them on to a mapped write.\nThey keep their names, but the arguments come off every call, and the parameters off their declarations -\nwhich, unlike the mapped writes, survive into the build.\n\n**With source maps**, in release builds only, just the trailing `node` comes off - from every call,\nand from every declaration. The mappings are real in these builds so the offsets stay, but `node` isn't read\nexcept by debug asserts. Debug builds get neither mode - the `node` parameter stays for the debug asserts to read.\n\nWithout it, the offsets would still be read and held live across a call which ignores them.\n\nThe three `markMap*` calls write nothing, so they have no plain form to become, and keep their names -\nthe arguments come off, and a body which opens `if (!SOURCEMAPS) return` lets the minifier remove what is left.\n\nIt fails the build if a mapped write is called with the wrong number of arguments, if a declaration it transforms\nhas the wrong number of parameters, or if a call it rewrote had no matching import to rewrite.\n\n#### `const_functions`\n\nRewrites top-level `function foo(...) { ... }` into `const foo = (...) => { ... }`.\n\nA function declaration creates a binding which could be assigned later, so every call has to read\nthe current value out of module scope. A `const` cannot be, so V8 calls it directly.\n\n- **Measured at ~3%** across all benchmark fixtures.\n- It really is the `const`. The same rewrite with `let` measures ~4% _slower_ than this, and slower than\n  leaving the declarations alone.\n- The source keeps its declarations, because they hoist and read better. Which is why this is a build step\n  rather than a house style.\n\n#### `remove_asserts`\n\nRemoves `debugAssert` and `typeAssertIs` calls _and the expressions inside them_ from release builds.\n\nThe minifier can drop the calls on its own, but cannot prove `node.value` in `debugAssert(node.value !== null)`\nhas no getter, so it would leave `node.value !== null` behind as a bare expression statement.\n\nThis is what gives us Rust `debug_assert!`-style checks in tests and conformance, with zero cost in release build.\n\n## Other things which make it fast\n\n### Popularity-ordered dispatch\n\n`printExpression` (34 arms) and `printStatement` (33 arms) are megamorphic string switches.\n\nV8 lowers a switch on strings to what amounts to an `if / else if` chain, so arm order is execution order.\nBoth are ordered roughly by how often each node type occurs in real code - common variants like `Identifier`,\n`MemberExpression`, `CallExpression` first.\n\nThis sped up the printer by ~30%.\n\nIt also means **adding an arm near the top of either switch has a cost**, however rare the node type is.\n\nEven after ordering `case` arms, these \"dispatch\" functions remain a large part of runtime (~25%).\nMuch sweat and tokens were spilled trying to reduce it further:\n\n- Perfect hash table to convert `node.type` to an integer ID.\n- `switch`-ing on an ID to reduce the switch to a jump table.\n- Branching on first character to narrow down the range - `switch (node.type.charCodeAt(0)) { case 69: ... }`.\n- Many others...\n\nAll to no effect. Arm ordering worked, nothing else did.\n\nIf anyone else can crack this, it's probably the single largest unrealized perf gain on the table.\n\n### Monomorphism\n\nAs much as possible, call paths are structured so that functions receive the same object shape consistently.\nOnce it has gathered type feedback from the first runs, V8 tiers up hot functions to TurboFan, which generates\ncode along these lines (pseudo-code):\n\n```ts\nfunction printThing(state, node): void {\n  if (SHAPE_OF(node) !== K_EXPECTED_SHAPE) {\n    DE_OPTIMIZE(printThing);\n    return printThing(state, node);\n  }\n\n  // Now it's known exactly what shape `node` is, it becomes static-typed like Rust.\n  // Working with it is very cheap.\n\n  // Original code: `const { left } = node;`\n  const left: Expression = GET_UNCHECKED(node, K_LEFT_OFFSET);\n  // ... fast code from here on ...\n}\n```\n\nIn particular, it is **essential** that `State` remains the same shape _everywhere_. Every function touches it,\nso any inconsistency in shape would turn every access polymorphic and completely tank performance.\n\nThe obvious exception is the dispatch functions discussed above (`printExpression` etc), which are\nunavoidably megamorphic.\n\nWe have not done a full audit of all functions to check if any are receiving polymorphic/megamorphic inputs\nwhen that could be avoided. Such an audit might throw up potential improvements.\n\n#### Moral of the story\n\nConsistent object shapes are _really_ important for good perf in JS engines.\n\nWe should provide users with utility functions to construct AST nodes which produce the exact same object shape\nas what `oxc-parser` produces. This would speed up not just `oxc-codegen`, but also their own code that\noperates on ASTs.\n\n```ts\n// Good\nconst binary = oxc.binaryExpression(left, operator, right);\n// Bad - different field order, different object shape\nconst binary = { left, right, operator };\n```\n\n### Intended parser pairing\n\nThe printer is meant to be used with `oxc-parser` with `experimentalRawTransfer` enabled.\n\nRaw transfer builds the AST with a JS deserializer rather than via JSON. Node `type` strings then come out\nas source literals, so those big `switch`es compare interned strings by pointer, rather than character by character.\nThis gives a 10% - 30% perf boost versus a JSON AST.\n\n### Fast paths\n\n- Pretty mode always puts a single space either side of a binary or assignment operator, so the whole\n  token is one constant string - `\" + \"`, `\" ??= \"` - and one `write`, instead of space-operator-space.\n  It also makes the token-glue checks unobservable around those operators, so they are not made at all.\n  See `PADDED_BIN_OPERATORS` in [`print/operators.ts`].\n- `printNonNegativeFloat` prints integers below 1000 straight from `String()`. They are the overwhelming\n  majority, and three digits are too few for any shortening form to win.\n- `printString` tests the whole string against one regex of characters needing escapes, and appends it\n  whole when there are none. The escaping loop is a separate function because it almost never runs.\n- `printSpaceBeforeOperator` is one compare, with its three-clause slow path split out so the common path\n  stays small enough for V8 to inline.\n- `printLiteral` switches on `typeof value` first, identifying strings and numbers without touching the\n  `regex` or `bigint` properties.\n- `printNumericLiteral` tests `value > 0 && value < Infinity` first, reaching the same branch the full\n  chain would without the two builtin calls.\n\nEach of these produced a small perf bump individually, ~10% gain in aggregate.\n\n### Indent cache\n\n`printIndent` reads a pre-built string for the current level out of an array.\n\n- The cache is created once per _process_, alongside the module-level state in `state.ts`.\n  Every `State` carries a reference to it. So indent strings one printer run has grown are there for the next.\n  This cache is shared between JS and TS, sourcemap and no-sourcemap builds.\n- `growIndents` deliberately forces each new indent string flat (a throwaway `charCodeAt(0)`) before caching it,\n  paying the flatten cost once rather than on every append.\n\n### Source maps computed at the end\n\nDuring printing, a mapped write records only an output offset and the node's original UTF-16 offset\nfrom `start` / `end`. Nodes without offsets are not mapped, and `sourceText` is required to convert\nsource offsets to line/column positions.\n\nThe offset pairs go into an `Int32Array` which, like the indent cache, is created once per process\nand reused by every print - each `State` tracks how much of it that print has filled, and the buffer\ndoubles when a print fills it, so a steady-state print allocates nothing to record its mappings.\n\n`generateSourceMap` then walks the output once at the end, counting ECMAScript line terminators,\nbuilds the equivalent line table for `sourceText`, turns both sets of offsets into line/column,\nand encodes the mappings as base64 VLQ. Tracking lines and columns throughout would cost every write\nin every build.\n\n#### Potential future improvement\n\nProducing source maps is exactly the kind of raw number-crunching that native code excels at.\nAnd, unlike the AST, it's just small integers - the kind of data that transfers across the JS-native\nboundary cheaply.\n\nThe offsets are already assembled on the JS side in an `Int32Array`, so it might be worthwhile making\na single JS-Rust-JS round-trip with it, with the sourcemap generation (VLQ encoding etc) happening on Rust side.\n\nWASM might also be a good option for this.\n\n### No separate stack in the binary walk\n\n`printBinaryish` walks the left spine of a binary/logical chain iteratively. `a + b + c + d` nests as deep\nas it is long, so recursion would put the stack at the mercy of the input.\n\nRust does the same, but with an explicit `Stack` on `Codegen`. Here each level has a small visitor\nobject, and pending outer levels are threaded through its `parent` field rather than a separate stack array.\n\n### Lookup tables are `__proto__: null`\n\nSo a property miss cannot walk up to `Object.prototype`.\n\n## Where the ESTree AST differs\n\nThe two printers walk different ASTs, and some of the differences reach the output.\n\n### Node types\n\n- **One `Literal` node type** covers strings, numbers, booleans, `null`, regexes and bigints, where Rust\n  has six separate types. `printLiteral` tells them apart from `typeof value` and the presence of `regex` / `bigint`.\n- **`#x in obj` arrives as a `BinaryExpression`** with a `PrivateIdentifier` on the left, not as its own node type.\n  Hence the extra test in `printExpression`'s `BinaryExpression` arm.\n\n### Parenthesized function expressions\n\nOxc's Rust AST records that a function expression was parenthesized in a `pife` flag, and prints the parentheses\nback from it.\n\nESTree has no such flag. The information is only there when the AST was parsed with `preserveParens`,\nas a `ParenthesizedExpression` wrapper. `printExpression`'s arm for that node re-emits the parentheses\naround a function or arrow, and is transparent around anything else.\n\nThis is why `printBinaryish` prints its operands **unstripped**. `withoutParens` is used for\nthe structural type tests, but the node handed to `printExpression` keeps its wrapper,\nso that arm can run.\n\n### Things ESTree cannot express\n\n- `import \"m\";` versus `import {} from \"m\";`\n- An absent `with` clause versus an empty `with {}`\n- `assert {...}` versus `with {...}`\n- The span of a `with {...}` clause (only its individual attributes have ESTree locations)\n\nThe conformance harness normalizes the Rust AST down to what ESTree can express before printing,\nrather than expecting the JS side to reproduce information it was never given.\nSee `Normalize` in `tasks/codegen_conformance/src/lib.rs`.\n\n### Positions, and other producers\n\n- **Oxc offsets only.** `start` / `end` offsets are converted to UTF-16 line/column once at the end.\n  Source maps require `sourceText`; `loc` and `range` are not supported inputs.\n\n## How it is tested\n\n`oxc-codegen` is tested against all Test262, Acorn-JSX, and TypeScript test cases - about 62,000 fixtures.\n\nEvery fixture is printed three times:\n\n1. In Rust, with a source map, via the `oxc-codegen-conformance` NAPI addon (`tasks/codegen_conformance`).\n2. In JS, through the no-maps build.\n3. In JS, through the separately compiled maps build.\n\nAll three generated outputs must agree byte for byte. Every decoded generated/original line, column,\nand optional original name from the maps build must also agree with Rust, including ordering and\nduplicate suppression.\n\nBoth sides parse the same source text with the same `SourceType`, derived by the same function.\nFixtures which do not parse cleanly are skipped, rather than passing quietly.\n\nEvery fixture is checked in **both `preserveParens` modes**. They are different paths through the printer -\nwith the wrappers present it must decide what to re-emit, without them it must re-derive every parenthesis\nfrom precedence.\n\n## Rules to keep\n\nA checklist, all of it argued for above.\n\n1. **Never read a character of `state.output`.**\n2. **A `write` must declare a category which truthfully describes its final character**, and must never be\n   given an empty string.\n3. **Only use `writeNoLast` where `last` is provably dead.** A real write must follow before any reader runs.\n4. **Preserve the three numbering properties** when adding or renumbering a `CAT_*` code, and keep the\n   `if (DEBUG)` assertions which check them.\n5. **Never let a build-time flag add or remove a field on `State`.**\n6. **Do not add an arm near the top of `printExpression`, `printStatement` or other dispatch functions**\n   unless the node type really is that common.\n7. **Do not relax `const_functions` to `let`.**\n8. **Run the conformance suites against a debug build** (`pnpm run build-test`), so the assertions are live.\n9. **Benchmark against a release build** (`pnpm run bench` rebuilds one first). A debug build keeps\n   `debugAssert` calls and does not represent shipped performance.\n\n[`oxc_codegen`]: https://github.com/oxc-project/oxc/tree/main/crates/oxc_codegen\n[`print/categories.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/categories.ts\n[`print/write.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/write.ts\n[`print/space.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/space.ts\n[`print/operators.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/operators.ts\n[`print/types.ts`]: https://github.com/oxc-project/oxc/blob/main/packages/codegen/src-js/print/types.ts\n\n<!-- File: packages/codegen/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Codegen Skill\n\nPath: `packages/codegen/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/package.json)\n\n<!-- File: packages/codegen/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Codegen Skill\n\nPath: `packages/codegen/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/tsconfig.json)\n\n<!-- File: .github/copilot-instructions.md (Tokens: ~150 | Category: copilot-instructions) -->\n# Copilot Review Instructions\n\nWhen reviewing pull requests in this repository:\n\n- Do not guess or infer compiler errors. Only report them when supported by actual compiler diagnostics.\n- Treat files inside any `fixtures` directories as test data, not production code.\n- Fixture files may intentionally contain buggy, unsafe, or syntactically invalid code to validate parser/linter/transformer behavior.\n- Do not report normal code-quality or correctness issues for fixture files.\n- You may report a fixture issue only when the test data appears incorrect or mislabeled:\n  - The folder or filename does not match what the test claims to cover.\n  - The fixture content does not actually test the described case.\n  - The fixture appears accidentally broken rather than intentionally crafted.\n\n<!-- File: crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json (Tokens: ~300 | Category: plugin-manifest) -->\n# plugins Plugin Manifest\n\nPath: `crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/crates/oxc_linter/fixtures/extends_config/plugins/child_with_plugins_parent_default_plugin.json)\n\n<!-- File: crates/oxc_transformer_plugins/README.md (Tokens: ~391 | Category: plugin-manifest) -->\n# Oxc Transformer Plugins\n\nSpecialized transformation plugins for common JavaScript and TypeScript patterns.\n\n## Overview\n\nThis crate provides specific transformation plugins that can be used independently or as part of the broader oxc transformation pipeline. These plugins handle common transformation needs like global variable injection and module format conversion.\n\n## Key Features\n\n- **Global variable injection**: Add global variables to modules\n- **Module runner transforms**: Transform modules for different execution environments\n- **Global defines replacement**: Replace compile-time constants\n- **Composable plugins**: Use individually or combine multiple plugins\n\n## Architecture\n\n### Plugin System\n\nEach plugin implements the transformation logic for a specific use case:\n\n- **Focused responsibility**: Each plugin handles one type of transformation\n- **Traverse integration**: Uses oxc's traversal system for efficient AST walking\n- **Composable**: Multiple plugins can be applied in sequence\n- **Context-aware**: Access to semantic information when needed\n\n### Common Use Cases\n\n#### Build-time Optimizations\n\n- **Dead code elimination**: Remove unreachable code paths\n- **Constant folding**: Replace compile-time constants\n- **Environment variables**: Inject environment-specific values\n\n#### Module System Compatibility\n\n- **Polyfill injection**: Add necessary polyfills for target environments\n- **Global shimming**: Provide Node.js globals in browser environments\n- **Format conversion**: Transform between module formats\n\n#### Development Tools\n\n- **Debug injection**: Add development-time debugging code\n- **Hot module replacement**: Support for HMR systems\n- **Testing utilities**: Inject test-specific globals\n\n### Integration Points\n\nThese plugins integrate with:\n\n- **oxc_transformer**: Core transformation infrastructure\n- **oxc_traverse**: AST traversal and mutation\n- **Build tools**: Webpack, Vite, Rollup, and other bundlers\n- **CLI tools**: Direct usage in command-line transformations\n\nThe plugin architecture enables extensible, composable transformations while maintaining high performance through oxc's efficient traversal system.\n\n<!-- File: npm/oxlint-plugins/README.md (Tokens: ~507 | Category: plugin-manifest) -->\n# @oxlint/plugins\n\nPlugin utilities for [Oxlint](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n\nThis package provides optional functions to assist in creating Oxlint JS plugins and rules.\n\n## Installation\n\n```bash\nnpm install @oxlint/plugins\n```\n\n## Usage\n\n### Define functions\n\nUse `definePlugin` and `defineRule` if authoring your plugin in TypeScript for type safety.\n\n```typescript\nimport { definePlugin, defineRule } from \"@oxlint/plugins\";\n\nconst rule = defineRule({\n  create(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n});\n\nexport default definePlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n### Types\n\nThis package also includes types for plugins and rules.\n\n```typescript\nimport type { Context, Rule, ESTree } from \"@oxlint/plugins\";\n\nconst rule: Rule = {\n  create(context: Context) {\n    return {\n      Program(node: ESTree.Program) {\n        // Rule logic here\n      },\n    };\n  },\n};\n```\n\n### ESLint compatibility\n\nIf your plugin uses Oxlint's [alternative `createOnce` API](https://oxc.rs/docs/guide/usage/linter/js-plugins#alternative-api),\nuse `eslintCompatPlugin` to convert the plugin so it will also work with ESLint.\n\n```typescript\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nconst rule = {\n  createOnce(context) {\n    return {\n      Program(node) {\n        // Rule logic here\n      },\n    };\n  },\n};\n\nexport default eslintCompatPlugin({\n  meta: { name: \"oxlint-plugin-amazing\" },\n  rules: { amazing: rule },\n});\n```\n\n## Node.js version\n\nThis package requires Node.js 12.22.0+, 14.17.0+, 16.0.0+, or later.\nThis matches the minimum Node.js version required by ESLint 8.\n\nThis package provides both ESM and CommonJS entry points.\n\nSo a plugin which depends on `@oxlint/plugins` can be:\n\n- Used with any version of Oxlint.\n- Used with ESLint 8+.\n- Published as either ESM or CommonJS.\n\n## Docs\n\nFor full documentation, see [Oxlint JS Plugins docs](https://oxc.rs/docs/guide/usage/linter/js-plugins).\n\n<!-- File: packages/codegen/README.md (Tokens: ~1322 | Category: plugin-manifest) -->\n# oxc-codegen\n\nFast, synchronous code generation for JavaScript and TypeScript ASTs.\n\n`oxc-codegen` turns an [ESTree](https://github.com/estree/estree) or\n[TS-ESTree](https://typescript-eslint.io/packages/typescript-estree/) AST into formatted source\ncode. It supports JavaScript, JSX, TypeScript, and TSX.\n\nThe printer is a port of Oxc's Rust `oxc_codegen` crate. With the default options, both printers\nproduce byte-identical output: tab indentation, double-quoted strings, and no comments.\n\n## Installation\n\n```sh\nnpm install oxc-codegen\n```\n\n`oxc-codegen` is ESM-only and requires Node.js `^20.19.0` or `>=22.12.0`.\n\n## Quick start\n\nPair it with [`oxc-parser`](https://www.npmjs.com/package/oxc-parser) to parse and print source code:\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst { program } = parseSync(\"input.js\", \"const answer=6*7\");\nconst { code } = printSync(program);\n\nconsole.log(code);\n// const answer = 6 * 7;\n```\n\nYou can also print a manually constructed AST:\n\n```js\nconst program = {\n  type: \"Program\",\n  sourceType: \"script\",\n  body: [\n    {\n      type: \"ExpressionStatement\",\n      expression: {\n        type: \"CallExpression\",\n        callee: {\n          type: \"MemberExpression\",\n          object: { type: \"Identifier\", name: \"console\" },\n          property: { type: \"Identifier\", name: \"log\" },\n          computed: false,\n          optional: false,\n        },\n        arguments: [{ type: \"Literal\", value: \"Hello!\" }],\n        optional: false,\n      },\n    },\n  ],\n};\n\nconsole.log(printSync(program).code);\n// console.log(\"Hello!\");\n```\n\n### TypeScript and TSX\n\nSet `ts` when the AST can contain TypeScript nodes. For TSX, set both `ts` and `jsx`:\n\n```js\nconst { program } = parseSync(\"component.tsx\", \"const Box = <T,>(value: T) => <div>{value}</div>\");\n\nconst { code } = printSync(program, {\n  ts: true,\n  jsx: true,\n});\n```\n\n## API\n\n### `printSync(node, options?)`\n\n```ts\nfunction printSync(\n  node: ESTree.Program | ESTree.Statement,\n  options?: Options,\n): {\n  code: string;\n  map: SourceMap | null;\n};\n```\n\nPrints a complete `Program` or a single statement and returns the generated source code,\nand (when requested) a standard Source Map v3 object.\n\n```js\nimport { printSync } from \"oxc-codegen\";\nimport { parseSync } from \"oxc-parser\";\n\nconst sourceText = \"const answer=6*7\";\nconst { program } = parseSync(\"input.js\", sourceText);\nconst { code, map } = printSync(program, {\n  sourcemap: true,\n  sourceFilename: \"input.js\",\n  sourceText,\n});\n```\n\nSource-map mappings require `sourceText` and nodes with valid Oxc `start` / `end` offsets.\nA manually constructed AST without offsets can still be printed, but its source map has\nan empty `mappings` string.\n\n### Options\n\n| Option                | Type      | Default | Description                                                      |\n| :-------------------- | :-------- | :------ | :--------------------------------------------------------------- |\n| `indent`              | `string`  | `\"\\t\"`  | Non-empty string of spaces and/or tabs used for one indent level |\n| `startingIndentLevel` | `number`  | `0`     | Starting indent level, from `0` to `1000`                        |\n| `jsx`                 | `boolean` | `false` | Enable TSX-safe printing for ambiguous TypeScript syntax         |\n| `ts`                  | `boolean` | `false` | Select the printer that supports TypeScript nodes                |\n| `sourcemap`           | `boolean` | `false` | Return a Source Map v3 object in `map`                           |\n| `sourceFilename`      | `string`  | `\"\"`    | Original source filename recorded in the source map              |\n| `sourceText`          | `string`  | -       | Original text required for source-map mappings and content       |\n\n## Why pure JavaScript?\n\nMost Oxc packages use native bindings. This package deliberately does not: when an AST already\nlives in JavaScript, passing the entire object graph across a JS/native boundary can cost more than\nprinting it in place. `oxc-codegen` avoids that serialization and uses specialized printer builds\nfor JavaScript and TypeScript workloads.\n\nSee [DESIGN.md](https://github.com/oxc-project/oxc/blob/main/packages/codegen/DESIGN.md) for the\nimplementation details and performance constraints.\n\n## Current limitations\n\n- Comments are not printed.\n- Minified output is not supported.\n\n## Benchmarks\n\nRepresentative time per `printSync` call:\n\n| Fixture                      |     Bytes |       Time |\n| :--------------------------- | --------: | ---------: |\n| `tiny.js`                    |        26 |  0.0001 ms |\n| `RadixUIAdoptionSection.jsx` |     2,518 |  0.0070 ms |\n| `react.development.js`       |    72,141 |  0.1518 ms |\n| `binder.ts`                  |   193,077 |  0.3364 ms |\n| `App.tsx`                    |   415,340 |  1.2912 ms |\n| `lodash.js`                  |   544,096 |  0.7977 ms |\n| `kitchen-sink.tsx`           |   732,222 |  4.2924 ms |\n| `antd.js`                    | 6,683,633 | 16.9652 ms |\n\nThese figures come from one machine and are illustrative, not a regression baseline.\nResults vary between runs, most noticeably for large fixtures such as `antd.js`.\n\n<!-- File: packages/codegen/test/fixtures/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# fixtures Documentation\n\nPath: `packages/codegen/test/fixtures/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/oxc-project/oxc/HEAD/packages/codegen/test/fixtures/README.md)\n\n<!-- File: .cursorrules (typescript-expo-jest-detox-cursorrules-prompt-file.mdc) (Tokens: ~124 | Category: cursor-rule) -->\n---\ndescription: \"Cursor rules for TypeScript development with Expo, Jest, and Detox integration.\"\nglobs: **/*\nalwaysApply: false\n---\nYou are an expert in TypeScript, React Native, Expo, and Mobile UI development.\n\nCode Style and Structure\n\nNaming Conventions\nTypeScript Usage\nSyntax and Formatting\nUI and Styling\nSafe Area Management\nPerformance Optimization\nNavigation\nState Management\nError Handling and Validation\nTesting\nSecurity\nInternationalization (i18n)\n\nKey Conventions\n\nAPI Documentation\n\nRefer to Expo's documentation for detailed information on Views, Blueprints, and Extensions for best practices.\n</yakaai_skills>"}