Skip to main content
In node-compat mode, 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.
meow.config.json
{ "mode": "node-compat" }

What’s available

node: built-ins

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.

CommonJS & ESM

require() and import interop, with correct CJS/ESM classification and the Node resolution algorithm.

The process global

process.argv, process.env, process.cwd(), process.exit(), process.platform/arch, process.pid/ppid, and IPC via process.send.

Native addons

N-API native modules load through the same package graph.

Frameworks run out of the box

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

Next.js

Vite

Astro

Playwright

Puppeteer

Jest workers

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 shimsnode, npm, pnpm, yarn, bun — proxy straight back into meow.
  • Ephemeral shimsnpx, 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.
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.
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, 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:
ContextConditions tried, in order
import (ESM)meowimportnodedefault
require() (CJS)meowrequirenodedefault
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.
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:
meow install --compat-lockfile
This writes a minimal package-lock.json flagged meowCompatibilityLockfile. Your meow.lock.jsonl remains the authoritative resolution.

Next: workers

Cooperative V8 isolates for parallelism.