perf(web): bundle only the app version from package.json

The environment files and the playback diagnostic panel imported the
whole package.json (`import packageJson from '@package'`) to read its
version, and esbuild cannot tree-shake a property read on a JSON default
import, so main.js carried the complete file: scripts, dependency lists
and all. Every edit to a script or dependency moved renderer.initialBytes
even though nothing users download had changed, which is what produced
the apparent 556-byte "platform difference" between a local build made
before the perf scripts were added and the CI build made after.

Import `{ version }` instead, which esbuild tree-shakes down to the one
string. Production main.js shrinks by 11,539 bytes (renderer.initialBytes
2,751,047 -> 2,739,508) and no longer changes with package.json edits.
Jest's ESM loader exposes JSON only as a default export, so the two web
Jest configs map '@package' to a stub that serves the real file's fields
as named exports.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5.1 committed 2026-09-26 11:12:50 +02:00
1 parent dfe884360d
commit eb662c4855
10 files changed
+34 -11

No files matched your search

@@ -0,0 +1,7 @@
---
type: perf
area: web
---
The app no longer ships its whole `package.json` inside the startup bundle,
only its version number, which trims about 11 KB from every launch.
+1
View File
@@ -39,6 +39,7 @@ export default {
tslib: 'tslib/tslib.es6.js',
'^iptv-playlist-parser$':
'<rootDir>/src/test-stubs/iptv-playlist-parser.mjs',
'^@package$': '<rootDir>/src/test-stubs/package.mjs',
'^shaka-player$': '<rootDir>/src/test-stubs/shaka-player.js',
'^video.js$': '<rootDir>/src/test-stubs/video-js.js',
'^rxjs': '<rootDir>/../../node_modules/rxjs/dist/bundles/rxjs.umd.js',
+2 -2
View File
@@ -3,11 +3,11 @@
// `ng build --env=prod` then `index.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
import packageJson from '@package';
import { version as appVersion } from '@package';
export const AppConfig = {
production: false,
environment: 'DEV',
version: packageJson.version,
version: appVersion,
BACKEND_URL: 'http://localhost:3000',
};
@@ -1,8 +1,8 @@
import packageJson from '@package';
import { version as appVersion } from '@package';
export const AppConfig = {
production: true,
environment: 'PROD',
version: packageJson.version,
version: appVersion,
BACKEND_URL: 'https://iptvnator-playlist-parser-api.vercel.app',
};
+2 -2
View File
@@ -1,8 +1,8 @@
import packageJson from '@package';
import { version as appVersion } from '@package';
export const AppConfig = {
production: false,
environment: 'LOCAL',
version: packageJson.version,
version: appVersion,
BACKEND_URL: 'http://localhost:3000',
};
+2 -2
View File
@@ -1,8 +1,8 @@
import packageJson from '@package';
import { version as appVersion } from '@package';
export const AppConfig = {
production: false,
environment: 'WEB',
version: packageJson.version,
version: appVersion,
BACKEND_URL: 'http://localhost:3333',
};
+12
View File
@@ -0,0 +1,12 @@
// Jest's ESM loader exposes a JSON module only as a default export, while the
// app imports `{ version }` from '@package' so esbuild can tree-shake the rest
// of package.json out of the bundle. This stub serves the real file's fields
// as named exports for tests.
import { readFileSync } from 'node:fs';
const packageJson = JSON.parse(
readFileSync(new URL('../../../../package.json', import.meta.url), 'utf8')
);
export const version = packageJson.version;
export default packageJson;
+3 -1
View File
@@ -38,7 +38,9 @@ error, never zero bytes. The value is raw (uncompressed) size, which is what the
renderer parses. It is Angular's "Initial total" plus `index.html` and
`assets/app-config.js` (about 4 KB together), so it sits slightly above the
rounded figure the build prints; never copy that figure into a baseline, use
the script's output.
the script's output. The bundle embeds only the app version from
`package.json` (a named import, which esbuild tree-shakes), not the whole
file, so editing scripts or dependencies does not move the counter.
```bash
pnpm nx build web # production configuration
+1
View File
@@ -37,6 +37,7 @@ export default {
tslib: 'tslib/tslib.es6.js',
'^iptv-playlist-parser$':
'<rootDir>/apps/web/src/test-stubs/iptv-playlist-parser.mjs',
'^@package$': '<rootDir>/apps/web/src/test-stubs/package.mjs',
'^shaka-player$': '<rootDir>/apps/web/src/test-stubs/shaka-player.js',
'^rxjs': '<rootDir>/node_modules/rxjs/dist/bundles/rxjs.umd.js',
'^uuid$': '<rootDir>/node_modules/uuid/wrapper.mjs',
@@ -1,5 +1,5 @@
import { DOCUMENT } from '@angular/common';
import packageJson from '@package';
import { version as appVersion } from '@package';
import { createDiagnosticReport } from './playback-diagnostic-report.util';
import { ClipboardModule } from '@angular/cdk/clipboard';
import {
@@ -91,7 +91,7 @@ export class PlaybackDiagnosticPanelComponent {
readonly diagnosticReport = computed(() =>
createDiagnosticReport(
this.diagnostic(),
packageJson.version,
appVersion,
this.document.defaultView?.navigator.userAgent ?? ''
)
);