Skip to content

perf: reduce main bundle via shared subpath exports#260

Merged
test3207 merged 2 commits intomainfrom
perf/bundle-optimization-subpath-exports
Dec 22, 2025
Merged

perf: reduce main bundle via shared subpath exports#260
test3207 merged 2 commits intomainfrom
perf/bundle-optimization-subpath-exports

Conversation

@test3207
Copy link
Owner

@test3207 test3207 commented Dec 22, 2025

Summary

Reduce JavaScript main bundle size from 354KB to 293KB (61KB reduction, 17% smaller) by implementing subpath exports for @m3w/shared and fixing barrel export patterns.

Closes #261

Part of Epic 4.3 (#219)

Problem

The main bundle was unnecessarily large due to:

  1. @aws-crypto/sha256-browser (~112KB) pulled via @/lib/utils/index.ts barrel export
  2. Zod + 45 locale files (~30KB) pulled via @m3w/shared barrel even when only importing constants/types
  3. Unnecessary eager loading of MobileLayout, ReloadPrompt, InstallPrompt
  4. Useless dynamic imports in auth-provider (modules already statically imported elsewhere)

Solution

1. Shared Package Subpath Exports

Added 5 subpath exports to @m3w/shared:

  • @m3w/shared/types - Runtime enums (RepeatMode)
  • @m3w/shared/schemas - Zod schemas (only for lazy-loaded code)
  • @m3w/shared/api-contracts - Route definitions
  • @m3w/shared/constants - Pure functions (isDefaultLibrary, etc.)
  • @m3w/shared/transformers - Data transformers

2. Barrel Export Fixes

  • Removed hash utilities from @/lib/utils/index.ts (import directly when needed)
  • Created @/lib/shared.ts as unified re-export layer for safe imports

3. Lazy Loading Improvements

  • Lazy load MobileLayout (removes playerStore from critical path)
  • Lazy load ReloadPrompt and InstallPrompt (only shown on PWA events)
  • Split Radix UI into ui-core (landing page) and ui-extended (lazy loaded)

4. Code Cleanup

  • Converted useless dynamic imports in auth-provider to static (modules already in main bundle)
  • Added default export to mobile-layout.tsx for lazy loading

Results

Metric Before After Change
Main bundle 354 KB 293 KB -61 KB (-17%)
gzip 109 KB 91 KB -18 KB
Build warnings 4 0
Tests 410 pass 410 pass

Files Changed

Shared package:

  • shared/tsup.config.ts - Multiple entry points
  • shared/package.json - Subpath exports

Frontend:

  • frontend/src/lib/shared.ts - NEW: Unified re-export layer
  • frontend/src/lib/utils/index.ts - Remove hash exports
  • frontend/src/main.tsx - Lazy load MobileLayout, PWA prompts
  • frontend/src/components/layouts/mobile-layout.tsx - Add default export
  • frontend/src/components/providers/auth-provider.tsx - Static imports
  • frontend/vite.config.ts - Split Radix UI chunks
  • 17 files updated to use subpath imports

Documentation:

  • .github/instructions/development-standards.instructions.md - Bundle optimization guidelines

Testing

  • Build passes with zero warnings
  • All 410 tests passing
  • Verified bundle sizes with vite-bundle-visualizer
  • Manual testing of lazy-loaded components

Follow-up

The new "Bundle Optimization" section in development standards documents:

  • What goes on critical path vs lazy loaded
  • Three patterns for dynamic imports
  • Anti-patterns to avoid
  • How to check bundle impact

- Add subpath exports to @m3w/shared (types, schemas, api-contracts, constants, transformers)
- Update 20+ frontend files to use specific subpaths
- Remove hash utils from barrel export (import directly when needed)
- Lazy load MobileLayout, ReloadPrompt, InstallPrompt
- Convert auth-provider from useless dynamic imports to static
- Create @/lib/shared.ts as unified re-export layer
- Document bundle optimization patterns in development standards
- Split Radix UI into ui-core (landing page) and ui-extended (lazy loaded)

Results:
- Main bundle: 354KB → 293KB (-61KB, -17%)
- gzip: 109KB → 91KB (-18KB)
- Build warnings: 4 → 0
- Tests: 410 pass
Copilot AI review requested due to automatic review settings December 22, 2025 04:56
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR successfully reduces the main JavaScript bundle size from 354KB to 293KB (17% reduction, 61KB saved) by implementing subpath exports for the @m3w/shared package and optimizing lazy loading patterns. The optimization prevents heavy dependencies like Zod schemas and AWS crypto libraries from being unnecessarily pulled into the main bundle.

Key Changes:

  • Added 5 subpath exports to @m3w/shared (/types, /schemas, /api-contracts, /constants, /transformers) to enable tree-shaking
  • Lazy loaded MobileLayout, ReloadPrompt, and InstallPrompt components to remove player store and PWA code from critical path
  • Removed hash utilities from barrel exports to avoid pulling @aws-crypto/sha256-browser into main bundle
  • Added comprehensive bundle optimization guidelines to development standards

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
shared/tsup.config.ts Configured multiple entry points for subpath exports with clear documentation
shared/package.json Defined 5 subpath exports for types, schemas, api-contracts, constants, and transformers
frontend/vite.config.ts Split Radix UI into core and extended chunks, separated audio and utility vendors
frontend/src/lib/utils/index.ts Removed hash utility exports to avoid pulling crypto library into main bundle
frontend/src/lib/shared.ts NEW: Unified re-export layer (currently unused - see comment)
frontend/src/main.tsx Lazy loaded MobileLayout and PWA prompt components to reduce critical path
frontend/src/components/layouts/mobile-layout.tsx Added default export for lazy loading compatibility
frontend/src/components/providers/auth-provider.tsx Converted useless dynamic imports to static imports with cleanup improvements
frontend/src/stores/*.ts Updated 3 store files to use subpath imports for RepeatMode and constants
frontend/src/pages/*.tsx Updated 3 page files to use subpath imports for constants
frontend/src/lib/offline-proxy/routes/*.ts Updated 3 route files to use subpath imports for schemas and transformers
frontend/src/lib/db/schema.ts Updated to use subpath import for RepeatMode type
frontend/src/lib/cache/response-cache.ts Updated to use subpath import for getCacheConfig
frontend/src/lib/audio/queue.ts Updated to use subpath import for RepeatMode type
frontend/src/lib/api/router.ts Updated to use subpath import for isOfflineCapable
frontend/src/lib/utils/defaults.ts Updated to use subpath imports for constants
frontend/src/services/api/main/resources/player.ts Updated to use subpath import for RepeatMode type
frontend/src/components/features/upload/upload-form/index.tsx Updated to use subpath import for isDefaultLibrary
frontend/src/components/features/playlists/AddToPlaylistSheet.tsx Updated to use subpath import for isFavoritesPlaylist
frontend/src/components/features/player/full-player/PlaybackControls.tsx Updated to use subpath import for RepeatMode type
.github/instructions/development-standards.instructions.md Added comprehensive "Bundle Optimization" section with patterns and guidelines

Core modules now import from unified @/lib/shared layer:
- lib/api/router.ts
- lib/audio/queue.ts
- lib/cache/response-cache.ts
- lib/db/schema.ts
- lib/utils/defaults.ts
- services/api/main/resources/player.ts
- stores/libraryStore.ts
- stores/playerStore/*
- stores/playlistStore.ts

Lazy-loaded modules (pages, offline-proxy) continue using
direct @m3w/shared subpath imports as documented.
@github-actions
Copy link
Contributor

Coverage after merging perf/bundle-optimization-subpath-exports into main will be

78.73%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
100%100%100%100%
src/lib
   events.ts100%100%100%100%
   logger-client.ts82.61%60%100%100%15, 21, 27, 8
   shared.ts100%100%100%100%
src/lib/audio
   media-session.ts74.84%90.91%71.43%65.12%105–106, 124, 135–137, 144–146, 148, 155–157, 159, 167–169, 171, 178–180, 182, 198, 212, 226, 237, 245–246, 259, 44–45, 76, 86–87, 93
   queue.ts86.15%82.54%92%86.92%210–211, 223, 225, 225, 225–226, 228, 249–252, 303, 313, 313, 313–314, 316, 37–38, 54–55, 72, 76–77
src/lib/offline-proxy/routes
   libraries.ts42.23%24.32%57.14%51.69%127–128, 213, 253, 266–268, 271, 273, 273, 273–274, 283, 283, 283, 283, 283–284, 293–294, 296, 296, 296–297, 301, 304–305, 308, 310, 310, 310, 312, 316, 316–317, 317, 321, 325, 328, 331, 331, 331–332, 343, 346, 349, 349, 349, 349, 349–350, 352, 357, 361, 364, 366, 369, 369–370, 370–371, 371–372, 372–373, 373–374, 374, 374, 374–375, 375–376, 376, 378, 378, 378, 378, 381, 381, 393–394, 397, 402, 402, 402–404, 404, 410, 420–421, 427–430, 430, 430, 432, 434, 434, 434, 434, 434–435, 445, 448, 450, 455, 66, 95, 95–96
   player.ts47.26%35%57.14%55.70%102, 138, 150–151, 154, 159, 161, 165, 167, 167, 167–169, 171, 171, 171–172, 180, 180–181, 181, 195, 200–201, 207–208, 208, 208–209, 217, 217–218, 218, 231, 236, 259, 280–283, 294, 306–309, 311, 313, 313, 313–314, 314, 314–315, 315, 315–316, 316, 316, 320, 322, 327, 57–59, 59, 59, 59, 59–60, 64–65, 72, 74, 94
   playlists.ts86.27%92.31%91.67%84.35%102, 121, 138, 155, 175, 189, 195–196, 215, 228, 234, 59, 65–70, 72, 89
   songs.ts90.48%75%100%95.24%104, 106, 113, 115, 143–144
src/lib/offline-proxy/utils
   auth.ts93.33%100%100%89.47%70–71
   index.ts100%100%100%100%
   sorting.ts100%100%100%100%
src/lib/storage
   storage-constants.ts100%100%100%100%
src/lib/utils
   audio-filter.ts100%100%100%100%
   defaults.ts100%100%100%100%
   format-duration.ts100%100%100%100%
   format.ts100%100%100%100%
   hash.ts88.24%100%100%85.19%64–65, 87–88
   uuid.ts90%87.50%83.33%93.75%31–32
src/locales
   i18n.ts95.45%87.50%100%97.37%23, 40–41

@test3207 test3207 merged commit a9f3537 into main Dec 22, 2025
4 checks passed
@test3207 test3207 deleted the perf/bundle-optimization-subpath-exports branch December 22, 2025 09:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: reduce main bundle via shared subpath exports

1 participant