Skip to content

Monorepo Refactoring Plan: Create apps/web/package.json

Status:COMPLETED (2026-02-08) Worklog: docs/worklog/2026-02-08_monorepo-package-json-refactoring_complete.md


Context

The Lantern app monorepo has recently been reorganized with a proper workspace structure (apps/, services/, packages/). However, the main web application (apps/web/) doesn't have its own package.json, causing all web-specific dependencies, scripts, and configurations to live at the root level. This creates several issues:

1.Workspace inconsistency: All other workspaces (apps/admin, services/api/docs, etc.) have their own package.json, but the main app doesn't

2.Dependency confusion: Root dependencies include web-specific packages (maplibre-gl, storybook, vitest, etc.) that aren't shared

3.Script disorganization: 86 root scripts mix web-specific commands with monorepo-wide orchestration

4.Build complexity: Web app can't be built independently from the monorepo root

Goal: Create a proper apps/web/package.json to establish workspace independence while maintaining backward compatibility through root delegation scripts.


Implementation Strategy

Phase 1: Create apps/web/package.json

Create a new package.json for the web app with:

Name & metadata:

json

{

  "name": "lantern-web",

  "version": "0.1.0",

  "private": true,

  "type": "module"

}

Production dependencies (move from root):

-react (^19.2.4)

-react-dom (^19.2.4)

-firebase (^12.9.0)

-geofire-common (^6.0.0)

-lucide-react (^0.563.0)

-maplibre-gl (^5.17.0)

-prop-types (^15.8.1) - Used for PropTypes in components

DevDependencies (move from root):

Build tools:

-vite (latest)

-@vitejs/plugin-react (^5.1.3)

-vite-plugin-pwa (^1.2.0)

Styling:

-tailwindcss (^4.1.18)

-@tailwindcss/postcss (^4.1.18)

-@tailwindcss/forms (^0.5.11)

-postcss (^8.5.6)

-autoprefixer (^10.4.24)

Testing:

-vitest (^4.0.18)

-@vitest/coverage-v8 (^4.0.18)

-@vitest/browser-playwright (^4.0.18)

-@testing-library/react (^16.3.2)

-@testing-library/jest-dom (^6.9.1)

-@testing-library/user-event (^14.6.1)

-jsdom (^28.0.0)

-playwright (^1.58.1)

Linting:

-eslint (^8.57.1)

-eslint-plugin-react (^7.37.5)

-eslint-plugin-react-hooks (^7.0.1)

-eslint-plugin-react-refresh (^0.4.26)

-eslint-plugin-import (^2.32.0)

-eslint-import-resolver-node (^0.3.9)

-eslint-plugin-jsx-a11y (^6.10.2)

-eslint-plugin-unused-imports (^4.3.0)

-eslint-plugin-security (^3.0.1)

-eslint-plugin-vitest (^0.4.1)

-prettier (^3.8.1)

Utilities:

-dotenv (^17.2.4)

-diff (^8.0.3)

-css-select (^6.0.0)

-nth-check (^2.1.1)

-svgo (^4.0.0)

-postcss-svgo (^7.1.0)

Scripts to add:

json

{

  "scripts": {

    "predev": "node ../../tooling/scripts/generate-version.mjs",

    "dev": "vite",

    "prebuild": "node ../../tooling/scripts/generate-version.mjs",

    "build": "vite build",

    "preview": "vite preview",

    "test": "vitest",

    "test:coverage": "vitest run --coverage",

    "lint": "eslint src --ext .js,.jsx",

    "lint:fix": "eslint src --ext .js,.jsx --fix",

    "format": "prettier --write \"src/**/*.{js,jsx,css}\"",

    "format:check": "prettier --check \"src/**/*.{js,jsx,css}\""

  }

}

Note: Vite/vitest configs remain at apps/web/vite.config.mjs and apps/web/vitest.config.js (already there).


Phase 2: Update Root package.json

Add apps/web to workspaces array:

json

{

  "workspaces": [

    "apps/web",      // ← ADD THIS

    "apps/admin",

    "services/api/docs",

    "services/bots/discord",

    "services/functions/firebase",

    "packages/shared"

  ]

}

Remove dependencies that moved to apps/web:

  • Remove: prop-types, maplibre-gl
  • Keep shared: react, react-dom, firebase, geofire-common, lucide-react (also used by apps/admin)

Remove devDependencies that moved to apps/web:

  • Remove: All vite plugins, testing libraries, most eslint plugins, styling tools
  • Keep shared: firebase-admin, firebase-tools, dotenv (used by multiple workspaces)
  • Keep monorepo-wide: Storybook, VitePress, workflow testing

Update scripts to delegate to workspace:

json

{

  "scripts": {

    "// === Web App (Main PWA) ===": "",

    "predev": "npm run predev -w apps/web",

    "dev": "npm run dev -w apps/web",

    "prebuild": "npm run prebuild -w apps/web",

    "build": "npm run build -w apps/web",

    "build:app": "npm run build -w apps/web",

    "preview": "npm run preview -w apps/web",


    "// === Testing ===": "",

    "test": "npm run test -w apps/web",

    "test:coverage": "npm run test:coverage -w apps/web",

    "test:workflows": "vitest --config=vitest.workflows.config.js",

    "test:workflows:run": "vitest --config=vitest.workflows.config.js --run",

    "test:workflows:issue-extraction": "bash .github/workflows/test-issue-extraction.sh",

    "test:workflows:real": "RUN_REAL_API_TESTS=true vitest --config=vitest.workflows.config.js -- ai-issue-triage-real",

    "test:workflows:validate": "node tooling/scripts/validate-triage-consistency.js && bash .github/workflows/validate-triage-config.sh",


    "// === Linting & Formatting ===": "",

    "lint": "npm run lint -w apps/web",

    "lint:fix": "npm run lint:fix -w apps/web",

    "format": "npm run format -w apps/web",

    "format:check": "npm run format:check -w apps/web",

    "lint:docs": "node tooling/scripts/lint.docs.js",

    "lint:docs:strict": "node tooling/scripts/lint.docs.js --strict",

    "lint:conventions": "node tooling/scripts/lint.lint-conventions.js",

    "lint:console": "bash tooling/scripts/lint.find-console-logs.sh",

    "lint:venue-config": "node tooling/scripts/lint.venue-config.js",

    "lint:venue-config:static": "node tooling/scripts/lint.venue-config.js --no-firestore",

    "lint:workspace-cache": "node tooling/scripts/lint.workspace-cache.js",


    "// ... rest of scripts remain unchanged ..."

  }

}

Keep at root (monorepo-wide orchestration):

-validate - Calls all workspaces

-storybook* - Component library (uses web components but serves entire monorepo)

-build-storybook - Storybook static build

-docs:* - VitePress documentation

-admin:* - Admin workspace delegation

-audit, audit:fix, audit:fix:force - Security audits

  • All infrastructure scripts (cf:*, env:*, validate:headers)
  • All data management scripts (backfill:*, import:*, check:*, etc.)

Phase 3: Update Configuration Files (if needed)

IMPORTANT: Vite config already handles monorepo structure correctly:

-apps/web/vite.config.mjs sets envDir: path.resolve(__dirname, '../..')

  • This means .env files are read from root (as intended for monorepo)
  • No changes needed to vite.config.mjs for environment variables

Check and update paths in:

1.Root validate script - Ensure it still works with workspace structure:

bash

   "validate": "npm run validate:headers && npm run test:workflows:validate && npm run test:coverage && npm run format:check && npm run lint && npm run audit && npm run validate -w apps/admin && npm run validate -w services/bots/discord && npm run validate -w services/functions/firebase"

Update to:

bash

   "validate": "npm run validate:headers && npm run test:workflows:validate && npm test:coverage -w apps/web && npm run format:check -w apps/web && npm run lint -w apps/web && npm run audit && npm run validate -w apps/admin && npm run validate -w services/bots/discord && npm run validate -w services/functions/firebase"

2.ESLint config (.eslintrc.json) - Check if paths need updating:

  • Currently references are relative, should work as-is
  • Verify eslint can resolve apps/web/src from root

3.Prettier config - No changes needed (paths passed via command)

4.Storybook config - tooling/.storybook/main.js:

  • ✅ Already correct: "../../apps/web/src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
  • No changes needed

5.GitHub Actions workflows - Check CI/CD:

-.github/workflows/ - Ensure build/test commands still work

  • May need to add npm install or use workspace commands

Phase 4: Handle Shared Dependencies

Strategy: Keep at root for hoisting benefits

npm workspaces automatically hoist shared dependencies to the root node_modules/. We'll keep commonly-shared packages at root and let npm manage duplication:

Keep at root level:

-react, react-dom - Used by apps/web AND apps/admin

-firebase - Used by apps/web, apps/admin, services/functions/firebase

-lucide-react - Used by apps/web AND apps/admin

-geofire-common - Could be used by services

-firebase-admin, firebase-tools - Used by multiple workspaces

-dotenv - Used by multiple scripts

  • Monorepo tooling: Storybook, VitePress, workflow testing

How it works:

  1. Root package.json lists shared deps
  2. Workspace package.json also lists them (ensures version compatibility)
  3. npm installs once at root, symlinks to workspaces
  4. If workspace needs different version, npm installs locally in workspace

Action items:

  • Add shared dependencies to BOTH root AND apps/web/package.json
  • This ensures apps/web can be installed standalone while still getting hoisting benefits in monorepo

Phase 5: Update Documentation

Files to update:

1.CLAUDE.md - Update "Common Commands" section:

markdown

   # Development

   npm run dev                  # Vite dev server (delegates to apps/web)

   npm run dev -w apps/web      # Direct workspace command

   cd apps/web && npm run dev   # From within workspace

2.README.md (if exists) - Update installation/development instructions

3.docs/engineering/guides/ - Update any build/development guides


Critical Files to Modify

1.NEW: apps/web/package.json - Create with web-specific config

2.EDIT: /package.json (root) - Add workspace, update scripts, adjust dependencies

3.CHECK: tooling/.storybook/main.js - Verify story paths

4.CHECK: .eslintrc.json - Verify paths resolve correctly

5.CHECK: .github/workflows/*.yml - Ensure CI commands work

6.EDIT: CLAUDE.md - Update development commands

7.CHECK: vitest.workflows.config.js - Ensure test paths still work


Migration Steps (Order matters!)

1.Create apps/web/package.json with all web-specific dependencies and scripts

2.Update root package.json:

a. Add apps/web to workspaces

b. Update scripts to delegate to -w apps/web

c. Remove web-only dependencies (keep shared ones)

3.Run npm install at root to regenerate lock file and link workspaces

4.Test basic commands:

bash

   npm run dev              # Should delegate to apps/web

   npm run test             # Should delegate to apps/web

   npm run build            # Should delegate to apps/web

   cd apps/web && npmrundev  # Should work independently

5.Update Storybook config if story paths broken

6.Update GitHub Actions workflows if needed

7.Run full validation: npm run validate

8.Update documentation (CLAUDE.md, guides)


Verification Checklist

After implementation, verify:

  • [ ] npm install completes without errors
  • [ ] npm run dev starts Vite dev server
  • [ ] npm run test runs web app tests
  • [ ] npm run build creates production build
  • [ ] npm run validate passes all checks
  • [ ] cd apps/web && npm run dev works independently
  • [ ] npm run storybook still serves component library
  • [ ] npm run admin:dev still works for admin app
  • [ ] GitHub Actions CI passes
  • [ ] No duplicate dependencies installed (check node_modules sizes)
  • [ ] All workspace commands work: npm run <script> -w <workspace>

Rollback Plan

If issues arise:

1.Revert root package.json to previous version (git restore)

2.Delete apps/web/package.json

3.Run npm install to restore previous state

4.Commit revert with explanation of what failed


Benefits of This Approach

1.Workspace independence: apps/web can be developed standalone

2.Clear dependency ownership: Know what each workspace actually uses

3.Smaller lock file: Only include what's needed per workspace

4.Faster installs: Shared deps hoisted, unique deps isolated

5.Better IDE support: Workspaces can have independent TypeScript/ESLint configs

6.Scalability: Easy to add new workspaces following same pattern

7.Backward compatibility: Root scripts still work via delegation


Notes

-Storybook stays at root: It documents components from all workspaces, not just web

-VitePress stays at root: Documentation is monorepo-wide

-Workflow tests stay at root: They validate CI/CD across entire monorepo

-Tooling scripts stay at root: They're infrastructure for the entire project

-Shared dependencies duplicated: Listed in both root and workspace for standalone capability

Built with VitePress