Create detailed migration plan for transitioning from window globals to ES modules + Alpine.js architecture. The plan addresses all gaps in the previous setup document and provides incremental migration phases. Changes: - Add ESBUILD_MIGRATION_PLAN.md: Complete 46KB guide with 6 phases - Add ESBUILD_README.md: Quick reference for starting migration - Add ESBUILD_IMPORT_FIXES.md: Summary of import corrections - Archive ESBUILD_SETUP_OLD.md: Preserve previous incomplete plan Key improvements: - ES module exports for TypeScript→TypeScript dependencies - Alpine.js ONLY for template bridge (not internal TS) - Incremental migration with no legacy code - Clear testing and rollback procedures - File-by-file checklists for each phase The plan corrects critical issues: - 193+ internal window reads → proper ES imports - Function wrapping (themeDropdown.ts) → restructured - Dual exports: ES modules + Alpine namespaces - SSR-first with progressive enhancement Total scope: 21 TypeScript files, 27 template files, ~1700 lines of detailed instructions. Related: Issue #ESBuild-Migration
5.1 KiB
ESBuild Migration - Quick Reference
Status: Ready to Execute
Created: Complete migration plan (ESBUILD_MIGRATION_PLAN.md) Archived: Old incomplete plan (ESBUILD_SETUP_OLD.md)
The Problem
- 274 window global references across 21 TypeScript files
- 193+ internal TypeScript dependencies using window instead of ES imports
- 27 template files with 150+ unique onclick handlers
- Function wrapping creating tight coupling
- Individual .js files instead of single bundle
The Solution
Incremental migration with no legacy code:
- Phase 0: Add ES exports (1-2 hours) - Can ship ✅
- Phase 1: Convert internal dependencies (4-6 hours) - Can ship ✅
- Phase 2: Alpine.js bridge (2-3 hours) - Can ship ✅
- Phase 3: Template migration (27-54 hours) - Can ship per template ✅
- Phase 4: Data injection (1-2 hours) - Can ship ✅
- Phase 5: Cleanup (1-2 hours) - Final step
Total: 36-69 hours over ~5 weeks
Key Architecture Decisions
1. ES Modules for TypeScript → TypeScript
// library.ts
import { api } from "./api";
import { showToast } from "./toast";
const response = await api.get("/libraries");
showToast.success("Loaded!");
2. Alpine.js for Templates → TypeScript ONLY
// api.ts (bottom of file)
import { Alpine } from "./alpine";
Alpine.global("api", {
get: apiGet,
post: apiPost,
// ...
});
<!-- template -->
<button @click="api.post('/api/save', data)">Save</button>
3. SSR-First with Progressive Enhancement
- Server renders complete HTML with data
- Client-side JavaScript only for interactivity
- Data loaded via fetch() APIs
- No window globals for data
File Changes
21 TypeScript Files
All will have:
- ES module exports (
export { functions }) - ES module imports (
import { functions } from "./module") - Alpine registration (
Alpine.global("namespace", { ... })) - NO window exports (cleaned up in Phase 5)
27 Template Files
All will have:
- Single script tag:
<script src="/static/main.js"></script> - Alpine directives:
@clickinstead ofonclick - Alpine state:
x-datafor modals/dropdowns - NO individual .js file loads
Quick Start
Right Now: Start Phase 0
# 1. Open web/src/api.ts
# 2. Add at bottom (after Alpine.global block):
export { apiGet, apiPost, apiPut, apiDelete, apiPatch, handleResponse, handleVoidResponse, handleError };
# 3. Repeat for other utility modules (toast.ts, events.ts, theme.ts, header.ts, woodPaneling.ts)
# 4. Build and test
npm run build:ts
go run .
After Phase 0 Complete
Move to Phase 1: Convert internal dependencies (see full plan)
Critical Path
Must complete in order:
- Phase 0 (ES exports) - Enables Phase 1
- Phase 1 (Internal imports) - Enables Phase 2
- Phase 2 (Alpine registration) - Enables Phase 3
- Phase 3 (Template migration) - Can do incrementally
- Phase 4 (Data injection) - Verify pattern
- Phase 5 (Cleanup) - Final polish
Safety Features
✅ Incremental: Each phase is complete and testable ✅ Ship anytime: Can deploy after Phases 0-2, or during Phase 3 ✅ Easy rollback: Revert individual files if issues ✅ No legacy: Each file fully migrated, no half-states ✅ Testing: Clear verification criteria for each phase
Common Patterns
Before Migration
// TypeScript
function doWork() { ... }
(window as any).doWork = doWork;
// Another file
const result = (window as any).doWork();
<!-- Template -->
<button onclick="doWork()">Click</button>
After Migration
// work.ts
export function doWork() { ... }
import { Alpine } from "./alpine";
Alpine.global("work", { doWork });
// Another file
import { doWork } from "./work";
const result = doWork();
<!-- Template -->
<button @click="work.doWork()">Click</button>
Testing
After Each Phase
# Build
npm run build:ts
# Run
go run .
# Test
# - Homepage loads
# - Login works
# - Dashboard loads
# - No console errors
# - No 404s for .js files
Rollback
If any phase has issues:
# Revert changes
git checkout web/src/ # For TypeScript issues
git checkout templates/PROBLEM.templ # For template issues
# Rebuild
npm run build:ts
templ generate
go run .
Next Steps
- Read the full plan:
ESBUILD_MIGRATION_PLAN.md - Start Phase 0: Add ES exports (1-2 hours)
- Test thoroughly after each phase
- Track progress using checklist in plan
- Ask questions if anything is unclear
Resources
- Full Plan: ESBUILD_MIGRATION_PLAN.md (this document)
- Old Plan: ESBUILD_SETUP_OLD.md (archived, incomplete)
- Alpine Docs: https://alpinejs.dev/
- ESBuild Docs: https://esbuild.github.io/
- Templ Docs: https://github.com/a-h/templ
Support
If you encounter issues:
- Check the "Troubleshooting" section in the full plan
- Review the "Testing Checklist" for your phase
- Use the "Rollback Procedures" if needed
- Reference the "Quick Reference" patterns
Remember: This is an incremental migration with clear phases. Take it one phase at a time, test thoroughly, and you'll have a clean, modern codebase ready for launch.