perf(bundle): dynamic-import iptv-playlist-parser to drop 130KB validator from eager bundle

iptv-playlist-parser statically imports the entire validator library
(~130KB across 113 files). Because PlaylistsService is eager (re-exported
through the 'services' barrel that AppComponent imports), validator was
landing in the cold-start preloaded chunk even though parse() only runs
on user-triggered playlist imports (FILE/URL/TEXT add).

Switch handlePlaylistParsing() to dynamic-import the parser. The two
callers (playlist-backup.service and the parsePlaylist$ NgRx effect)
needed minor adjustments — the effect now uses mergeMap(from(...)) to
flatten the Promise back into the action stream.

Cold-start preloaded chunk: 1511KB -> 1374KB (-9%).
Validator now sits in a separate ~129KB chunk that loads only when
the user adds a playlist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3ec205264762
This commit is contained in:
4grayandClaude Opus 4.7 committed 2026-05-01 23:10:10 +02:00
1 parent 39ddaa2f9c
commit d6a91d79e9
3 files changed
+16 -9

No files matched your search

+10 -6
View File
@@ -11,7 +11,9 @@ import {
EMPTY,
filter,
firstValueFrom,
from,
map,
mergeMap,
switchMap,
tap,
withLatestFrom,
@@ -277,12 +279,14 @@ export class PlaylistEffects {
parsePlaylist$ = createEffect(() => {
return this.actions$.pipe(
ofType(PlaylistActions.parsePlaylist),
map((action) =>
this.playlistsService.handlePlaylistParsing(
action.uploadType,
action.playlist,
action.title,
action.path
mergeMap((action) =>
from(
this.playlistsService.handlePlaylistParsing(
action.uploadType,
action.playlist,
action.title,
action.path
)
)
),
map((playlist) =>
@@ -610,7 +610,7 @@ export class PlaylistBackupService {
playlistId: string,
existing: Playlist | null
): Promise<Playlist> {
const parsedPlaylist = this.playlistsService.handlePlaylistParsing(
const parsedPlaylist = await this.playlistsService.handlePlaylistParsing(
'TEXT',
entry.source.rawM3u,
entry.title
+5 -2
View File
@@ -2,7 +2,6 @@
import { inject, Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { TranslateService } from '@ngx-translate/core';
import { parse } from 'iptv-playlist-parser';
import {
aggregateFavoriteChannels,
createFavoritesPlaylist,
@@ -760,13 +759,17 @@ export class PlaylistsService {
);
}
handlePlaylistParsing(
async handlePlaylistParsing(
uploadType: 'FILE' | 'URL' | 'TEXT',
playlist: string,
title: string,
path?: string
) {
try {
// Dynamic import keeps the ~130KB validator dep (transitively pulled
// by iptv-playlist-parser) out of the eager bundle. parse() only runs
// on user-triggered imports.
const { parse } = await import('iptv-playlist-parser');
const parsedPlaylist = parse(playlist);
return createPlaylistObject(
title,