TypeScript Monorepo Magic: Organize, Build, and Ship…
    Neura MarketNeura Market/Stable Diffusion
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityStable DiffusionStable Diffusion
    DeepSeekDeepSeekCoPilotCoPilotMidjourneyMidjourney
    View All Directories
    OverviewPromptsBlogVideosGuidesCoursesCommunityModelsLoRAsComfyUI WorkflowsTrending
    Stable DiffusionBlogTypeScript Monorepo Magic: Organize, Build, and Ship Multi-Package Apps
    Back to Blog
    TypeScript Monorepo Magic: Organize, Build, and Ship Multi-Package Apps
    programming

    TypeScript Monorepo Magic: Organize, Build, and Ship Multi-Package Apps

    M Hossein June 2, 2026
    0 views

    What is a Monorepo? A monorepo is a single Git repository that contains multiple distinct...


    How Monorepos Work — Using shrimp-monorepo as the Example


    What is a Monorepo?

    A monorepo is a single Git repository that contains multiple distinct packages or applications. The alternative is a polyrepo: one repo per app or package.

    This repo is shrimp-monorepo. It contains:

    shrimp-monorepo/
    ├── apps/
    │   ├── api/            ← Node.js backend (Elysia + gRPC)
    │   ├── client-user/    ← React frontend for end users
    │   └── client-admin/   ← React frontend for admins
    ├── packages/
    │   ├── shared-types/   ← TypeScript types used everywhere
    │   ├── ui/             ← Shared React components
    │   ├── proto/          ← Protobuf definitions + generated code
    │   ├── grpc-client/    ← gRPC client wrapper
    │   └── db-schema/      ← Drizzle ORM schema
    └── tooling/
        └── typescript/     ← Shared tsconfig files
    

    The core idea: code that is needed by multiple apps lives in packages/, and all apps can import it directly — no npm publishing required.


    Tool 1: pnpm Workspaces — The Foundation

    pnpm is the package manager. Workspaces are its monorepo feature.

    How it's declared

    pnpm-workspace.yaml:

    packages:
      - 'apps/*'
      - 'packages/*'
      - 'tooling/*'
    

    This tells pnpm: treat every directory in these globs as a package. That's it — three lines define the entire workspace.

    What this enables

    When you run pnpm install at the repo root, pnpm:

    1. Reads every package.json in every workspace directory
    2. Hoists shared external dependencies into a single node_modules at the root
    3. Creates symlinks for internal packages so they can import each other

    The workspace:* protocol

    Look at apps/api/package.json:

    "dependencies": {
      "@shrimp/db-schema": "workspace:*",
      "@shrimp/proto": "workspace:*",
      "@shrimp/shared-types": "workspace:*"
    }
    

    workspace:* means: don't fetch this from npm — link it from this workspace instead. When api imports @shrimp/db-schema, Node resolves it to packages/db-schema/src/index.ts on disk via a symlink in node_modules/.pnpm.

    This is what makes internal sharing work without publishing packages.

    Why pnpm over npm/yarn?

    pnpm uses a content-addressable store (the .pnpm-store/ directory in this repo). Every version of every package is stored once globally. Workspaces get hard links to the store, not copies. This means:

    • Faster installs
    • Significantly less disk space
    • Strict by default — packages can't accidentally import things they didn't declare

    Tool 2: Turborepo — Task Orchestration and Caching

    pnpm workspaces handle dependencies. Turbo handles tasks (build, test, lint, etc.).

    The problem Turbo solves

    If you run pnpm run build in a repo with 8 packages, you have to figure out the order yourself. proto must build before grpc-client, which must build before api. Do it wrong and you get stale or missing types.

    Also, if nothing in packages/ui changed, you shouldn't need to rebuild it.

    Turbo solves both: dependency-aware task ordering + task result caching.

    turbo.json — the pipeline

    {
      "tasks": {
        "build": {
          "dependsOn": ["^build"],
          "outputs": ["dist/**", ".output/**", "generated/**"]
        },
        "dev": {
          "cache": false,
          "persistent": true
        },
        "lint": {
          "dependsOn": ["^build"]
        },
        "typecheck": {
          "dependsOn": ["^build"]
        },
        "test:unit": {
          "dependsOn": ["^build"],
          "outputs": ["coverage/**"]
        }
      }
    }
    

    Key concepts:

    • "dependsOn": ["^build"] — the ^ prefix means build all packages that this package depends on first. So when building @shrimp/api, Turbo automatically builds @shrimp/proto, @shrimp/db-schema, and @shrimp/shared-types beforehand, in the right order.

    • "dependsOn": ["build"] (no ^) — run the same package's build task first. Used by test:e2e: build the app before running E2E tests.

    • "cache": false — never cache this task. dev is persistent/interactive, so caching makes no sense.

    • "persistent": true — this task runs forever (a dev server). Turbo knows not to treat it as something that finishes.

    • "outputs" — Turbo hashes these paths to know what "done" looks like. If inputs haven't changed and outputs still exist, Turbo skips the task entirely (cache hit).

    How the build graph flows

    proto:generate
        ↓
    @shrimp/proto (build)
        ↓
    @shrimp/grpc-client (build)
        ↓
    apps/api (build)
    apps/client-user (build)
    apps/client-admin (build)
    

    @shrimp/shared-types, @shrimp/db-schema, and @shrimp/ui also build in parallel before their consumers, since they have no inter-dependencies.

    Filtering — run tasks for specific packages

    The root package.json uses --filter for targeted dev:

    "dev:user":  "turbo run dev --filter=@shrimp/client-user",
    "dev:admin": "turbo run dev --filter=@shrimp/client-admin",
    "dev:api":   "turbo run dev --filter=@shrimp/api"
    

    --filter accepts package names, directory globs, or git-based expressions.

    Affected-only in CI

    The CI workflow uses the most powerful filter:

    run: pnpm turbo run typecheck lint test:unit build --affected
    

    --affected uses the git diff against the base branch to determine which packages changed, then runs tasks only on those packages (and their dependents). A PR that only touches packages/ui won't re-run api tests.


    Tool 3: Shared Packages — How Internal Code is Shared

    Source-level packages (no compilation step)

    Most packages in this repo point directly at TypeScript source:

    packages/shared-types/package.json:

    {
      "name": "@shrimp/shared-types",
      "main": "./src/index.ts",
      "types": "./src/index.ts",
      "exports": {
        ".": "./src/index.ts"
      }
    }
    

    There is no build script. When @shrimp/api imports @shrimp/shared-types, it imports .ts files directly. The consuming app's bundler or TypeScript compiler handles the compilation.

    This is sometimes called the "internal packages" pattern — no dist/ folder, no compile step, just source. It works because all consumers in the monorepo are TypeScript themselves.

    Packages that do need a build step

    @shrimp/proto generates TypeScript from .proto files:

    "scripts": {
      "proto:generate": "pnpm exec protoc --ts_out ./generated ...",
      "build": "pnpm run proto:generate"
    }
    

    Turbo's "dependsOn": ["^build"] ensures proto:generate runs before anything that consumes @shrimp/proto.


    Tool 4: Shared TypeScript Config — tooling/typescript

    Rather than duplicating 25 lines of compilerOptions across 8 packages, this repo centralizes TypeScript config in tooling/typescript/.

    tooling/typescript/tsconfig.base.json — strict settings shared by all packages:

    {
      "compilerOptions": {
        "strict": true,
        "noUnusedLocals": true,
        "noUncheckedIndexedAccess": true,
        "moduleResolution": "bundler",
        ...
      }
    }
    

    tooling/typescript/tsconfig.react.json — extends base, adds JSX:

    {
      "extends": "./tsconfig.base.json",
      "compilerOptions": { "jsx": "react-jsx" }
    }
    

    Each package inherits via extends:

    apps/api/tsconfig.json:

    {
      "extends": "../../tooling/typescript/tsconfig.base.json",
      "compilerOptions": {
        "outDir": "./dist",
        "rootDir": "./src"
      }
    }
    

    packages/ui/tsconfig.json:

    {
      "extends": "../../tooling/typescript/tsconfig.react.json",
      "include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"]
    }
    

    Why this matters: change one setting in tsconfig.base.json and it propagates to every package in the repo. No drift, no "why is strict mode off in this one package" surprises.


    Tool 5: Biome — Unified Linting and Formatting

    Biome replaces ESLint + Prettier with a single fast tool. A single biome.json at the root applies to the entire repo.

    biome.json key config:

    {
      "linter": {
        "rules": {
          "correctness": {
            "noUnusedVariables": "error",
            "noUnusedImports": "error"
          },
          "style": { "useConst": "error" }
        }
      },
      "formatter": {
        "indentStyle": "tab",
        "indentWidth": 2,
        "lineWidth": 100
      }
    }
    

    Each package runs biome check . as its lint script, but the rules are defined once. This is the same pattern as TypeScript config inheritance: one source of truth, many consumers.


    Tool 6: Git Hooks — Enforcing Quality at Commit Time

    The repo uses a custom git hooks directory instead of the default .git/hooks. This means hooks can be committed and versioned.

    Setup (runs on pnpm install via the prepare lifecycle):

    "prepare": "git config core.hooksPath .githooks"
    

    .githooks/pre-commit:

    #!/usr/bin/env sh
    pnpm precommit:staged
    

    scripts/biome-staged.mjs — runs Biome only on staged files:

    const staged = spawnSync("git", ["diff", "--cached", "--name-only", "-z", ...]);
    // ... filters to .ts/.tsx/.js/.json files
    spawnSync("pnpm", ["exec", "biome", "check", "--no-errors-on-unmatched", ...files]);
    

    The key insight: it doesn't lint the whole repo on every commit — only the files currently staged. This keeps commits fast while still enforcing quality.


    The Full Picture: How These Tools Interact

    Developer commits
        ↓
    git pre-commit hook (.githooks/pre-commit)
        ↓ runs biome-staged.mjs
        ↓ Biome checks only staged files
        ↓ (fails → abort commit; passes → continue)
        ↓
    pnpm workspace resolves internal deps via workspace:* symlinks
        ↓
    turbo run build
        ↓ reads turbo.json pipeline
        ↓ resolves dependency graph from package.json deps
        ↓ builds packages in order: proto → grpc-client → api/clients
        ↓ caches outputs in .turbo/
        ↓
    CI: turbo run ... --affected
        ↓ diffs against main
        ↓ runs only impacted packages
        ↓ TypeScript config inherited from tooling/typescript/
        ↓ Biome config inherited from root biome.json
    

    Summary: Why This Stack

    ProblemToolMechanism
    Share code without publishing to npmpnpm workspacesworkspace:* + symlinks
    Run tasks in dependency orderTurbo"dependsOn": ["^build"]
    Don't rebuild unchanged packagesTurbo cacheoutputs hashing
    Run tasks on only changed code in CITurbo --affectedgit diff
    Consistent TypeScript configtooling/typescriptextends inheritance
    One linter/formatter configBiome root biome.jsonsingle config, all packages
    Enforce quality before commitsGit hooks (.githooks/)staged-file Biome check

    The monorepo isn't one tool — it's these tools composing together. pnpm handles what exists, Turbo handles when things run, and the tooling layer handles how they're configured.

    Tags

    programmingtutorialarchitecturetypescript

    Comments

    More Blog

    View all
    Context bankruptcy: The case for strategic forgetting for AI Agentsai

    Context bankruptcy: The case for strategic forgetting for AI Agents

    Most of us have seen a coding agent fail to complete a task we know it can do. We just don't...

    J
    James O'Reilly
    Parallel Compliance Engine: Drive-to-Sheets Multi-Agent Orchestrationgooglecloud

    Parallel Compliance Engine: Drive-to-Sheets Multi-Agent Orchestration

    When building Generative AI applications, developers often encounter a massive bottleneck: sequential...

    A
    Aryan Irani
    Is It Ethical to Post and Ask About Circuits on Dev.to?discuss

    Is It Ethical to Post and Ask About Circuits on Dev.to?

    I’ve been thinking about sharing some electronic circuit posts on Dev.to — small circuits, DIY...

    C
    codebunny20
    The One-Click Exporter: AI Studio Antigravity, Probed to Its Limitsagents

    The One-Click Exporter: AI Studio Antigravity, Probed to Its Limits

    What nobody tells you about exporting your multi-agent prototype to a local workspace. Every...

    L
    leslysandra
    Guarding the till while autonomous data agents do the diggingagenticarchitect

    Guarding the till while autonomous data agents do the digging

    Autonomous agents are genuinely good at answering messy business questions. Give one an LLM and a set...

    S
    Sireesha Pulipati
    Return on Attention: Why AI Code Reviews Are Wearing Us Outai

    Return on Attention: Why AI Code Reviews Are Wearing Us Out

    PR volume went up, ticket quality didn't, and the gap got filled with LLMs on both sides of the review: bots reviewing, bots replying, bots occasionally arguing with bots about priorities that only existed in a teammate's head. Our CEO named the actual problem, and it's bigger than code review.

    C
    christine

    Stay up to date

    Get the latest Stable Diffusion prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for Stable Diffusion and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Guides

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this Stable Diffusion resource

    • Get Details of a GitLab Repositoryn8n · $12.99 · Related topic
    • Automated Backup of Workflows to Gitea Repositoryn8n · $12.85 · Related topic
    • Automated Light Control on GitHub Repository Updatesn8n · $8.11 · Related topic
    • Get Community Profile of a GitHub Repositoryn8n · $6.99 · Related topic
    Browse all workflows