Files
bookhoard/ESBUILD_README.md
T
john-okeefe e20857d760 docs: Create comprehensive ESBuild migration plan
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
2026-03-08 01:14:07 -05:00

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:

  1. Phase 0: Add ES exports (1-2 hours) - Can ship
  2. Phase 1: Convert internal dependencies (4-6 hours) - Can ship
  3. Phase 2: Alpine.js bridge (2-3 hours) - Can ship
  4. Phase 3: Template migration (27-54 hours) - Can ship per template
  5. Phase 4: Data injection (1-2 hours) - Can ship
  6. 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: @click instead of onclick
  • Alpine state: x-data for 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:

  1. Phase 0 (ES exports) - Enables Phase 1
  2. Phase 1 (Internal imports) - Enables Phase 2
  3. Phase 2 (Alpine registration) - Enables Phase 3
  4. Phase 3 (Template migration) - Can do incrementally
  5. Phase 4 (Data injection) - Verify pattern
  6. 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

  1. Read the full plan: ESBUILD_MIGRATION_PLAN.md
  2. Start Phase 0: Add ES exports (1-2 hours)
  3. Test thoroughly after each phase
  4. Track progress using checklist in plan
  5. Ask questions if anything is unclear

Resources

Support

If you encounter issues:

  1. Check the "Troubleshooting" section in the full plan
  2. Review the "Testing Checklist" for your phase
  3. Use the "Rollback Procedures" if needed
  4. 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.