> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meow.style/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js compatibility

> Run real npm packages and frameworks: node: built-ins, CommonJS, child processes, and the node shim.

In [`node-compat` mode](/concepts/modes), meow layers the full Node.js surface on
top of its web baseline. The Node built-ins, CommonJS, and N-API support are
provided by Deno's battle-tested Node-compatibility crates, so real packages and
frameworks run unmodified.

```json meow.config.json theme={null}
{ "mode": "node-compat" }
```

## What's available

<CardGroup cols={2}>
  <Card title="node: built-ins" icon="cube">
    `node:fs`, `node:path`, `node:os`, `node:crypto`, `node:process`,
    `node:child_process`, `node:worker_threads`, `node:http`, and the rest of the
    standard library.
  </Card>

  <Card title="CommonJS & ESM" icon="arrows-to-circle">
    `require()` and `import` interop, with correct CJS/ESM classification and the
    Node resolution algorithm.
  </Card>

  <Card title="The process global" icon="gears">
    `process.argv`, `process.env`, `process.cwd()`, `process.exit()`,
    `process.platform`/`arch`, `process.pid`/`ppid`, and IPC via `process.send`.
  </Card>

  <Card title="Native addons" icon="microchip">
    N-API native modules load through the same package graph.
  </Card>
</CardGroup>

## Frameworks run out of the box

meow boots real frameworks on its tuned V8 build — no toy examples:

<CardGroup cols={3}>
  <Card title="Next.js" icon="forward" />

  <Card title="Vite" icon="bolt" />

  <Card title="Astro" icon="rocket" />

  <Card title="Playwright" icon="masks-theater" />

  <Card title="Puppeteer" icon="ghost" />

  <Card title="Jest workers" icon="vial" />
</CardGroup>

```bash theme={null}
meow x create-next-app@latest my-app
cd my-app
meow run dev
```

## The node shim

This is what makes framework tooling "just work." When meow runs your code, it
writes shim executables into `~/.meow/bin` and puts that directory **first** on
`PATH`:

* **Runtime shims** — `node`, `npm`, `pnpm`, `yarn`, `bun` — proxy straight back
  into meow.
* **Ephemeral shims** — `npx`, `pnpx`, `bunx` — proxy into `meow x`.

So when Next.js, Vite, or a Jest worker shells out to `node ./worker.js`, the call
transparently re-enters meow with the same project context. You don't reconfigure
how tools invoke each other.

<Tip>
  Need to bypass the shim for a single command and reach the real system binary?
  Set `MEOW_NO_SHIM=1` — the shim execs the underlying `node`/`npm`/etc. instead of
  re-entering meow.
</Tip>

The shims are created during `meow run` and `meow install` (not by `meow add`
alone). meow also sets `NODE` and `npm_node_execpath` to the shimmed `node` so
tools that spawn "the current Node" find their way back.

## Child processes & IPC

`child_process.fork` IPC works, including the binary-safe message channel that
`jest-worker` and Turbopack workers rely on. meow honors the standard
`NODE_CHANNEL_FD` / `NODE_CHANNEL_SERIALIZATION_MODE` protocol, so `process.send()`
and `'message'` events behave as Node programs expect.

For in-process parallelism, see [Workers](/runtime/workers), which run as
cooperative V8 isolates on meow's event loop.

## Module resolution

meow's single resolver handles both `import` and `require()`. It applies a **fixed,
deterministic** condition priority rather than Node's author-insertion order:

| Context           | Conditions tried, in order              |
| ----------------- | --------------------------------------- |
| `import` (ESM)    | `meow` → `import` → `node` → `default`  |
| `require()` (CJS) | `meow` → `require` → `node` → `default` |

<Note>
  For a package whose `exports` lists both `node` and `import`, meow deterministically
  prefers `import` regardless of the order the author wrote the keys — a deliberate
  divergence from Node's insertion-order selection that keeps resolution reproducible.
  In practice it matches what virtually all packages intend.
</Note>

When meow can't be *certain* a module is ESM, a `require()` treats it as CommonJS
(the Node-correct default), with a fallback to the ESM translator on a genuine ESM
syntax error — so mixed-module dependencies resolve correctly.

## Lockfile compatibility

Some framework detectors look for a `package-lock.json`. meow can write a tiny
compatibility marker without giving up its own lockfile:

```bash theme={null}
meow install --compat-lockfile
```

This writes a minimal `package-lock.json` flagged `meowCompatibilityLockfile`.
Your `meow.lock.jsonl` remains the authoritative resolution.

<Card title="Next: workers" icon="diagram-project" href="/runtime/workers">
  Cooperative V8 isolates for parallelism.
</Card>
