Open-Source Wikis

/

Tailwind CSS

/

Crates

/

crates/node — @tailwindcss/oxide (NAPI binding)

tailwindlabs/tailwindcss

crates/node — @tailwindcss/oxide (NAPI binding)

Active contributors: Robin, Philipp, Jordan

Purpose

crates/node/ is the NAPI binding that exposes the Scanner from oxide to JavaScript. It is published as the @tailwindcss/oxide npm package and is the reason the JavaScript adapters can call Rust code without spawning a subprocess.

Directory layout

crates/node/
├── Cargo.toml             # napi + napi-derive deps
├── package.json           # name: "@tailwindcss/oxide"
├── build.rs               # napi-build entry
├── .cargo/
│   └── config.toml        # Per-target rustflags
├── npm/                   # 14 platform-stub packages (e.g. linux-x64-gnu)
├── scripts/               # Helper scripts (publish, etc.)
└── src/
    ├── lib.rs             # The NAPI-exposed types and methods (~280 lines)
    └── utf16.rs           # UTF-16 ↔ UTF-8 index conversion

What it exposes

The crate re-shapes the Rust Scanner into NAPI-compatible types defined in crates/node/src/lib.rs:

NAPI type Rust origin Used for
ChangedContent wraps tailwindcss_oxide::ChangedContent The unit of work for scan_files.
GlobEntry wraps tailwindcss_oxide::GlobEntry Glob with base + pattern.
SourceEntry wraps tailwindcss_oxide::PublicSourceEntry Public source spec with negation.
ScannerOptions new Constructor input.
Scanner wraps tailwindcss_oxide::Scanner The scanner facade.
CandidateWithPosition new { candidate, position } returned by the LSP API.

Scanner API

#[napi]
impl Scanner {
    #[napi(constructor)]
    pub fn new(opts: ScannerOptions) -> Self { ... }

    #[napi] pub fn scan(&mut self) -> Vec<String>
    #[napi] pub fn scan_files(&mut self, input: Vec<ChangedContent>) -> Vec<String>
    #[napi] pub fn get_candidates_with_positions(&mut self, input: ChangedContent)
              -> Vec<CandidateWithPosition>

    #[napi(getter)] pub fn files(&mut self) -> Vec<String>
    #[napi(getter)] pub fn globs(&mut self) -> Vec<GlobEntry>
    #[napi(getter)] pub fn normalized_sources(&mut self) -> Vec<GlobEntry>
}

These are the methods every adapter calls. scan performs a full disk walk; scan_files works in-memory; get_candidates_with_positions is for tooling that needs to highlight class names in their source location (e.g. the IntelliSense LSP).

UTF-16 conversion

src/utf16.rs exists because JavaScript exposes string positions as UTF-16 code units, but Rust strings are UTF-8 byte slices. IndexConverter walks the original input once and provides a get(byte_position) -> utf16_position mapping. get_candidates_with_positions uses it to translate Rust byte offsets into JavaScript-compatible UTF-16 indices before returning.

Build and publish

The npm package is built via napi-build from build.rs. The publish flow:

  1. Cargo compiles a .node binary per target (configured in .cargo/config.toml).
  2. Each .node binary is bundled into a stub package under crates/node/npm/<target>/. There are 14 such stubs covering the supported matrix:
    • linux-x64-gnu, linux-x64-musl
    • linux-arm64-gnu, linux-arm64-musl
    • linux-arm-gnueabihf
    • darwin-x64, darwin-arm64
    • win32-x64-msvc, win32-x64-gnu, win32-arm64-msvc, win32-ia32-msvc
    • freebsd-x64
    • wasm32-wasi
    • and the universal/loader package itself.
  3. The top-level @tailwindcss/oxide npm package depends on all stubs as optionalDependencies. At install time, npm picks whichever one matches the host platform.
  4. The index.js shim in the published package detects the platform and require()s the right stub.

This is the standard NAPI pattern — napi-rs automates most of it.

Integration points

  • Re-exports the public API of tailwindcss-oxide to JavaScript.
  • Used by tailwindcss, @tailwindcss/cli, @tailwindcss/vite, @tailwindcss/postcss, @tailwindcss/webpack, and @tailwindcss/upgrade.
  • Published as @tailwindcss/oxide.
  • The pnpm workspace (pnpm-workspace.yaml) includes crates/node and crates/node/npm/* so all stubs resolve as workspace:* for development.

Entry points for modification

  • Add a new method on Scanner: declare the wrapper method in lib.rs with #[napi]. The Rust side needs the corresponding Scanner method on the underlying tailwindcss_oxide::Scanner.
  • Add a new NAPI type: add the struct under lib.rs with #[napi(object)]. Implement From on the underlying Rust type if needed.
  • Add a new platform target: add a new entry to crates/node/npm/, the GitHub Actions release workflow (.github/workflows/release.yml), and the .cargo/config.toml linker config.

For the underlying scanner implementation see oxide.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

crates/node — @tailwindcss/oxide (NAPI binding) – Tailwind CSS wiki | Factory