Skip to content

bun init: put typescript in devDependencies, not peerDependencies - #35445

Open
robobun wants to merge 3 commits into
mainfrom
farm/581fe4fb/init-typescript-dev-dependency
Open

robobun wants to merge 3 commits into
mainfrom
farm/581fe4fb/init-typescript-dev-dependency

Conversation

@robobun

@robobun robobun commented Jul 24, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #7040.

Repro

$ mkdir /tmp/x && cd /tmp/x
$ bun init -y
$ cat package.json

Before:

{
  "name": "x",
  "module": "index.ts",
  "type": "module",
  "private": true,
  "devDependencies": {
    "@types/bun": "latest"
  },
  "peerDependencies": {
    "typescript": "^6"
  }
}

After:

{
  "name": "x",
  "module": "index.ts",
  "type": "module",
  "private": true,
  "devDependencies": {
    "@types/bun": "latest",
    "typescript": "^6"
  }
}

Why

peerDependencies declares "my consumer must install this". For a scaffolded application that has no consumer, the field is meaningless. For a scaffolded library it is actively wrong: publishing the scaffold as-is makes every installer of the library warn (or, with npm's auto-install-peers, pull in) typescript@^6 even in a plain-JS project. TypeScript is a build-time tool; it belongs in devDependencies, which is where every other scaffolding tool that adds it (create-vite, create-next-app, tsc --init workflows, etc.) puts it.

Fix

  • init_command.rs: write typescript into devDependencies instead of peerDependencies. The existing "skip if already declared" check (devDependencies or peerDependencies) is kept, so a user who deliberately put it under peerDependencies keeps their version and we don't duplicate it.
  • React templates (react-app/react-tailwind/react-shadcn package.json + their checked-in bun.lock workspace header): move the entry accordingly. These package.json files are include_bytes!'d directly, so they're the source of truth for bun init --react*.

Verification

test/cli/init/init.test.ts is updated to assert devDependencies.typescript === "^6" and peerDependencies === undefined for every template. The existing installs TypeScript 6, typechecks, and builds matrix test confirms TypeScript is still installed and tsc --noEmit still passes.

$ bun bd test test/cli/init/init.test.ts
 15 pass
 0 fail

no test proof · iteration 1 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/init/init.test.ts

@robobun

robobun commented Jul 24, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 6:09 PM PT - Jul 24th, 2026

✅ @robobun, your commit ea756e34a71ac8b762b3d21033bf2867cc08485b passed in Build #79759! 🎉


🧪   To try this PR locally:

bunx bun-pr 35445

That installs a local version of the PR into your bun-35445 executable, so you can run:

bun-35445 --bun

@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@robobun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e273d95e-2011-4e42-8702-e889be75798e

📥 Commits

Reviewing files that changed from the base of the PR and between 028f7a3 and ea756e3.

⛔ Files ignored due to path filters (3)
  • src/runtime/cli/init/react-app/bun.lock is excluded by !**/*.lock
  • src/runtime/cli/init/react-shadcn/bun.lock is excluded by !**/*.lock
  • src/runtime/cli/init/react-tailwind/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • docs/quickstart.mdx
  • src/runtime/cli/init/react-app/package.json
  • src/runtime/cli/init/react-shadcn/package.json
  • src/runtime/cli/init/react-tailwind/package.json
  • src/runtime/cli/init_command.rs
  • test/cli/init/init.test.ts

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — mechanical move of typescript from peerDependencies to devDependencies across the init scaffolding.

What was reviewed:

  • init_command.rs: the two sequential devDependencies get/put blocks compose correctly (second reads back the object the first wrote), and needs_typescript_dependency still checks peerDependencies so an existing user entry there is preserved rather than duplicated.
  • Template package.json + bun.lock workspace headers: only the root workspace entry moved; remaining peerDependencies hits in the lockfiles are third-party package metadata, correctly untouched.
  • docs/quickstart.mdx example updated to match the new output.
  • Test assertions cover all four templates plus the "init twice" re-run path.
Extended reasoning...

Overview

This PR moves the typescript dependency that bun init writes into scaffolded projects from peerDependencies to devDependencies, fixing #7040. It touches: the Rust init command (init_command.rs, a 3-identifier rename in one block), the three embedded React template package.json files and their checked-in bun.lock workspace headers, the quickstart docs example, and the init test file's assertions.

Security risks

None. This only affects which JSON key a build-time dependency is written under in newly scaffolded projects. No untrusted input handling, no filesystem/path logic, no auth/crypto.

Level of scrutiny

Low. The Rust change is a straightforward rename (peerDependencies → devDependencies) in a block that already followed the exact get-or-create/mutate/put pattern used by the adjacent needs_dev_dependencies block. I verified the two blocks don't clobber each other: the first object.put(b"devDependencies", ...) stores the object with @types/bun, and the second object.get(b"devDependencies") retrieves that same stored object before adding typescript — the tests confirm both entries land in the output. The needs_typescript_dependency guard was intentionally left checking both devDependencies and peerDependencies, so re-running init on a project where the user deliberately kept typescript as a peer dep won't add a duplicate.

The template/lockfile changes are pure JSON key relocation. Grepping src/runtime/cli/init for peerDependencies after the change shows only third-party package metadata in the lockfile packages section, which is correct.

Other factors

Test coverage is thorough: exact-object assertions for the blank template, toHaveProperty("devDependencies.typescript", "^6") + peerDependencies === undefined for all three React templates, and the "bun init twice" snapshot. The pre-existing installs TypeScript 6, typechecks, and builds matrix test guards against install-time regressions from the lockfile header edits. The rationale (peer deps are semantically wrong for a build tool in an app scaffold; matches create-vite/create-next-app) is sound and uncontroversial.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — mechanical move of typescript from peerDependencies to devDependencies across the init scaffolds.

What was reviewed:

  • init_command.rs: the needs_typescript_dependency block now targets devDependencies; it runs after the needs_dev_dependencies block, so the second object.get(b"devDependencies") sees @types/bun already added and both entries land in the same object (confirmed by the updated toEqual snapshot in init.test.ts).
  • The skip-if-already-declared check still probes both devDependencies and peerDependencies, so re-initing over a pre-existing peerDep doesn't duplicate.
  • Template bun.lock edits touch only the workspace-root header; the resolved packages map is unchanged (typescript was already installed as an auto-installed peer), so the lockfiles stay consistent.
  • Grepped src/runtime/cli/init/ and docs/ for other peerDependencies+typescript references — none remain.
Extended reasoning...

Overview

This PR moves the typescript: "^6" entry that bun init writes from peerDependencies to devDependencies. It touches: a 3-identifier rename in src/runtime/cli/init_command.rs (the needs_typescript_dependency block), three include_bytes!'d template package.json files (react-app/tailwind/shadcn) plus their checked-in bun.lock workspace headers, the docs/quickstart.mdx example, and test/cli/init/init.test.ts assertions.

Security risks

None. This only changes which JSON key a scaffolding template writes a version string under. No user input parsing, no filesystem/path handling changes, no auth/crypto.

Level of scrutiny

Low. The Rust change is a pure identifier substitution reusing the exact same object.get(...).unwrap_or_else(...) / put_string / object.put pattern as the needs_dev_dependencies block immediately above it. I verified the ordering: needs_dev_dependencies runs first and puts the updated devDependencies back onto object, so the subsequent object.get(b"devDependencies") in the typescript block returns the object already containing @types/bun — the bun init works test's toEqual on the full package.json confirms both entries coexist. The lockfile edits only touch the workspaces[""] header (the resolved packages map is byte-identical, since typescript was already resolved as an auto-installed peer). The template package.json edits are trivial JSON section moves.

Other factors

  • The pre-existing needs_typescript_dependency guard still checks both devDependencies and peerDependencies, so a user who deliberately keeps typescript under peerDependencies won't get a duplicate on re-init.
  • Test coverage is updated for every template (-y, --react, --react=tailwind, --react=shadcn) to assert devDependencies.typescript === "^6" and peerDependencies === undefined, and the existing installs TypeScript 6, typechecks, and builds matrix test still covers that tsc --noEmit passes with the installed TypeScript.
  • No CODEOWNERS match the changed paths.
  • Grepped for any remaining peerDependencies+typescript references in src/runtime/cli/init/ and docs/ — the only remaining peerDependencies occurrences in the template lockfiles are the peer-dep declarations of resolved third-party packages (react-dom→react, etc.), which are unrelated.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — mechanical move of typescript from peerDependencies to devDependencies across the init scaffold.

What was reviewed:

  • init_command.rs: the typescript block now targets devDependencies; ordering is fine since it re-gets after the needs_dev_dependencies block's put, so @types/bun is preserved (confirmed by the exact-equality assertion in "bun init works").
  • The retained peerDependencies skip-check means re-initing a project that already has typescript under peerDeps won't duplicate it.
  • Grepped src/runtime/cli/init/ and docs/ for stale peerDependencies+typescript references — none remain (leftover peerDependencies in the bun.lock files are transitive package metadata, not the workspace root).
  • The one CI failure (test-worker-message-port-transfer-terminate.js SIGABRT on x64-asan) is unrelated to this change.
Extended reasoning...

Overview

Moves the typescript: "^6" entry that bun init writes from peerDependencies to devDependencies. Touches the Rust init command (3-line variable/key rename in the needs_typescript_dependency block), the three include_bytes!'d React template package.json files and their checked-in bun.lock workspace headers, the quickstart doc, and updates init.test.ts assertions to match.

Security risks

None. This is scaffolding metadata for newly-created projects; no auth, crypto, path handling, or untrusted input is involved.

Level of scrutiny

Low. The Rust change reuses the identical get-or-create/put_string/put pattern from the needs_dev_dependencies block immediately above it, just with a different key. The template edits are pure JSON key relocation. The behavior change is user-visible but is a straightforward correction to match ecosystem convention (create-vite, create-next-app, etc.) and fixes a filed issue.

Other factors

  • Test coverage is thorough: exact-equality assertions on the blank template's package.json, toHaveProperty("devDependencies.typescript", "^6") + peerDependencies toBeUndefined for all three React templates, and the existing installs TypeScript 6, typechecks, and builds matrix test still exercises the install path.
  • Verified no sibling sites were missed: grepped for peerDependencies under src/runtime/cli/init/ and for typescript+peerDependencies under docs/ — all remaining hits are transitive package metadata in the lockfiles' packages sections, not the workspace root.
  • The lone CI failure is a known-flaky worker termination ASAN test with no connection to bun init.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeScript being installed as a peer dependency

1 participant