Files
iptvnator/apps/web/jest.config.ts
T
4grayandClaude Fable 5.1 8bc877b625 chore(performance): measure initial bytes of the built web app (#1692)
First step of the performance-journeys ratchet (plan thread: J1 `launch`, counter `renderer.initialBytes`).

- `tools/performance/measure-initial-bytes.mjs` reads the built `dist/apps/web/index.html` and sums `index.html` plus every same-origin `<script src>`, `<link rel="stylesheet">` and `<link rel="modulepreload">` it references. Manifest, icons, external URLs and lazy chunks are not counted. A referenced file missing from the build fails the measurement instead of counting as zero bytes.
- `--json` prints the breakdown; `--summary <file>` writes the `journeys.<journey>.counters` shape a ratchet checker will consume (next PR).
- New Nx project `performance-tools` (test + lint targets), Tier B in `tools/coverage/coverage-policy.json`, root scripts `perf:initial-bytes` and `perf:tools:test`.
- New contract `docs/architecture/performance-journeys.md`, linked from the validation map, the agent context map and the README.
- **Review follow-ups:** resources are deduplicated by request URL (query kept, fragment dropped); `index.html` is parsed with parse5 (already a repository dependency, scripting enabled), so comments, bogus comments, raw-text bodies (script/style/noscript/title/textarea), inert `<template>` contents and character references in attributes all follow the HTML5 algorithm instead of a hand-written scanner; the review's edge cases stay as regression tests; docs show the `pnpm --silent` form for JSON output and explain how the counter relates to Angular's rounded "Initial total".
- **Found while measuring:** the environment files and the playback diagnostic panel imported the whole `package.json` (`import packageJson from '@package'`), which esbuild cannot tree-shake, so `main.js` carried the complete file and the counter moved with every script or dependency edit. They now import `{ version }` only (eb662c485): `main.js` shrinks by **11,539 bytes** and the counter no longer depends on `package.json`. 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. Release note: `.changes/web-version-only-from-package-json.md` (`type: perf`).

Production build after this PR measures **2,739,508 bytes** (11 files + `index.html`); Angular's "Initial total" is this minus `index.html` and `assets/app-config.js`. The baseline file and the CI check land in the follow-up PRs; C1 (lazy Angular date locales) then lowers it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-26 13:31:27 +02:00

53 lines
1.6 KiB
TypeScript

import { nxPreset } from '@nx/jest/preset';
import { createEsmPreset } from 'jest-preset-angular/presets/index.js';
const angularEsmPreset = createEsmPreset({
diagnostics: false,
tsconfig: '<rootDir>/tsconfig.spec.json',
});
const coverageReporters = ['json', 'json-summary', 'lcovonly', 'text-summary'];
const collectCoverageFrom = [
'src/**/*.{ts,js,mjs,html}',
'!src/**/*.{spec,test}.ts',
'!src/**/test-setup.ts',
'!src/**/test-stubs/**',
'!src/**/*.generated.*',
'!src/**/environments/**',
'!src/**/index.ts',
];
export default {
...nxPreset,
...angularEsmPreset,
displayName: 'web',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../coverage/apps/web',
resolver: nxPreset.resolver,
moduleFileExtensions: Array.from(
new Set([
...(nxPreset.moduleFileExtensions ?? []),
...(angularEsmPreset.moduleFileExtensions ?? []),
])
),
testMatch: nxPreset.testMatch,
testEnvironmentOptions: {},
snapshotSerializers: angularEsmPreset.snapshotSerializers,
moduleNameMapper: {
...(angularEsmPreset.moduleNameMapper ?? {}),
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',
},
transform: angularEsmPreset.transform,
transformIgnorePatterns: [],
extensionsToTreatAsEsm: angularEsmPreset.extensionsToTreatAsEsm,
coverageReporters,
collectCoverageFrom,
};