Artifacts Platform
Agent Docs

Development Guide

Monorepo setup with JIT packages, pnpm workspaces, Turbo tasks, and troubleshooting

Development Tooling Guide

This guide explains the monorepo tooling setup for the Artifacts platform.

Table of Contents


Just-in-Time (JIT) Package Architecture

Critical concept: This monorepo uses Just-in-Time packages - packages are consumed directly from TypeScript source files, not pre-built artifacts.

How It Works

Packages (packages/*):

  • Export TypeScript source files directly (see package.json exports: "./src/index.ts")
  • No build step required
  • No dist/ or build/ directories
  • Next.js apps transpile package sources on-demand during their own build

Apps (apps/*):

  • chatgpt-app - Built by Vercel on deployment
  • expo-app - Built by EAS or Vercel (for web)
  • storybook-chatgpt - Development-only, runs in dev mode for ChatGPT-specific component previews

Local Development:

  • You never run turbo run build locally for packages
  • Dev servers (pnpm dev) consume source files directly
  • Type checking uses source files via TypeScript's project references
  • Only apps build, and only when deployed to Vercel

Why JIT?

  • Faster local development (no build step between package changes)
  • Simpler workflow (edit code → see changes immediately)
  • Single source of truth (TypeScript source files)
  • Next.js handles all transpilation efficiently

What this means for you:

  • ❌ Don't run turbo run build for packages
  • ❌ Don't expect dist/ directories in packages
  • ✅ Run pnpm dev and start coding
  • ✅ Type check with turbo run check-types
  • ✅ Let Vercel handle production builds

Package Manager: pnpm + Workspaces

Workspace Structure

The repository uses pnpm workspaces with catalog for shared dependencies:

# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'
  - '!examples/**'

catalog:
  react: ^19.1.1
  next: ^15.5.5
  zod: ^3.25.76
  # ... other shared versions

Why pnpm?

  • Efficient disk usage via symlinks (shared node_modules)
  • Strict dependency resolution (no phantom dependencies)
  • Workspace protocol for internal packages
  • Catalog for consistent versioning across packages

Workspace Protocol

Internal dependencies use workspace:* protocol:

{
  "dependencies": {
    "@chatgpt-artifacts/ui": "workspace:*",
    "@chatgpt-artifacts/artifact-kit": "workspace:*"
  }
}

What this means:

  • workspace:* = "use latest version from workspace"
  • Resolved to file: protocol during pnpm install
  • Changes in one package immediately visible to dependents (via symlinks)

Catalog Dependencies

Shared dependencies use catalog: protocol:

{
  "dependencies": {
    "react": "catalog:",
    "next": "catalog:",
    "zod": "catalog:"
  }
}

What this means:

  • catalog: = "use version from pnpm-workspace.yaml catalog"
  • Ensures same version of React, Tailwind, Zod, etc. across all packages
  • Update one place (catalog) to update everywhere

Common pnpm Commands

Installation

# Install all workspace dependencies
pnpm install

# Install in specific package
cd apps/chatgpt-app
pnpm install

When to use:

  • After git pull if pnpm-lock.yaml changed
  • After adding/removing dependencies
  • Fresh checkout

Adding Dependencies

# Add to specific package (from root)
pnpm --filter chatgpt-app add lodash

# Add dev dependency
pnpm --filter artifact-kit add -D @types/node

# Add workspace dependency
pnpm --filter expo-app add @chatgpt-artifacts/ui

# Add catalog dependency (manually edit package.json)
# In packages/foo/package.json:
{
  "dependencies": {
    "react": "catalog:"  // Add this line
  }
}
# Then run: pnpm install

Pattern:

  • Use --filter <package-name> to target specific package
  • Package name = name field in package.json (not directory name)
  • Example: chatgpt-app (not apps/chatgpt-app)

Filtering Packages

Both turbo and pnpm support filtering to target specific packages:

# Single package
turbo run check-types --filter chatgpt-app

# Multiple packages
turbo run lint --filter chatgpt-app --filter expo-app

# All apps
turbo run check-types --filter "./apps/*"

# Package + dependencies
pnpm --filter chatgpt-app... install

# Package + dependents
pnpm --filter ...chatgpt-app install

When to use turbo vs pnpm:

  • turbo run - For tasks in turbo.json (check-types, lint)
  • pnpm --filter - For dev servers and dependency management

Task Orchestration: Turborepo

What Turbo Does

Turbo orchestrates tasks across the monorepo (type checking, linting) with smart caching and dependency ordering.

Important: The build task exists for Vercel deployments only. Locally, you run check-types and lint.

{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "lint": {
      "dependsOn": ["^lint"]
    },
    "check-types": {
      "dependsOn": ["^check-types"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Task Dependencies: ^lint and ^check-types

The ^ prefix means "run this task in dependencies first":

"check-types": {
  "dependsOn": ["^check-types"]
}

Example:

  • You run: turbo run check-types --filter chatgpt-app
  • Turbo sees chatgpt-app depends on artifact-kit, ui, artifact-service
  • Turbo type checks dependencies first
  • Then type checks chatgpt-app

Why?

  • Dependencies must be type-safe before dependents can type check correctly
  • Turbo ensures correct execution order automatically

Caching Behavior

Turbo caches task outputs to avoid redundant work:

# First type check: runs tsc in all packages
turbo run check-types

# Second type check (no changes): instant (uses cache)
turbo run check-types

Cache key includes:

  • File contents
  • TypeScript configuration
  • Task outputs from dependencies

When cache helps:

  • Repeated type checks without code changes
  • CI/CD pipelines

Note: Since packages use JIT, there's no build cache to worry about. Turbo only caches type check and lint results.


Common Development Tasks

Running Development Servers

# All apps in parallel (uses Turbo)
pnpm dev

# Specific app only
cd apps/chatgpt-app
pnpm dev

# Or from root:
pnpm --filter chatgpt-app dev

Preference:

  • Use cd apps/X && pnpm dev for single app development
  • Use pnpm dev from root when working across multiple packages

Type Checking & Linting

# Type check all packages
turbo run check-types

# Lint all packages
turbo run lint

# Both (recommended before committing)
turbo run check-types && turbo run lint

# Lint with auto-fix
pnpm --filter chatgpt-app run lint --fix

Use filtering (see "Filtering Packages" section above) to target specific packages.

Deployment (Vercel Only)

Never run builds locally. Vercel handles builds automatically:

  1. Push to git → Vercel detects changes
  2. Vercel runs turbo run build --filter <app>
  3. Next.js transpiles JIT packages during build
  4. Deploys to production

If deployment fails: Check Vercel logs and run turbo run check-types && turbo run lint locally to catch issues before pushing.

Adding a New Package

  1. Create directory structure:
mkdir -p packages/new-package/src
cd packages/new-package
  1. Initialize package.json:
{
  "name": "@chatgpt-artifacts/new-package",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "exports": {
    ".": "./src/index.ts"
  },
  "scripts": {
    "lint": "npx eslint .",
    "check-types": "tsc --noEmit"
  },
  "dependencies": {
    "zod": "catalog:"
  },
  "devDependencies": {
    "@chatgpt-artifacts/typescript-config": "workspace:*",
    "@chatgpt-artifacts/eslint-config": "workspace:*"
  }
}
  1. Add TypeScript config (tsconfig.json):
{
  "extends": "@chatgpt-artifacts/typescript-config/base.json",
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
  1. Install dependencies:
pnpm install
  1. Use in other packages:
pnpm --filter chatgpt-app add @chatgpt-artifacts/new-package

Adding a New App

Same as adding a package, but:

  • Use apps/ directory instead of packages/
  • Include framework-specific setup (Next.js, Vite, etc.)
  • Add dev script for development server

Infrastructure Scripts

reset-modules.sh

Deletes all node_modules directories, removes pnpm-lock.yaml, and reinstalls dependencies from scratch.

When to use:

  • Phantom dependency issues (importing packages not in package.json)
  • Corrupted node_modules after failed install
  • Switching between Node versions
  • Weird TypeScript errors that don't make sense

Usage:

pnpm run reset:modules

Warning: Slow operation (re-downloads all dependencies).


Troubleshooting

"Cannot find module '@chatgpt-artifacts/ui'"

Cause: Workspace dependency not installed.

Solution:

# Ensure all dependencies installed
pnpm install

Note: No build needed - packages use JIT and consume source files directly.

Type errors in IDE won't go away

Cause: TypeScript language server cache issue.

Solution:

# Verify types are actually correct:
turbo run check-types

# If check-types passes but IDE still shows errors, inform the user
# that they may need to restart their TypeScript language server

"The inferred type cannot be named without a reference..."

Cause: TypeScript can't resolve types from workspace dependency.

Solution:

# Verify the dependency is installed
pnpm install

# Type check to see actual errors
turbo run check-types --filter chatgpt-app

Note: This is usually a TypeScript configuration issue, not a build issue.

pnpm install fails with ENOENT

Cause: Corrupted lockfile or node_modules.

Solution:

pnpm run reset:modules

Dev server shows old code after package changes

Cause: Next.js dev server hasn't detected file changes.

Solution:

# Clear Next.js cache and ask user to restart dev server:
rm -rf apps/chatgpt-app/.next

# Then inform user to restart their dev server

Note: You cannot restart background processes. Inform the user that they need to restart the dev server after clearing the cache.


Quick Reference

Common Commands

TaskCommand
Install dependenciespnpm install
Run dev (all apps)pnpm dev
Run dev (one app)pnpm --filter <app> dev
Type check & lintturbo run check-types && turbo run lint
Add dependencypnpm --filter <pkg> add <dep>
Clear cacherm -rf apps/*/.next
Reset workspacepnpm run reset:modules

Common Scenarios

ScenarioWhat to Do
Before committingturbo run check-types && turbo run lint
Type errors persistRun turbo run check-types, then inform user if passes
Dev server staleClear cache, inform user to restart server
Dependency issuespnpm run reset:modules
Deployment failedCheck Vercel logs, run checks locally

Key Principles

  1. Packages use JIT - No build step needed, consume TypeScript source directly
  2. Use workspace protocol (workspace:*) for internal dependencies
  3. Use catalog protocol (catalog:) for shared external dependencies
  4. Filter by package name, not directory path
  5. Type check before committing - Run turbo run check-types && turbo run lint
  6. Don't build locally - Let Vercel handle builds on deployment
  7. Clear Next.js cache when dev server shows stale code
  8. Reset modules as last resort for dependency issues
  9. Inform user when they need to take manual actions (restart servers, etc.)

On this page

Development Guide