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 conversionWhat 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:
- Cargo compiles a
.nodebinary per target (configured in.cargo/config.toml). - Each
.nodebinary is bundled into a stub package undercrates/node/npm/<target>/. There are 14 such stubs covering the supported matrix:linux-x64-gnu,linux-x64-musllinux-arm64-gnu,linux-arm64-musllinux-arm-gnueabihfdarwin-x64,darwin-arm64win32-x64-msvc,win32-x64-gnu,win32-arm64-msvc,win32-ia32-msvcfreebsd-x64wasm32-wasi- and the universal/loader package itself.
- The top-level
@tailwindcss/oxidenpm package depends on all stubs asoptionalDependencies. At install time, npm picks whichever one matches the host platform. - The
index.jsshim in the published package detects the platform andrequire()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-oxideto 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) includescrates/nodeandcrates/node/npm/*so all stubs resolve asworkspace:*for development.
Entry points for modification
- Add a new method on
Scanner: declare the wrapper method inlib.rswith#[napi]. The Rust side needs the correspondingScannermethod on the underlyingtailwindcss_oxide::Scanner. - Add a new NAPI type: add the struct under
lib.rswith#[napi(object)]. ImplementFromon 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.tomllinker 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.