Home / migration / MONOREPO-MIGRATION-PLAN

Migration Plan: From Git Submodules to Monorepo

๐ŸŽฏ Goal

Transform the LARC project from a git submodules architecture to a modern monorepo that's easier to develop and maintain.

๐Ÿ“Š Current State Analysis

Current Architecture (Submodules - PAINFUL ๐Ÿ˜ซ)

larc/ (meta repo)
โ”œโ”€โ”€ core/ (submodule โ†’ @larcjs/core)
โ”œโ”€โ”€ ui/ (submodule โ†’ @larcjs/ui)
โ”œโ”€โ”€ apps/ (submodule)
โ”‚   โ”œโ”€โ”€ components/ (nested submodule)
โ”‚   โ””โ”€โ”€ core/ (nested submodule)
โ”œโ”€โ”€ site/ (submodule)
โ”œโ”€โ”€ examples/ (submodule)
โ”œโ”€โ”€ devtools/ (submodule)
โ”œโ”€โ”€ cli/ (submodule?)
โ”œโ”€โ”€ playground/ (submodule?)
โ”œโ”€โ”€ react-adapter/ (submodule?)
โ”œโ”€โ”€ components-types/ (submodule)
โ””โ”€โ”€ core-types/ (submodule)
Problems:
  • 8+ separate git repos to manage
  • Detached HEAD confusion
  • Complex push/pull workflows
  • Nested submodules (apps has its own submodules!)
  • Hard for new contributors
  • Difficult to make atomic changes across packages

๐ŸŽจ Proposed Architecture (Monorepo - EASY ๐Ÿ˜Š)

Option A: npm/yarn/pnpm Workspaces (RECOMMENDED)

larc/ (single git repo)
โ”œโ”€โ”€ package.json (workspace root)
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ core/              (@larcjs/core)
โ”‚   โ”œโ”€โ”€ components/        (@larcjs/ui)
โ”‚   โ”œโ”€โ”€ components-types/  (@larcjs/ui-types)
โ”‚   โ”œโ”€โ”€ core-types/        (@larcjs/core-types)
โ”‚   โ”œโ”€โ”€ react-adapter/     (@larcjs/react-adapter)
โ”‚   โ”œโ”€โ”€ cli/               (@larcjs/cli)
โ”‚   โ””โ”€โ”€ devtools/          (@larcjs/devtools)
โ”œโ”€โ”€ apps/
โ”‚   โ”œโ”€โ”€ contact-manager/
โ”‚   โ”œโ”€โ”€ invoice/
โ”‚   โ”œโ”€โ”€ markdown-notes/
โ”‚   โ””โ”€โ”€ data-browser/
โ”œโ”€โ”€ examples/
โ”œโ”€โ”€ site/ (documentation site)
โ””โ”€โ”€ playground/
Workflow:
# One clone
git clone https://github.com/larcjs/larc.git

# One install (links all packages automatically)
npm install

# Work across packages easily
cd packages/core
# edit core...
cd ../components
# edit components that depend on core...
# Both changes tested together!

# One commit for atomic changes
git add packages/core packages/ui
git commit -m "Add feature across core and components"
git push

๐Ÿš€ Migration Steps

Phase 1: Assessment (1 hour)

  • [ ] Identify which "submodules" are actually published packages vs. just code organization
  • [ ] List cross-package dependencies
  • [ ] Document current release process

Phase 2: Preparation (2 hours)

  • [ ] Create new branch feature/monorepo-migration
  • [ ] Backup current state
  • [ ] Create root package.json with workspaces config
  • [ ] Set up build tooling (turborepo, nx, or just npm workspaces)

Phase 3: Migration (4-6 hours)

  • [ ] Copy submodule code into packages/ directory
  • [ ] Remove .git directories from former submodules
  • [ ] Update package.json dependencies to use workspace protocol
  • [ ] Update import paths if needed
  • [ ] Test that everything builds and runs
  • [ ] Update CI/CD pipelines

Phase 4: Cleanup (1 hour)

  • [ ] Archive old submodule repos (keep for history)
  • [ ] Update documentation
  • [ ] Update contributor guide
  • [ ] Test deployment

๐Ÿ“ Detailed Implementation

1. Root package.json

{
  "name": "larc-monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "scripts": {
    "build": "npm run build --workspaces",
    "test": "npm run test --workspaces",
    "dev": "npm run dev --workspaces --if-present",
    "clean": "npm run clean --workspaces --if-present"
  },
  "devDependencies": {
    "turbo": "^2.0.0",  // Optional: faster builds
    "changesets": "^2.0.0"  // Optional: version management
  }
}

2. Update Package Dependencies

Before (submodules):
// packages/ui/package.json
{
  "peerDependencies": {
    "@larcjs/core": "^1.1.0"
  }
}
After (monorepo):
// packages/ui/package.json
{
  "dependencies": {
    "@larcjs/core": "workspace:*"
  },
  "peerDependencies": {
    "@larcjs/core": "^1.1.0"  // Still needed for external consumers
  }
}

3. Migration Script

Create migrate-to-monorepo.sh:

#!/bin/bash
set -e

echo "๐Ÿš€ Starting migration to monorepo..."

# Create packages directory
mkdir -p packages

# Move submodules to packages (preserving git history)
PACKAGES="core ui components-types core-types react-adapter cli devtools"

for pkg in $PACKAGES; do
  echo "๐Ÿ“ฆ Migrating $pkg..."

  # Get the actual directory name (ui โ†’ components)
  if [ "$pkg" = "ui" ]; then
    SOURCE="ui"
    DEST="packages/ui"
  else
    SOURCE="$pkg"
    DEST="packages/$pkg"
  fi

  # Move with history preservation
  if [ -d "$SOURCE" ]; then
    # Remove submodule connection
    git submodule deinit -f "$SOURCE" 2>/dev/null || true
    rm -rf ".git/modules/$SOURCE" 2>/dev/null || true

    # Move the directory
    mv "$SOURCE" "$DEST"

    # Remove .git reference if it exists
    rm -f "$DEST/.git"
  fi
done

# Move apps, examples, site to root (not in packages)
echo "๐Ÿ“ Organizing other directories..."
# These are already in place

# Remove submodule config
rm -f .gitmodules

echo "โœ… Physical migration complete!"
echo "๐Ÿ“ Next steps:"
echo "  1. Update package.json files with workspace: protocol"
echo "  2. Run npm install"
echo "  3. Test builds"
echo "  4. Update documentation"

๐Ÿ”„ Alternative: Keep Some Repos Separate

If you want to keep @larcjs/core and @larcjs/ui in separate repos (for independent versioning), use a hybrid approach:

Hybrid Option: Monorepo + External Packages

larc/ (monorepo)
โ”œโ”€โ”€ packages/
โ”‚   โ”œโ”€โ”€ cli/
โ”‚   โ”œโ”€โ”€ react-adapter/
โ”‚   โ””โ”€โ”€ devtools/
โ”œโ”€โ”€ apps/        (links to @larcjs/core, @larcjs/ui via npm)
โ”œโ”€โ”€ examples/    (links via npm)
โ””โ”€โ”€ site/        (links via npm)

# Separate repos:
github.com/larcjs/core        โ†’ published as @larcjs/core
github.com/larcjs/components  โ†’ published as @larcjs/ui
Development workflow:
# For core development
npm link @larcjs/core
npm link @larcjs/ui

# Or use npm install git+https://...

This is simpler but loses the "atomic commits" benefit.


๐Ÿ“ฆ Tooling Options

Option 1: Plain npm Workspaces (Simple)

Pros: Built-in, no extra tools Cons: Slower builds, basic features
// package.json
{
  "workspaces": ["packages/*"]
}

Option 2: pnpm Workspaces (Better)

Pros: Faster, better disk usage, workspace protocol Cons: Need to install pnpm
# pnpm-workspace.yaml
packages:
  - 'packages/*'

Option 3: Turborepo (Best for scaling)

Pros: Caching, parallel builds, remote caching Cons: Extra complexity
// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    }
  }
}

Option 4: Nx (Most features)

Pros: Everything + graph viz, generators Cons: Steeper learning curve

๐ŸŽฏ Recommended Path for LARC

Recommendation: pnpm Workspaces + Changesets

Why:
  • Fast, reliable package manager (pnpm)
  • Excellent monorepo support
  • Simple workspace protocol
  • Changesets for version/changelog management
  • Not too complex, not too simple
Steps:
# 1. Install pnpm
npm install -g pnpm

# 2. Create pnpm-workspace.yaml
cat > pnpm-workspace.yaml << EOF
packages:
  - 'packages/*'
EOF

# 3. Create root package.json
cat > package.json << EOF
{
  "name": "larc",
  "private": true,
  "scripts": {
    "build": "pnpm -r build",
    "test": "pnpm -r test",
    "dev": "pnpm -r --parallel dev"
  },
  "devDependencies": {
    "@changesets/cli": "^2.27.0"
  }
}
EOF

# 4. Migrate packages
./migrate-to-monorepo.sh

# 5. Update package.json dependencies
# Change peerDependencies to: "@larcjs/core": "workspace:*"

# 6. Install
pnpm install

# 7. Test
pnpm build
pnpm test

๐Ÿ“Š Comparison: Before vs After

| Aspect | Submodules | Monorepo | |--------|-----------|----------| | Clone | git clone --recurse-submodules | git clone | | Update | git pull && git submodule update --init --recursive --remote | git pull | | Work on package | cd pkg && git checkout main && git pull | cd packages/pkg | | Cross-package change | 3+ commits, 3+ pushes | 1 commit, 1 push | | Dependencies | Manual, error-prone | Automatic via workspaces | | Build | Manual coordination | Orchestrated | | New contributor | 30min + docs | 5min | | CI/CD | Complex, multiple triggers | Simple, single trigger |


๐Ÿšจ Migration Risks & Mitigation

Risk 1: Lost Git History

Mitigation: Use git subtree or keep old repos as archives
# Preserve history when moving
git subtree add --prefix=packages/core https://github.com/larcjs/core.git main

Risk 2: Breaking External Dependents

Mitigation: Keep old repos as "release mirrors"
  • Keep github.com/larcjs/core for npm registry
  • Monorepo becomes source of truth
  • Auto-push releases to old repos

Risk 3: Large Repo Size

Mitigation: Use git partial clone for CI
git clone --filter=blob:none --depth=1

Risk 4: Team Resistance

Mitigation:
  • Pilot with small team first
  • Document benefits clearly
  • Provide scripts/aliases for common tasks

๐ŸŽฌ Quick Start: Try It Out (Non-Destructive)

Want to try before committing? Test on a copy:

# 1. Clone into new directory
git clone https://github.com/larcjs/larc.git larc-monorepo-test
cd larc-monorepo-test

# 2. Create test branch
git checkout -b test-monorepo

# 3. Run migration script (I'll create this)
./test-monorepo.sh

# 4. Try the workflow
pnpm install
pnpm build
# Make changes...
git add .
git commit -m "Test change"

# 5. If you like it, do it for real. If not, delete the directory.

๐Ÿ“š Resources


โœ… Success Criteria

Migration is successful when:

  • [ ] git clone + pnpm install + pnpm build works
  • [ ] All tests pass
  • [ ] Can edit core and see changes in components immediately
  • [ ] Single commit can update multiple packages
  • [ ] New contributors can get started in < 10 minutes
  • [ ] CI/CD is simpler and faster
  • [ ] Team is happier ๐Ÿ˜Š

๐Ÿค” Still Want Submodules?

If you must keep submodules for some reason, at least use git-subrepo instead of git submodule:

  • No detached HEAD
  • Simpler workflow
  • Better for contributors
But honestly, just use a monorepo. Your future self will thank you.


Created: 2025-12-06