# How the Celeris Web Monorepo Architecture Works with pnpm Workspaces

> Discover how the Celeris Web monorepo architecture leverages pnpm workspaces. Learn about package linking, version centralization, and build orchestration from a single lock file.

- Repository: [Kirk Lin/celeris-web](https://github.com/kirklin/celeris-web)
- Tags: architecture
- Published: 2026-03-05

---

**Celeris Web uses a single-repo monorepo powered by pnpm workspaces to link dozens of interdependent packages via `workspace:*` specifiers, centralize third-party versions through the `catalog:` protocol, and orchestrate builds across apps and services with a single [`pnpm-lock.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-lock.yaml) file.**

The Celeris Web project (available at `kirklin/celeris-web`) organizes its frontend components, utilities, and applications into a scalable monorepo structure. By leveraging pnpm workspaces, the architecture eliminates dependency duplication while enabling atomic updates across shared libraries and consumer applications. This setup allows developers to run the admin UI, mock API, and shared packages simultaneously using a unified command interface.

## Workspace Layout and Configuration

The foundation of the Celeris Web monorepo rests in the root **[`pnpm-workspace.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-workspace.yaml)** file, which defines glob patterns for all workspace packages. This configuration tells pnpm which directories to treat as independent packages while maintaining them within a single dependency graph.

```yaml

# pnpm-workspace.yaml

packages:
  - packages/shared/*
  - packages/web/*
  - packages/ai/**
  - apps/*
  - services/*
  - scripts

```

The workspace is divided into logical groups:

- **`packages/shared/*`** – Common tooling, Vite plugins, and shared TypeScript configurations
- **`packages/web/*`** – UI components, composables, directives, locale files, and request utilities  
- **`packages/ai/**`** – Future-proof placeholder for AI-related packages
- **`apps/*`** – Full-stack applications such as the admin dashboard
- **`services/*`** – Backend-like services including the mock admin API
- **`scripts`** – Utility scripts such as the dependency tree generator

The root **[`package.json`](https://github.com/kirklin/celeris-web/blob/main/package.json)** marks the repository as private to prevent accidental publishing and pins the pnpm version for consistency:

```json
{
  "private": true,
  "packageManager": "pnpm@10.10.0"
}

```

## Dependency Management with workspace:* and Catalogs

Internal dependencies between workspace packages use the **`workspace:*`** version specifier. This protocol instructs pnpm to resolve the package from the local filesystem rather than the npm registry, ensuring that changes in shared code propagate immediately to dependent applications.

In **[`packages/web/components/package.json`](https://github.com/kirklin/celeris-web/blob/main/packages/web/components/package.json)** (and similar packages), internal references follow this pattern:

```json
"dependencies": {
  "@celeris/ca-components": "workspace:*",
  "@celeris/constants": "workspace:*",
  "@celeris/styles": "workspace:*",
  "@celeris/utils": "workspace:*"
}

```

For third-party dependencies, the architecture employs the **`catalog:`** protocol. Rather than scattering version numbers across individual [`package.json`](https://github.com/kirklin/celeris-web/blob/main/package.json) files, versions are centralized in the `catalog:` section of [`pnpm-workspace.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-workspace.yaml). This ensures that every package uses identical versions of Vue, Vite, Axios, and other shared libraries:

```json
"devDependencies": {
  "vue": "catalog:",
  "vite": "catalog:"
}

```

pnpm creates **hard-links** from each package's `node_modules` to a central content-addressable store, eliminating duplicate installations of common dependencies across the monorepo.

## Monorepo Scripts and Orchestration

The root [`package.json`](https://github.com/kirklin/celeris-web/blob/main/package.json) provides convenience scripts utilizing pnpm's **`--filter`** flag to target specific workspaces or run commands across multiple packages. These scripts abstract the complexity of working with multiple interdependent codebases.

| Script | Function | Implementation Detail |
|--------|----------|---------------------|
| `pnpm bootstrap` | Installs all dependencies across the monorepo | Equivalent to `pnpm install` at root |
| `pnpm dev` | Launches both admin UI and mock API concurrently | Uses `run-p` to execute filtered commands in parallel |
| `pnpm dev:admin` | Starts the admin UI development server | `pnpm --filter @celeris/admin dev` targeting `apps/admin` |
| `pnpm dev:mock` | Starts the mock API service | `pnpm --filter @celeris/admin-api dev` targeting `services/admin-api` |
| `pnpm build` | Builds the admin UI for production | `pnpm --filter @celeris/admin build` |
| `pnpm clean` | Removes all `node_modules` and `dist` directories | Ensures fresh install across entire workspace |

When packages declare **peerDependencies** (such as Vue in `@celeris/components`), pnpm hoists compatible versions to the root `node_modules` directory, reducing disk usage and ensuring consistent runtime behavior across applications.

## Development Workflow

### Installing the Entire Monorepo

From the repository root, run:

```bash
pnpm bootstrap

# or simply:

pnpm i

```

pnpm reads the workspace configuration, resolves all `workspace:*` links to local paths, installs catalog-defined versions of third-party libraries, and generates a single [`pnpm-lock.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-lock.yaml) file at the root.

### Running Development Servers

To start both the frontend application and its corresponding mock API simultaneously:

```bash
pnpm dev

```

This command executes `pnpm --filter @celeris/admin dev` (launching the Vite dev server for the admin UI) alongside `pnpm --filter @celeris/admin-api dev` (starting the mock service). Both processes share the same source tree, enabling hot-reload when modifying shared packages like `@celeris/utils` or `@celeris/styles`.

### Importing Workspace Packages

Any package within the monorepo can import from siblings using standard module resolution:

```typescript
// Inside apps/admin or packages/web/components
import { formatDate } from '@celeris/utils'
import { Button } from '@celeris/components'

```

TypeScript resolves these imports through the monorepo's unified `node_modules` structure without requiring additional path mapping configuration, as the `workspace:*` protocol ensures proper linking during installation.

### Adding a New Package

To extend the monorepo with new functionality:

1. Create a directory matching one of the glob patterns in [`pnpm-workspace.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-workspace.yaml) (e.g., `packages/web/new-feature`)
2. Add a [`package.json`](https://github.com/kirklin/celeris-web/blob/main/package.json) with `"name": "@celeris/new-feature"` and declare internal dependencies using `"workspace:*"`
3. Run `pnpm i` to automatically register the new workspace and update the lockfile

## Benefits of the pnpm Workspace Architecture

This architecture delivers several technical advantages for large-scale frontend development:

- **Zero-install duplication**: Hard-linked content stores reduce disk usage by storing identical dependency versions once regardless of how many packages reference them.
- **Consistent versioning**: The `catalog:` section centralizes third-party version management, while `workspace:*` guarantees internal packages remain synchronized without manual version bumping.
- **Deterministic CI/CD**: A single lockfile ensures reproducible builds across development and production environments.
- **Scalable code sharing**: Shared utilities (`@celeris/utils`), UI component libraries (`@celeris/components`), and build tooling (`@celeris/vite`) can be consumed by any application without publishing to npm or managing complex relative imports.
- **Atomic upgrades**: Updating a dependency version in the catalog or modifying a shared utility immediately propagates to all dependents after a single install command.

## Summary

- The **Celeris Web monorepo** is defined by [`pnpm-workspace.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-workspace.yaml), which groups packages into `apps/`, `packages/`, `services/`, and `scripts/` directories.
- **Internal dependencies** use the `workspace:*` protocol to link local packages, while the **`catalog:`** protocol centralizes third-party version management.
- Root scripts utilize `pnpm --filter` to run commands against specific workspaces, enabling commands like `pnpm dev` to orchestrate multiple services simultaneously.
- **pnpm's content-addressable store** eliminates duplicate dependencies through hard-linking, significantly reducing disk usage compared to traditional node_modules structures.
- The architecture supports hot-reloading across package boundaries, allowing changes in shared libraries to immediately reflect in consuming applications without rebuilds or republishing.

## Frequently Asked Questions

### What is the purpose of the `workspace:*` prefix in package.json files?

The `workspace:*` prefix tells pnpm to resolve the dependency from the local monorepo rather than fetching it from the npm registry. In Celeris Web, this ensures that when `@celeris/components` imports from `@celeris/utils`, it always uses the current source code from the repository rather than a potentially outdated published version, enabling real-time development across package boundaries.

### How does the `catalog:` protocol manage dependency versions?

The `catalog:` protocol references versions defined in the `catalog:` section of the root [`pnpm-workspace.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-workspace.yaml) file. Instead of specifying `"vue": "^3.4.0"` in every package that requires Vue, developers write `"vue": "catalog:"`, and pnpm substitutes the version defined centrally. This guarantees that all packages use identical versions of shared frameworks and libraries, preventing version drift and conflicting peer dependencies.

### How do I add a new package to the Celeris Web monorepo?

Create a new directory under one of the glob patterns defined in [`pnpm-workspace.yaml`](https://github.com/kirklin/celeris-web/blob/main/pnpm-workspace.yaml) (such as `packages/web/new-tool/`), add a [`package.json`](https://github.com/kirklin/celeris-web/blob/main/package.json) with a scoped name like `"@celeris/new-tool"`, and declare any internal dependencies using `"workspace:*"`. Running `pnpm i` from the root automatically detects the new workspace, links it into the monorepo's dependency graph, and makes it available for filtering via `pnpm --filter @celeris/new-tool`.

### What is the difference between `apps/*` and `packages/*` in this architecture?

The `apps/*` directory (such as `apps/admin`) contains deployable applications with entry points, build configurations, and environment-specific code. The `packages/*` directory contains library code—reusable components, utilities, and tooling—that multiple applications import. While both are valid workspaces, applications typically depend on many packages, whereas packages should remain agnostic of specific application implementations to maintain proper separation of concerns.