create-w3-app
Version:
Create ethereum dApps with w3 stack
530 lines (405 loc) • 20.9 kB
Markdown
# UPDATES.SPEC.md
## `create-w3-app` - Full Dependency Update Specification
---
## Table of Contents
1. [Current State](#current-state)
2. [Phase 1: CLI Self-Dependencies (Low Risk)](#phase-1-cli-self-dependencies-low-risk)
3. [Phase 2: Template Version Map (Mixed Risk)](#phase-2-template-version-map-mixed-risk)
4. [Phase 3: Base Template (Medium Risk)](#phase-3-base-template-medium-risk)
5. [Phase 4: Template File Changes (High Risk)](#phase-4-template-file-changes-high-risk)
6. [Phase 5: CI/CD Workflow](#phase-5-cicd-workflow)
7. [How to Run Locally](#how-to-run-locally)
8. [How to Test](#how-to-test)
9. [How to Release](#how-to-release)
10. [Release Channels](#release-channels)
---
## Current State
The project manages dependencies in **three separate places**:
| Location | Purpose |
|----------|---------|
| `package.json` (root) | Dependencies for the CLI tool itself |
| `src/installers/dependencyVersionMap.ts` | Versions injected into users' scaffolded projects |
| `template/base/package.json` | Base Next.js app starting point for scaffolded projects |
**Current versions (as of v0.7.0):**
| Package | CLI (package.json) | Version Map | Base Template |
|---------|-------------------|-------------|---------------|
| next | `^15.5.4` (devDep) | — | `^15.5.2` |
| react | `^19.2.0` (devDep) | — | `^19.2.0` |
| react-dom | `^19.2.0` (devDep) | — | `^19.0.0` |
| typescript | `^5.9.3` (devDep) | — | `^5.9.3` |
| tailwindcss | `^4.1.14` (devDep) | `^4.0.15` | — |
| @tailwindcss/postcss | `^4.1.14` (devDep) | `^4.0.15` | — |
| postcss | — | `^8.5.3` | — |
| @privy-io/react-auth | `^3.3.0` (devDep) | `^2.4.14` | — |
| @privy-io/wagmi | — | `^1.0.5` | — |
| @rainbow-me/rainbowkit | `^2.2.8` (devDep) | `^2.2.6` | — |
| wagmi | `^2.18.0` (devDep) | `^2.15.6` | — |
| viem | `^2.38.0` (devDep) | `^2.31.0` | — |
| @tanstack/react-query | `^5.90.2` (devDep) | `^5.69.0` | — |
| lucide-react | `^0.545.0` (devDep) | `^0.515.0` | — |
| class-variance-authority | `^0.7.1` (devDep) | `^0.7.1` | — |
| clsx | `^2.1.1` (devDep) | `^2.1.1` | — |
| tailwind-merge | `^3.3.1` (devDep) | `^3.3.1` | — |
| tw-animate-css | `^1.4.0` (devDep) | `^1.3.4` | — |
| prettier | `^3.6.2` (devDep) | `^3.5.3` | — |
| prettier-plugin-tailwindcss | `^0.6.14` (devDep) | `^0.6.11` | — |
| eslint | — | `^9.23.0` | — |
| eslint-config-next | — | `^15.2.3` | — |
| typescript-eslint | `^8.46.0` | `^8.27.0` | — |
| @biomejs/biome | — | `^1.9.4` | — |
| @eslint/eslintrc | — | `^3.3.1` | — |
| eslint-plugin-drizzle | — | `^0.2.3` | — |
| @clack/prompts | `^0.11.0` | — | — |
| @clack/core | `^0.5.0` | — | — |
| zod | `^4.1.12` | — | — |
| superjson | `^2.2.2` | — | — |
| next-auth | `^4.24.11` | — | — |
---
## Phase 1: CLI Self-Dependencies (Low Risk)
These dependencies only affect the CLI tool itself — they do **not** affect scaffolded user projects.
### 1.1 Production Dependencies (`package.json` dependencies)
| Package | Current | Latest | Action | Breaking? |
|---------|---------|--------|--------|-----------|
| `@clack/core` | `^0.5.0` | `1.4.2` | **UPDATE** | YES — v1 API differs |
| `@clack/prompts` | `^0.11.0` | `1.6.0` | **UPDATE** | YES — v1 API differs |
| `@ianvs/prettier-plugin-sort-imports` | `^4.7.0` | check latest | KEEP/UPDATE | Check latest |
| `chalk` | `5.6.2` | latest | KEEP (pinned) | None |
| `commander` | `^14.0.1` | check latest | KEEP/UPDATE | Check latest |
| `eslint-plugin-import-x` | `^4.16.1` | check latest | KEEP/UPDATE | Check latest |
| `execa` | `^9.6.0` | check latest | KEEP/UPDATE | Check latest |
| `fs-extra` | `^11.3.2` | latest | KEEP/UPDATE | None |
| `gradient-string` | `^3.0.0` | latest | KEEP/UPDATE | None |
| `ora` | `9.0.0` | latest | KEEP (pinned) | None |
| `sort-package-json` | `^3.4.0` | latest | KEEP/UPDATE | None |
| `typescript-eslint` | `^8.46.0` | check latest | KEEP/UPDATE | Check latest |
### 1.2 Dev Dependencies (`package.json` devDependencies)
| Package | Current | Latest | Action | Breaking? |
|---------|---------|--------|--------|-----------|
| `@changesets/cli` | `^2.29.7` | check latest | KEEP/UPDATE | Check latest |
| `@types/fs-extra` | `^11.0.4` | latest | KEEP/UPDATE | None |
| `@types/gradient-string` | `^1.1.6` | latest | KEEP/UPDATE | None |
| `@types/node` | `^24.7.1` | check latest | UPDATE | None |
| `prettier` | `^3.6.2` | check latest | KEEP/UPDATE | None |
| `tsup` | `^8.5.0` | check latest | KEEP/UPDATE | None |
| `type-fest` | `^5.0.1` | check latest | KEEP/UPDATE | None |
| `typescript` | `^5.9.3` | `6.0.3` | **HOLD** | Next.js may not support TS 6 |
| `zod` | `^4.1.12` | `4.4.3` | UPDATE | Low risk (minor) |
| `superjson` | `^2.2.2` | check latest | KEEP/UPDATE | None |
| `next-auth` | `^4.24.11` | check latest | KEEP/UPDATE | None |
### 1.3 @clack/prompts Migration Notes
The `@clack/prompts` v0 → v1 migration is the highest-risk item in Phase 1. The CLI uses it in `src/cli/index.ts` for interactive prompts:
- **Affected file:** `src/cli/index.ts`
- **Key usage:** `text()`, `confirm()`, `select()`, `multiselect()`, `isCancel()`, `intro()`, `outro()`, `note()`, `spinner()`
- **What to check:**
- Import paths may have changed
- Function signatures may differ
- Return types may be different
- `isCancel` behavior may have changed
- `spinner` API may differ
---
## Phase 2: Template Version Map (Mixed Risk)
File: `src/installers/dependencyVersionMap.ts`
These are the versions that get injected into users' `package.json` when scaffolding a project.
### 2.1 Safe Updates (No Breaking Changes)
| Package | Current | Target | Category |
|---------|---------|--------|----------|
| `tailwindcss` | `^4.0.15` | `^4.3.1` | Tailwind |
| `postcss` | `^8.5.3` | `^8.5.15` | Tailwind |
| `@tailwindcss/postcss` | `^4.0.15` | `^4.3.1` | Tailwind |
| `class-variance-authority` | `^0.7.1` | `^0.7.1` | shadcn (no change) |
| `clsx` | `^2.1.1` | `^2.1.1` | shadcn (no change) |
| `tailwind-merge` | `^3.3.1` | `^3.3.1` | shadcn (no change) |
| `tw-animate-css` | `^1.3.4` | `^1.4.0` | shadcn |
| `prettier` | `^3.5.3` | latest | ESLint |
| `@eslint/eslintrc` | `^3.3.1` | latest | ESLint |
| `prettier-plugin-tailwindcss` | `^0.6.11` | `^0.6.14` | ESLint |
| `eslint` | `^9.23.0` | latest | ESLint |
| `eslint-plugin-drizzle` | `^0.2.3` | latest | ESLint |
| `@biomejs/biome` | `^1.9.4` | latest | Biome |
| `@tanstack/react-query` | `^5.69.0` | `^5.101.1` | Web3 |
| `viem` | `^2.31.0` | `^2.53.1` | Web3 |
| `@rainbow-me/rainbowkit` | `^2.2.6` | `^2.2.11` | Web3 |
### 2.2 Breaking Updates (Template Files Need Rewriting)
| Package | Current | Target | Breaking Change |
|---------|---------|--------|-----------------|
| `eslint-config-next` | `^15.2.3` | `^16.x` | Must match Next.js 16. ESLint flat config changes |
| `typescript-eslint` | `^8.27.0` | latest | Low risk, but types may change |
| `@privy-io/react-auth` | `^2.4.14` | `^3.32.2` | Major v2→v3. `PrivyClientConfig`, `PrivyProvider` API |
| `@privy-io/wagmi` | `^1.0.5` | `^4.0.13` | Major v1→v4. Requires privy v3. `createConfig`/`WagmiProvider` from `@privy-io/wagmi` |
| `wagmi` | `^2.15.6` | `^3.6.18` | Major v2→v3. Hook renames, connector deps now optional |
| `lucide-react` | `^0.515.0` | `^1.21.0` | Major v0→v1. Icon import API completely changed |
### 2.3 wagmi v2 → v3 Breaking Changes
From wagmi migration guide:
1. **Connector dependencies now optional** — `injected` no longer requires any extra package. For our templates using only `injected()`, no change needed.
2. **Hook renames:**
- `useAccount` → `useConnection`
- `useAccountEffect` → `useConnectionEffect`
- `useSwitchAccount` → `useSwitchConnection`
3. **Mutate function renames:** Custom names (e.g. `connect()`) → `mutate()`/`mutateAsync()`
4. **Removed:** `useConnect().connectors` → use `useConnectors()`
5. **Removed:** `useSwitchChain().chains` → use `useChains()`
6. **Removed:** `useDisconnect().connectors` and `useSwitchConnection().connectors` → use `useConnections()`
7. **Minimum TypeScript:** Now `5.9.3`
> **Note:** Our template config files (`wagmi.ts`, `wagmi-privy.ts`, `wagmi-rainbow.ts`) only use `createConfig`, `http`, `injected`, and `WagmiProvider` — these core APIs are **unchanged** in v3. The provider components in `template/extras/src/provider/` may need updates if they use any renamed hooks.
### 2.4 @privy-io/react-auth v2 → v3 Breaking Changes
- `PrivyClientConfig` type may have new required fields or renamed options
- `PrivyProvider` props may have changed
- `embeddedWallets` configuration shape may have changed
- `loginMethods` values may differ
**Affected template files:**
- `template/extras/config/privy.ts`
- `template/extras/src/provider/privy-provider.tsx`
- `template/extras/src/provider/privy-wagmi-provider.tsx`
### 2.5 @privy-io/wagmi v1 → v4 Breaking Changes
Peer dependencies from npm registry for v4.0.13:
- `@privy-io/react-auth: "^3"` (matches our privy v3 update)
- `wagmi: ">=2"` (works with both v2 and v3)
- `viem: "2.52.0"` (pin specific version)
- `react: ">=18"`
The `createConfig` and `WagmiProvider` API from `@privy-io/wagmi` need verification.
**Affected template files:**
- `template/extras/config/wagmi-privy.ts`
- `template/extras/src/provider/privy-wagmi-provider.tsx`
### 2.6 lucide-react v0 → v1 Breaking Changes
- Icon imports changed from named exports to default exports or different paths
- v0: `import { Activity } from "lucide-react"`
- v1: `import Activity from "lucide-react/dist/esm/icons/activity"` or tree-shakeable individual imports
- Dynamic icon imports API changed
**Affected template files:**
- Any component importing lucide icons (check `template/extras/src/app/components/connect.tsx` and `template/extras/src/pages/components/connect.tsx` variants)
---
## Phase 3: Base Template (Medium Risk)
File: `template/base/package.json`
### 3.1 Version Updates
| Package | Current | Target | Notes |
|---------|---------|--------|-------|
| `next` | `^15.5.2` | `^16.2.9` | Requires Node >=20.9.0 |
| `react` | `^19.2.0` | `^19.2.7` | Patch bump |
| `react-dom` | `^19.0.0` | `^19.2.7` | Match react version |
| `@types/react` | `^19.0.0` | `^19.2.7` | Match react version |
| `@types/react-dom` | `^19.2.0` | `^19.2.7` | Match react-dom version |
| `typescript` | `^5.9.3` | `^5.9.3` | Hold — TS 6.0.3 not needed yet |
| `@types/node` | `^24.7.1` | latest | Check latest |
### 3.2 Script Changes (Next.js 16)
Next.js 16 makes Turbopack the default. Remove `--turbopack` flags:
```diff
- "dev": "next dev --turbopack",
- "build": "next build --turbopack",
+ "dev": "next dev",
+ "build": "next build",
```
### 3.3 Next.js 16 Breaking Changes Summary
1. **Turbopack is default** — No need for `--turbopack` flag. If template uses custom webpack config, add `--webpack` flag.
2. **`next lint` removed** — Must use ESLint CLI directly. The `lint` script in the template should use `eslint .` instead of `next lint`.
3. **`middleware` → `proxy`** — Rename middleware file and functions in template if any exist.
4. **Async Request APIs** — `cookies()`, `headers()`, `params`, `searchParams` are now Promises. Template page/layout files need `await`.
5. **React 19.2** — Required by Next.js 16.
6. **`eslint-config-next`** — Must match Next.js 16 (`^16.x`). ESLint flat config is default.
7. **Minimum Node.js** — `20.9.0` (was 18.17.0).
---
## Phase 4: Template File Changes (High Risk)
### 4.1 Files Requiring Changes
| File | Change Needed | Reason |
|------|--------------|--------|
| `template/base/package.json` | Update versions, remove `--turbopack` | Next 16 + version bumps |
| `template/base/next.config.js` | Remove deprecated options if any | Next 16 |
| `template/extras/config/privy.ts` | `PrivyClientConfig` type update | Privy v3 |
| `template/extras/config/wagmi-privy.ts` | `createConfig` from `@privy-io/wagmi` v4 API | @privy-io/wagmi v4 |
| `template/extras/src/provider/privy-provider.tsx` | `PrivyProvider` API update | Privy v3 |
| `template/extras/src/provider/privy-wagmi-provider.tsx` | `PrivyProvider`/`WagmiProvider` from new packages | Privy v3 + @privy-io/wagmi v4 |
| `template/extras/src/app/layout/*.tsx` | `params` → `await params` | Next 16 async params |
| `template/extras/src/app/page/*.tsx` | `searchParams` → `await searchParams` | Next 16 async params |
| `template/extras/src/pages/_app/*.tsx` | No change (Pages Router not affected) | — |
| `template/extras/src/app/components/connect.tsx` | lucide-react v1 imports | lucide-react v1 |
| `template/extras/src/pages/components/connect.tsx` | lucide-react v1 imports | lucide-react v1 |
| `template/extras/config/_eslint.base.js` | Flat config updates, remove `next lint` | Next 16 + ESLint flat config |
| `template/extras/config/postcss.config.js` | Check for Tailwind v4 compatibility | Tailwind v4.3 |
| `template/extras/config/components.json` | Check for shadcn compatibility | May need update |
| `template/extras/src/styles/globals.css` | Check Tailwind v4.3 directives | Tailwind v4.3 |
| `template/extras/src/styles/shadcn-globals.css` | Check Tailwind v4.3 + shadcn | Tailwind v4.3 |
| `src/installers/eslint.ts` | `eslint-config-next` version, remove `next lint` scripts | Next 16 |
| `src/installers/privy.ts` | Package dependencies list, file copy logic | Privy v3 + @privy-io/wagmi v4 |
| `src/installers/wagmi.ts` | Package dependencies, config file paths | wagmi v3 |
| `src/installers/rainbow.ts` | Package dependencies | RainbowKit update |
| `src/installers/tailwind.ts` | Package dependencies | Tailwind v4.3 |
| `src/installers/shadcn.ts` | Package dependencies | shadcn/lucide updates |
### 4.2 Template Layout/Page Files — Async Params Migration
App Router layout and page files need `params` and `searchParams` to be awaited:
```tsx
// Before (Next 15):
export default function Layout({ children, params }: { children: React.ReactNode; params: { slug: string } }) {
const { slug } = params
// ...
}
// After (Next 16):
export default async function Layout({ children, params }: { children: React.ReactNode; params: Promise<{ slug: string }> }) {
const { slug } = await params
// ...
}
```
Multiple variants exist in:
- `template/extras/src/app/layout/` — 7 layout variants
- `template/extras/src/app/page/` — 7 page variants
Each variant must be checked and updated as needed.
### 4.3 eslint-config-next Update
Since Next.js 16 removed the `next lint` command, the template's `lint` script must change:
```diff
- "lint": "next lint",
- "lint:fix": "next lint --fix",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
```
This is handled in `src/installers/eslint.ts`.
---
## Phase 5: CI/CD Workflow
File: `.github/workflows/release.yml`
### 5.1 Changes
| Setting | Current | Target | Reason |
|---------|---------|--------|--------|
| `node-version` | `20` | `22` | Next 16 requires Node >=20.9. LTS 22 is current |
| `pnpm/action-setup` version | `v2` | `v4` | Latest version |
| `actions/checkout` | `v4` | `v4` | Already latest |
| `actions/setup-node` | `v4` | `v4` | Already latest |
| pnpm version | `9` | `9` | Staying on pnpm 9 |
### 5.2 Required GitHub Secrets
| Secret | Purpose |
|--------|---------|
| `NPM_TOKEN` | Publish to npm registry |
| `GH_PAT` | Create GitHub Release |
---
## How to Run Locally
```bash
# 1. Install dependencies
pnpm install
# 2. Type-check the CLI source
pnpm typecheck
# 3. Lint the CLI source
pnpm lint
# 4. Build the CLI
pnpm build
# 5. Run the CLI and create a test project
# This will scaffold a full app in ../my-test-app
pnpm start ../my-test-app
# 6. Navigate to the test project
cd ../my-test-app
# 7. Install the generated project's dependencies
pnpm install
# 8. Run the generated dev server
pnpm dev
# Expected: Next.js dev server starts on http://localhost:3000
# 9. Type-check the generated project
pnpm typecheck
# Expected: No type errors
# 10. Build the generated project
pnpm build
# Expected: Successful production build
```
**Quick dev loop (CLI changes only):**
```bash
# Watch mode: rebuilds CLI on every change and auto-runs
pnpm dev
```
---
## How to Test
### Test Matrix
Since the CLI supports many combinations of options, test each major path:
| Test Case | Flags/Options | What to Verify |
|-----------|--------------|----------------|
| **Default** | `--default` | Tailwind + Privy installs, app builds |
| **Tailwind only** | Select Tailwind: yes, Shadcn: no, Auth: none, Wagmi: no | Tailwind works, no web3 packages |
| **Tailwind + Shadcn** | Tailwind: yes, Shadcn: yes, Auth: none | shadcn components work, lucide icons render |
| **Privy + Wagmi** | Auth: Privy, Wagmi: yes | Providers wrap correctly, env vars exist |
| **Rainbow Kit** | Auth: Rainbow Kit | RainbowKit+wagmi+viem installed, provider renders |
| **ESLint** | ESLint: yes | Config files created, scripts added, lint runs |
| **Biome** | Biome: yes | biome.jsonc created, scripts added |
| **App Router** | Router: App Router | layout.tsx exists (not _app.tsx) |
| **Pages Router** | Router: Pages Router | _app.tsx exists (not layout.tsx) |
| **No Git** | `--noGit` | No .git directory created |
| **No Install** | `--noInstall` | No node_modules, manual `pnpm install` works after |
### Automated Check
After scaffolding each test case:
```bash
cd <test-project>
pnpm typecheck # Must pass
pnpm build # Must pass
```
---
## How to Release
### 1. Create a Changeset
```bash
pnpm changeset
```
This interactively asks:
- Which packages changed (select `create-w3-app`)
- Type of bump: `major` | `minor` | `patch`
- Summary message
### 2. Version Bump
```bash
pnpm release
```
This runs `changeset version` which:
- Bumps the version in `package.json`
- Updates `CHANGELOG.md`
- Deletes consumed changeset files
### 3. Build and Publish
```bash
# Build the CLI
pnpm build
# Publish to npm (latest tag)
pnpm pub:release
# Or publish with tags:
pnpm pub:beta # --tag beta
pnpm pub:next # --tag next
```
### 4. GitHub Release
The GitHub release is created **automatically** on push to `main` via `.github/workflows/release.yml`:
1. Push to `main` triggers the workflow
2. Workflow checks if the version already exists on npm
3. If new: publishes to npm + creates GitHub Release from `CHANGELOG.md`
**Manual trigger:** Use `workflow_dispatch` in GitHub Actions UI.
### 5. Post-Release Checklist
- [ ] Verify package appears on [npmjs.com/package/create-w3-app](https://www.npmjs.com/package/create-w3-app)
- [ ] Verify GitHub Release created at [github.com/gopiinho/create-w3-app/releases](https://github.com/gopiinho/create-w3-app/releases)
- [ ] Test fresh install: `npx create-w3-app@latest test-project`
- [ ] Verify `npx create-w3-app@latest` resolves correctly
- [ ] Test scaffolded app: `cd test-project && pnpm install && pnpm build`
---
## Release Channels
| Channel | Command | npm Tag | Use Case |
|---------|---------|---------|----------|
| `latest` | `pnpm pub:release` | `latest` | Stable releases |
| `beta` | `pnpm pub:beta` | `beta` | Pre-release testing |
| `next` | `pnpm pub:next` | `next` | Canary/experimental |
Users install specific channels:
```bash
npx create-w3-app@latest # stable
npx create-w3-app@beta # beta
npx create-w3-app@next # canary
```
---
## Risk Summary
| Phase | Risk Level | Breaking Changes | Rollback Safe? |
|-------|-----------|-----------------|----------------|
| Phase 1: CLI deps | Low | @clack/prompts v1 API | Yes — `pnpm install` revert |
| Phase 2: Version map | Mixed | wagmi v3, privy v3, lucide v1 | Partially — versions can be reverted in map |
| Phase 3: Base template | Medium | Next.js 16 async params | Yes — template file revert |
| Phase 4: Template files | **High** | All breaking changes combined | Complex — multiple files intertwined |
| Phase 5: CI/CD | Low | None | Yes — workflow file revert |
### Recommended Execution Order
1. **Phase 1 first** — Update CLI deps, rebuild, verify CLI still works
2. **Phase 2 (safe subset)** — Update non-breaking version map entries
3. **Phase 3** — Update base template versions and scripts
4. **Phase 4** — Update template files for each breaking change, one at a time:
- a. Tailwind v4.3 (lowest risk)
- b. Next.js 16 (async params, scripts, eslint config)
- c. lucide-react v1 (icon imports in connect components)
- d. wagmi v3 (config files, provider components)
- e. privy v3 + @privy-io/wagmi v4 (config, provider, integration)
5. **Phase 5** — CI/CD updates
### After All Phases
1. Run `pnpm typecheck` — must pass
2. Run `pnpm lint` — must pass
3. Run `pnpm build` — must build successfully
4. Scaffold a test project with `pnpm start ../test-app`
5. In the test project: `pnpm install && pnpm typecheck && pnpm build` — all must pass
6. Create a changeset and release