Busflow Docs

Internal documentation portal

Skip to content

Build Orchestration & Monorepo Optimization ​

For workspace topology and app/package map see frontend.md. For the full package dependency graph see app-structure.md.

Workspace Configuration ​

The monorepo uses pnpm workspaces for dependency resolution and Turborepo for task orchestration.

yaml
# pnpm-workspace.yaml
packages:
  - apps/*
  - studio/*
  - "!studio/presentations"
  - packages/*
  - packages/services/*

onlyBuiltDependencies:
  - vue-demi

All root-level scripts delegate to Turborepo:

json
{
  "build": "turbo run build",
  "lint": "turbo run lint",
  "test": "turbo run test",
  "dev": "turbo run dev"
}

Convenience shortcuts for individual apps (e.g. dev:workspace, dev:landing) use pnpm --filter directly for targeted local development.


Turborepo Pipeline ​

Task definitions live in turbo.json. Each task declares its dependency chain, cacheable inputs, and outputs.

Task Graph ​

text
build ──dependsOn──► ^build  (build dependencies first)
dev   ──dependsOn──► ^build  (build deps before starting dev servers)
lint  ──dependsOn──► ^build
test  ──dependsOn──► ^build

Build Task ​

The build task is the most heavily optimized:

  • Inputs: src/**, app/**, server/**, pages/**, components/**, composables/**, layouts/**, plugins/**, public/**, config files (*.config.*), and source files (*.ts, *.js, *.vue, *.css).
  • Excluded Inputs: Test files (*.spec.*, *.test.*) and Markdown (*.md) β€” changes to these do not invalidate the build cache.
  • Outputs: dist/**, .output/**, .nuxt/** β€” cached and restored on cache hits.

Dev Task ​

  • cache: false β€” dev servers are never cached.
  • persistent: true β€” Turborepo keeps the process alive (long-running dev servers).

Test Task ​

  • Outputs: coverage/** β€” the system caches coverage reports between runs.

Caching Strategy ​

Local Cache ​

Turborepo caches task outputs in node_modules/.cache/turbo by default. On a cache hit, the system restores outputs from disk instead of re-executing the task. This provides instant feedback for unchanged packages.

Remote Cache (Planned) ​

For shared CI/GitHub Actions caching:

  • Option A: Vercel Remote Cache (zero config, turbo login + turbo link).
  • Option B: Self-hosted via Turborepo's open-source cache server (aligns with self-hosted infrastructure strategy in infrastructure.md).

Turborepo composes cache keys from: file hashes of declared inputs, environment variable values, and the task's dependsOn chain.


Affected-Package Filtering ​

For PR pipelines, only changed packages and their dependents should be built/tested:

bash
# Build only packages affected by changes since the merge base
turbo run build --filter=...[origin/main]

# Test a specific package and everything that depends on it
turbo run test --filter=@busflow/types...

The CI pipeline integrates this (see ci-cd.md Β§5).


Docker Build Strategy ​

Each deployable app uses Turborepo prune to generate a minimal, isolated workspace for Docker builds. This replaces naive COPY . . approaches and dramatically reduces image sizes and layer cache invalidation.

dockerfile
# Stage 1: Prune the monorepo to only the target app's dependency tree
FROM node:22-alpine AS pruner
WORKDIR /app
COPY . .
RUN npx turbo prune @busflow/workspace --docker

# Stage 2: Install dependencies (cached unless lockfile changes)
FROM node:22-alpine AS installer
WORKDIR /app
COPY --from=pruner /app/out/json/ .
RUN pnpm install --frozen-lockfile

# Stage 3: Build (cached unless source changes)
COPY --from=pruner /app/out/full/ .
RUN pnpm turbo run build --filter=@busflow/workspace

# Stage 4: Production image
FROM nginx:alpine
COPY --from=installer /app/apps/workspace/dist /usr/share/nginx/html

This expands the build optimization strategy referenced in infrastructure.md.


Dependency Management ​

pnpm Conventions ​

  • Workspace protocol: Internal packages use workspace:* for local resolution (e.g. "@busflow/types": "workspace:*").
  • Shared devDependencies: Tooling installed at the root (turbo, shared configs). App-specific tools stay in the app's own package.json.
  • Overrides: Global dependency overrides for security patches live in the root package.json under pnpm.overrides (e.g. "node-forge": "^1.4.0").
  • onlyBuiltDependencies: Packages requiring native builds (e.g. vue-demi) are explicitly allowlisted in pnpm-workspace.yaml.

Adding a New Package ​

  1. Create the package directory under the appropriate workspace glob (packages/, apps/, studio/).
  2. Initialize with pnpm init and set the name field to @busflow/<package-name>.
  3. Add to consumers via pnpm add @busflow/<package-name> --workspace --filter @busflow/<consumer>.
  4. If the package produces build output, add the build script so Turborepo includes it in the task graph.

Internal documentation β€” Busflow