Files
twenty/packages/twenty-sdk
martmull 1fe6156cf5 fix(twenty-sdk): restore require/__filename/__dirname in rolldown ESM CLI bundle (#22711)
## Problem

Fixes #22708.

`twenty-sdk` 2.19.0 CLI commands run through the ESM entrypoint (`node
dist/cli.mjs dev --once`, `app:uninstall`, ...) crash on startup:

```
Error: Calling `require` for "fs" in an environment that doesn't expose the `require` function.
```

## Root cause

The 2.19.0 release switched bundling from esbuild to **rolldown** (Vite
7 → 8). Rolldown **inlines CommonJS dependencies** into the ESM output
(`dist/cli.mjs` grows from ~6k to ~136k lines). Those third-party CJS
modules — e.g. `typescript`, pulled in via `ts-morph` — call
`require(...)` and read `__filename` / `__dirname` at load time. None of
those exist in an ES module:

- `require(...)` is routed through rolldown's interop shim, which
**throws** when `require` is absent (i.e. in a `.mjs` file).
- `__filename` / `__dirname` are simply `ReferenceError: … is not
defined in ES module scope`.

esbuild (2.18.0) injected these CJS globals for node-targeted ESM
output; rolldown does not. Because the offending usage lives in
**bundled third-party CJS**, prefixing our own imports could not fix it.

## Fix

Add a banner to the **ESM output only** in `vite.config.node.ts` that
recreates the CJS globals from `import.meta.url`:

```js
import { createRequire as __twentyCreateRequire } from 'node:module';
import { fileURLToPath as __twentyFileURLToPath } from 'node:url';
import { dirname as __twentyDirname } from 'node:path';
const require = __twentyCreateRequire(import.meta.url);
const __filename = __twentyFileURLToPath(import.meta.url);
const __dirname = __twentyDirname(__filename);
```

This is the same `createRequire` pattern the repo already uses for the
`twenty-oxlint-rules` ESM build. The CJS output already provides all
three, so the banner is not applied there.

This PR also prefixes the SDK CLI's own Node-builtin imports with
`node:` (using native ESM imports instead of the interop shim for our
own code) — good hygiene and guarded by a unit test, but note the
**banner is the actual bug fix**.

## Verification (built and run locally)

- Built the node bundle and reproduced the crash on the pre-fix build
(`Calling require for "fs"`), then a follow-on `__filename is not
defined` once `require` was restored.
- With the banner, ran the previously-crashing commands against the
built `dist/cli.mjs`:
  - `--help` → prints usage, exit 0
- `dev --once` → reaches "Checking server… Cannot reach Twenty server"
(normal, no local server)
  - `app:uninstall` → reaches the interactive confirmation prompt
- CJS bin (`dist/cli.cjs`) still works.

## Tests

- `cli-esm-bundle-startup.integration.spec.ts` — runs the **built**
`dist/cli.mjs --help` and asserts no require/ESM-scope crash.
Demonstrated **red without the banner, green with it**. It runs in the
`sdk-test` job (which builds the SDK before tests); it fails loudly in
CI if the artifact is missing and skips locally when unbuilt, so it is
never silently green in CI.
- `node-builtin-import-protocol.test.ts` — guards the `node:`-prefix
hygiene across the CLI source.

Note: the existing `sdk-e2e-test` never caught this because it runs the
CLI via `tsx` on the TypeScript **source**, which has no rolldown shim —
only the bundled `.mjs` reproduces the crash.
2026-07-10 09:59:48 +02:00
..
2026-03-09 15:32:13 +00:00

Twenty logo

Twenty SDK

NPM version License Join the community on Discord

A CLI and SDK to develop, build, and publish applications that extend Twenty CRM.

Quick start

The recommended way to start is with create-twenty-app:

npx create-twenty-app@latest my-twenty-app
cd my-twenty-app
yarn twenty dev

Documentation

Full documentation is available at docs.twenty.com/developers/extend/apps:

  • Getting Started — scaffolding, local server, authentication, dev mode
  • Building Apps — entity definitions, API clients, testing, CLI reference
  • Publishing — deploy, npm publish, marketplace

Guides in this repository:

Manual installation

If you are adding twenty-sdk to an existing project instead of using create-twenty-app:

yarn add twenty-sdk twenty-client-sdk

Then add a twenty script to your package.json:

{
  "scripts": {
    "twenty": "twenty"
  }
}

Run yarn twenty help to see all available commands.

Configuration

The CLI stores credentials per remote in ~/.twenty/config.json. Run yarn twenty remote:add to configure a remote, or yarn twenty remote:list to see existing ones.

Troubleshooting

  • Auth errors: run yarn twenty remote:add to re-authenticate.
  • Typings out of date: restart yarn twenty dev to refresh the client and types.
  • Not seeing changes in dev: make sure dev mode is running (yarn twenty dev).

Contributing

Development setup

git clone https://github.com/twentyhq/twenty.git
cd twenty
yarn install

Development mode

npx nx run twenty-sdk:dev

Production build

npx nx run twenty-sdk:build

Running the CLI locally

npx nx run twenty-sdk:start -- <command>

Resources