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:
{
"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:
{
"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:
{
"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:
{
"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:
"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:
"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/srcfrom 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 installor 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:
- Root package.json lists shared deps
- Workspace package.json also lists them (ensures version compatibility)
- npm installs once at root, symlinks to workspaces
- 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:
# 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 workspace2.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:
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 independently5.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 installcompletes without errors - [ ]
npm run devstarts Vite dev server - [ ]
npm run testruns web app tests - [ ]
npm run buildcreates production build - [ ]
npm run validatepasses all checks - [ ]
cd apps/web && npm run devworks independently - [ ]
npm run storybookstill serves component library - [ ]
npm run admin:devstill 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