@ng-prism/plugin-perf 21.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/bundle/bundle-scanner.d.ts +3 -0
  2. package/dist/bundle/bundle-scanner.d.ts.map +1 -0
  3. package/dist/bundle/bundle-scanner.js +85 -0
  4. package/dist/bundle/bundle-section.component.d.ts +23 -0
  5. package/dist/bundle/bundle-section.component.d.ts.map +1 -0
  6. package/dist/bundle/bundle-section.component.js +321 -0
  7. package/dist/index.browser.d.ts +5 -0
  8. package/dist/index.browser.d.ts.map +1 -0
  9. package/dist/index.browser.js +3 -0
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +3 -0
  13. package/dist/memory/memory-section.component.d.ts +14 -0
  14. package/dist/memory/memory-section.component.d.ts.map +1 -0
  15. package/dist/memory/memory-section.component.js +323 -0
  16. package/dist/memory/perf-memory.service.d.ts +26 -0
  17. package/dist/memory/perf-memory.service.d.ts.map +1 -0
  18. package/dist/memory/perf-memory.service.js +62 -0
  19. package/dist/perf-config.token.d.ts +4 -0
  20. package/dist/perf-config.token.d.ts.map +1 -0
  21. package/dist/perf-config.token.js +2 -0
  22. package/dist/perf-panel.component.d.ts +18 -0
  23. package/dist/perf-panel.component.d.ts.map +1 -0
  24. package/dist/perf-panel.component.js +247 -0
  25. package/dist/perf-plugin.browser.d.ts +4 -0
  26. package/dist/perf-plugin.browser.d.ts.map +1 -0
  27. package/dist/perf-plugin.browser.js +14 -0
  28. package/dist/perf-plugin.d.ts +4 -0
  29. package/dist/perf-plugin.d.ts.map +1 -0
  30. package/dist/perf-plugin.js +27 -0
  31. package/dist/perf.types.d.ts +31 -0
  32. package/dist/perf.types.d.ts.map +1 -0
  33. package/dist/perf.types.js +8 -0
  34. package/dist/render/perf-render.service.d.ts +19 -0
  35. package/dist/render/perf-render.service.d.ts.map +1 -0
  36. package/dist/render/perf-render.service.js +73 -0
  37. package/dist/render/render-section.component.d.ts +13 -0
  38. package/dist/render/render-section.component.d.ts.map +1 -0
  39. package/dist/render/render-section.component.js +205 -0
  40. package/dist/render/sparkline.component.d.ts +23 -0
  41. package/dist/render/sparkline.component.d.ts.map +1 -0
  42. package/dist/render/sparkline.component.js +165 -0
  43. package/package.json +49 -0
@@ -0,0 +1,3 @@
1
+ import type { BundleMetrics } from '../perf.types.js';
2
+ export declare function scanBundle(filePath: string, maxTreeDepth?: number): BundleMetrics;
3
+ //# sourceMappingURL=bundle-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-scanner.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle-scanner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,aAAa,CAkB5E"}
@@ -0,0 +1,85 @@
1
+ import { readFileSync } from 'fs';
2
+ import { gzipSync } from 'zlib';
3
+ import { dirname, resolve, extname } from 'path';
4
+ import ts from 'typescript';
5
+ export function scanBundle(filePath, maxTreeDepth = 5) {
6
+ const source = readFileSync(filePath, 'utf-8');
7
+ const sourceSize = Buffer.byteLength(source, 'utf-8');
8
+ let gzipEstimate;
9
+ try {
10
+ gzipEstimate = gzipSync(Buffer.from(source)).length;
11
+ }
12
+ catch {
13
+ gzipEstimate = Math.round(sourceSize * 0.31);
14
+ }
15
+ const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);
16
+ const importList = extractImports(sourceFile);
17
+ const directImports = importList.length;
18
+ const treeDepth = computeTreeDepth(filePath, importList, maxTreeDepth);
19
+ return { sourceSize, gzipEstimate, directImports, importList, treeDepth };
20
+ }
21
+ function extractImports(sourceFile) {
22
+ const imports = [];
23
+ ts.forEachChild(sourceFile, (node) => {
24
+ if (ts.isImportDeclaration(node) &&
25
+ node.moduleSpecifier &&
26
+ ts.isStringLiteral(node.moduleSpecifier)) {
27
+ imports.push(node.moduleSpecifier.text);
28
+ }
29
+ });
30
+ return imports;
31
+ }
32
+ function computeTreeDepth(filePath, imports, maxDepth, visited = new Set(), currentDepth = 1) {
33
+ if (currentDepth >= maxDepth)
34
+ return currentDepth;
35
+ visited.add(filePath);
36
+ let deepest = currentDepth;
37
+ const dir = dirname(filePath);
38
+ for (const imp of imports) {
39
+ if (!imp.startsWith('.'))
40
+ continue;
41
+ const resolved = resolveImport(dir, imp);
42
+ if (!resolved || visited.has(resolved))
43
+ continue;
44
+ try {
45
+ const childSource = readFileSync(resolved, 'utf-8');
46
+ const childSf = ts.createSourceFile(resolved, childSource, ts.ScriptTarget.Latest, true);
47
+ const childImports = extractImports(childSf);
48
+ const childDepth = computeTreeDepth(resolved, childImports, maxDepth, visited, currentDepth + 1);
49
+ deepest = Math.max(deepest, childDepth);
50
+ }
51
+ catch {
52
+ // File not readable — treat as leaf
53
+ }
54
+ }
55
+ return deepest;
56
+ }
57
+ function resolveImport(dir, importPath) {
58
+ const extensions = ['.ts', '.tsx', '.js', '.mjs'];
59
+ const base = resolve(dir, importPath);
60
+ if (extname(base)) {
61
+ const withoutExt = base.replace(/\.[^.]+$/, '');
62
+ for (const ext of extensions) {
63
+ try {
64
+ readFileSync(withoutExt + ext);
65
+ return withoutExt + ext;
66
+ }
67
+ catch { /* noop */ }
68
+ }
69
+ }
70
+ for (const ext of extensions) {
71
+ try {
72
+ readFileSync(base + ext);
73
+ return base + ext;
74
+ }
75
+ catch { /* noop */ }
76
+ }
77
+ for (const ext of extensions) {
78
+ try {
79
+ readFileSync(resolve(base, 'index' + ext));
80
+ return resolve(base, 'index' + ext);
81
+ }
82
+ catch { /* noop */ }
83
+ }
84
+ return null;
85
+ }
@@ -0,0 +1,23 @@
1
+ import type { BundleMetrics } from '../perf.types.js';
2
+ import * as i0 from "@angular/core";
3
+ export declare class BundleSectionComponent {
4
+ readonly metrics: import("@angular/core").InputSignal<BundleMetrics | null>;
5
+ readonly warnKb: import("@angular/core").InputSignal<number>;
6
+ readonly critKb: import("@angular/core").InputSignal<number>;
7
+ readonly gzipPercent: import("@angular/core").Signal<number>;
8
+ readonly sourceBarPercent: import("@angular/core").Signal<number>;
9
+ readonly gzipBarPercent: import("@angular/core").Signal<number>;
10
+ readonly depthSteps: import("@angular/core").Signal<{
11
+ index: number;
12
+ filled: boolean;
13
+ deep: boolean;
14
+ }[]>;
15
+ readonly visibleImports: import("@angular/core").Signal<string[]>;
16
+ readonly hiddenImportCount: import("@angular/core").Signal<number>;
17
+ formatBytes(bytes: number): string;
18
+ formatKb(bytes: number): string;
19
+ chipClass(imp: string): string;
20
+ static ɵfac: i0.ɵɵFactoryDeclaration<BundleSectionComponent, never>;
21
+ static ɵcmp: i0.ɵɵComponentDeclaration<BundleSectionComponent, "perf-bundle-section", never, { "metrics": { "alias": "metrics"; "required": false; "isSignal": true; }; "warnKb": { "alias": "warnKb"; "required": false; "isSignal": true; }; "critKb": { "alias": "critKb"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
22
+ }
23
+ //# sourceMappingURL=bundle-section.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-section.component.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;;AAEtD,qBAwPa,sBAAsB;IACjC,QAAQ,CAAC,OAAO,4DAAqC;IACrD,QAAQ,CAAC,MAAM,8CAAa;IAC5B,QAAQ,CAAC,MAAM,8CAAa;IAE5B,QAAQ,CAAC,WAAW,yCAIjB;IAEH,QAAQ,CAAC,gBAAgB,yCAKtB;IAEH,QAAQ,CAAC,cAAc,yCAKpB;IAEH,QAAQ,CAAC,UAAU;;;;SAQhB;IAEH,QAAQ,CAAC,cAAc,2CAGpB;IAEH,QAAQ,CAAC,iBAAiB,yCAIvB;IAEH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIlC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAK/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;yCAvDnB,sBAAsB;2CAAtB,sBAAsB;CA4DlC"}
@@ -0,0 +1,321 @@
1
+ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ const _forTrack0 = ($index, $item) => $item.index;
4
+ function BundleSectionComponent_Conditional_0_For_69_Template(rf, ctx) { if (rf & 1) {
5
+ i0.ɵɵdomElement(0, "div", 29);
6
+ } if (rf & 2) {
7
+ const step_r1 = ctx.$implicit;
8
+ i0.ɵɵclassProp("filled", step_r1.filled)("deep", step_r1.deep);
9
+ } }
10
+ function BundleSectionComponent_Conditional_0_For_77_Template(rf, ctx) { if (rf & 1) {
11
+ i0.ɵɵdomElementStart(0, "span", 28);
12
+ i0.ɵɵtext(1);
13
+ i0.ɵɵdomElementEnd();
14
+ } if (rf & 2) {
15
+ const imp_r2 = ctx.$implicit;
16
+ const ctx_r2 = i0.ɵɵnextContext(2);
17
+ i0.ɵɵclassMap(ctx_r2.chipClass(imp_r2));
18
+ i0.ɵɵadvance();
19
+ i0.ɵɵtextInterpolate(imp_r2);
20
+ } }
21
+ function BundleSectionComponent_Conditional_0_Conditional_78_Template(rf, ctx) { if (rf & 1) {
22
+ i0.ɵɵdomElementStart(0, "span", 28);
23
+ i0.ɵɵtext(1);
24
+ i0.ɵɵdomElementEnd();
25
+ } if (rf & 2) {
26
+ const ctx_r2 = i0.ɵɵnextContext(2);
27
+ i0.ɵɵadvance();
28
+ i0.ɵɵtextInterpolate1("+", ctx_r2.hiddenImportCount(), " more");
29
+ } }
30
+ function BundleSectionComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
31
+ i0.ɵɵdomElementStart(0, "div", 1)(1, "div", 2)(2, "div", 3);
32
+ i0.ɵɵtext(3, "Source Size");
33
+ i0.ɵɵdomElementEnd();
34
+ i0.ɵɵdomElementStart(4, "div", 4);
35
+ i0.ɵɵtext(5);
36
+ i0.ɵɵdomElementStart(6, "span", 5);
37
+ i0.ɵɵtext(7, "KB");
38
+ i0.ɵɵdomElementEnd()();
39
+ i0.ɵɵdomElementStart(8, "div", 6);
40
+ i0.ɵɵtext(9);
41
+ i0.ɵɵdomElementEnd()();
42
+ i0.ɵɵdomElementStart(10, "div", 2)(11, "div", 3);
43
+ i0.ɵɵtext(12, "Gzip Estimate");
44
+ i0.ɵɵdomElementEnd();
45
+ i0.ɵɵdomElementStart(13, "div", 4);
46
+ i0.ɵɵtext(14);
47
+ i0.ɵɵdomElementStart(15, "span", 5);
48
+ i0.ɵɵtext(16, "KB");
49
+ i0.ɵɵdomElementEnd()();
50
+ i0.ɵɵdomElementStart(17, "div", 6);
51
+ i0.ɵɵtext(18);
52
+ i0.ɵɵdomElementEnd()();
53
+ i0.ɵɵdomElementStart(19, "div", 2)(20, "div", 3);
54
+ i0.ɵɵtext(21, "Direct Imports");
55
+ i0.ɵɵdomElementEnd();
56
+ i0.ɵɵdomElementStart(22, "div", 4);
57
+ i0.ɵɵtext(23);
58
+ i0.ɵɵdomElementEnd();
59
+ i0.ɵɵdomElementStart(24, "div", 6);
60
+ i0.ɵɵtext(25, "top-level only");
61
+ i0.ɵɵdomElementEnd()();
62
+ i0.ɵɵdomElementStart(26, "div", 2)(27, "div", 3);
63
+ i0.ɵɵtext(28, "Tree Depth");
64
+ i0.ɵɵdomElementEnd();
65
+ i0.ɵɵdomElementStart(29, "div", 4);
66
+ i0.ɵɵtext(30);
67
+ i0.ɵɵdomElementStart(31, "span", 5);
68
+ i0.ɵɵtext(32, "/ 10");
69
+ i0.ɵɵdomElementEnd()();
70
+ i0.ɵɵdomElementStart(33, "div", 6);
71
+ i0.ɵɵtext(34, "import levels");
72
+ i0.ɵɵdomElementEnd()()();
73
+ i0.ɵɵdomElementStart(35, "div", 7)(36, "div", 3);
74
+ i0.ɵɵtext(37, "Size Breakdown");
75
+ i0.ɵɵdomElementEnd();
76
+ i0.ɵɵdomElementStart(38, "div", 8)(39, "div", 9)(40, "span", 10);
77
+ i0.ɵɵtext(41, "source");
78
+ i0.ɵɵdomElementEnd();
79
+ i0.ɵɵdomElementStart(42, "div", 11);
80
+ i0.ɵɵdomElement(43, "div", 12);
81
+ i0.ɵɵdomElementEnd();
82
+ i0.ɵɵdomElementStart(44, "span", 13);
83
+ i0.ɵɵtext(45);
84
+ i0.ɵɵdomElementEnd()();
85
+ i0.ɵɵdomElementStart(46, "div", 9)(47, "span", 10);
86
+ i0.ɵɵtext(48, "gzip est.");
87
+ i0.ɵɵdomElementEnd();
88
+ i0.ɵɵdomElementStart(49, "div", 11);
89
+ i0.ɵɵdomElement(50, "div", 14);
90
+ i0.ɵɵdomElementEnd();
91
+ i0.ɵɵdomElementStart(51, "span", 15);
92
+ i0.ɵɵtext(52);
93
+ i0.ɵɵdomElementEnd()()();
94
+ i0.ɵɵdomElementStart(53, "div", 16)(54, "span");
95
+ i0.ɵɵtext(55, "Thresholds: ");
96
+ i0.ɵɵdomElementStart(56, "span", 17);
97
+ i0.ɵɵtext(57);
98
+ i0.ɵɵdomElementEnd();
99
+ i0.ɵɵtext(58, " \u00B7 ");
100
+ i0.ɵɵdomElementStart(59, "span", 18);
101
+ i0.ɵɵtext(60);
102
+ i0.ɵɵdomElementEnd()()()();
103
+ i0.ɵɵdomElementStart(61, "div", 19)(62, "div", 3);
104
+ i0.ɵɵtext(63, "Import Tree Depth");
105
+ i0.ɵɵdomElementEnd();
106
+ i0.ɵɵdomElementStart(64, "div", 20)(65, "span", 21);
107
+ i0.ɵɵtext(66, "depth");
108
+ i0.ɵɵdomElementEnd();
109
+ i0.ɵɵdomElementStart(67, "div", 22);
110
+ i0.ɵɵrepeaterCreate(68, BundleSectionComponent_Conditional_0_For_69_Template, 1, 4, "div", 23, _forTrack0);
111
+ i0.ɵɵdomElementEnd();
112
+ i0.ɵɵdomElementStart(70, "span", 24);
113
+ i0.ɵɵtext(71);
114
+ i0.ɵɵdomElementEnd()();
115
+ i0.ɵɵdomElementStart(72, "div", 25)(73, "div", 3);
116
+ i0.ɵɵtext(74);
117
+ i0.ɵɵdomElementEnd();
118
+ i0.ɵɵdomElementStart(75, "div", 26);
119
+ i0.ɵɵrepeaterCreate(76, BundleSectionComponent_Conditional_0_For_77_Template, 2, 3, "span", 27, i0.ɵɵrepeaterTrackByIdentity);
120
+ i0.ɵɵconditionalCreate(78, BundleSectionComponent_Conditional_0_Conditional_78_Template, 2, 1, "span", 28);
121
+ i0.ɵɵdomElementEnd()()();
122
+ } if (rf & 2) {
123
+ const m_r4 = ctx;
124
+ const ctx_r2 = i0.ɵɵnextContext();
125
+ i0.ɵɵadvance(5);
126
+ i0.ɵɵtextInterpolate(ctx_r2.formatKb(m_r4.sourceSize));
127
+ i0.ɵɵadvance(4);
128
+ i0.ɵɵtextInterpolate1("", ctx_r2.formatBytes(m_r4.sourceSize), " bytes");
129
+ i0.ɵɵadvance(5);
130
+ i0.ɵɵtextInterpolate(ctx_r2.formatKb(m_r4.gzipEstimate));
131
+ i0.ɵɵadvance(4);
132
+ i0.ɵɵtextInterpolate1("~", ctx_r2.gzipPercent(), "% of source");
133
+ i0.ɵɵadvance(5);
134
+ i0.ɵɵtextInterpolate(m_r4.directImports);
135
+ i0.ɵɵadvance(7);
136
+ i0.ɵɵtextInterpolate(m_r4.treeDepth);
137
+ i0.ɵɵadvance(13);
138
+ i0.ɵɵstyleProp("width", ctx_r2.sourceBarPercent(), "%");
139
+ i0.ɵɵadvance(2);
140
+ i0.ɵɵtextInterpolate1("", ctx_r2.formatKb(m_r4.sourceSize), " KB");
141
+ i0.ɵɵadvance(5);
142
+ i0.ɵɵstyleProp("width", ctx_r2.gzipBarPercent(), "%");
143
+ i0.ɵɵadvance(2);
144
+ i0.ɵɵtextInterpolate1("", ctx_r2.formatKb(m_r4.gzipEstimate), " KB");
145
+ i0.ɵɵadvance(5);
146
+ i0.ɵɵtextInterpolate1("warn \u2265 ", ctx_r2.warnKb(), " KB");
147
+ i0.ɵɵadvance(3);
148
+ i0.ɵɵtextInterpolate1("crit \u2265 ", ctx_r2.critKb(), " KB");
149
+ i0.ɵɵadvance(8);
150
+ i0.ɵɵrepeater(ctx_r2.depthSteps());
151
+ i0.ɵɵadvance(2);
152
+ i0.ɵɵstyleProp("color", m_r4.treeDepth >= 4 ? "var(--perf-warn, #fbbf24)" : "var(--prism-primary, #a78bfa)");
153
+ i0.ɵɵadvance();
154
+ i0.ɵɵtextInterpolate1("", m_r4.treeDepth, " / 10");
155
+ i0.ɵɵadvance(3);
156
+ i0.ɵɵtextInterpolate1("Direct Imports (", m_r4.directImports, ")");
157
+ i0.ɵɵadvance(2);
158
+ i0.ɵɵrepeater(ctx_r2.visibleImports());
159
+ i0.ɵɵadvance(2);
160
+ i0.ɵɵconditional(ctx_r2.hiddenImportCount() > 0 ? 78 : -1);
161
+ } }
162
+ function BundleSectionComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
163
+ i0.ɵɵdomElementStart(0, "div", 0)(1, "div", 30);
164
+ i0.ɵɵtext(2, "\uD83D\uDCE6");
165
+ i0.ɵɵdomElementEnd();
166
+ i0.ɵɵdomElementStart(3, "div", 31);
167
+ i0.ɵɵtext(4, "No bundle data available");
168
+ i0.ɵɵdomElementEnd();
169
+ i0.ɵɵdomElementStart(5, "div", 32);
170
+ i0.ɵɵtext(6, "Bundle metrics are collected at build time");
171
+ i0.ɵɵdomElementEnd()();
172
+ } }
173
+ export class BundleSectionComponent {
174
+ metrics = input(null, ...(ngDevMode ? [{ debugName: "metrics" }] : []));
175
+ warnKb = input(20, ...(ngDevMode ? [{ debugName: "warnKb" }] : []));
176
+ critKb = input(50, ...(ngDevMode ? [{ debugName: "critKb" }] : []));
177
+ gzipPercent = computed(() => {
178
+ const m = this.metrics();
179
+ if (!m || m.sourceSize === 0)
180
+ return 0;
181
+ return Math.round((m.gzipEstimate / m.sourceSize) * 100);
182
+ }, ...(ngDevMode ? [{ debugName: "gzipPercent" }] : []));
183
+ sourceBarPercent = computed(() => {
184
+ const m = this.metrics();
185
+ if (!m)
186
+ return 0;
187
+ const maxKb = this.critKb() * 1.2;
188
+ return Math.min(100, (m.sourceSize / 1024 / maxKb) * 100);
189
+ }, ...(ngDevMode ? [{ debugName: "sourceBarPercent" }] : []));
190
+ gzipBarPercent = computed(() => {
191
+ const m = this.metrics();
192
+ if (!m)
193
+ return 0;
194
+ const maxKb = this.critKb() * 1.2;
195
+ return Math.min(100, (m.gzipEstimate / 1024 / maxKb) * 100);
196
+ }, ...(ngDevMode ? [{ debugName: "gzipBarPercent" }] : []));
197
+ depthSteps = computed(() => {
198
+ const m = this.metrics();
199
+ const depth = m?.treeDepth ?? 0;
200
+ return Array.from({ length: 10 }, (_, i) => ({
201
+ index: i,
202
+ filled: i < depth,
203
+ deep: i >= 4 && i < depth,
204
+ }));
205
+ }, ...(ngDevMode ? [{ debugName: "depthSteps" }] : []));
206
+ visibleImports = computed(() => {
207
+ const m = this.metrics();
208
+ return m?.importList.slice(0, 10) ?? [];
209
+ }, ...(ngDevMode ? [{ debugName: "visibleImports" }] : []));
210
+ hiddenImportCount = computed(() => {
211
+ const m = this.metrics();
212
+ if (!m)
213
+ return 0;
214
+ return Math.max(0, m.importList.length - 10);
215
+ }, ...(ngDevMode ? [{ debugName: "hiddenImportCount" }] : []));
216
+ formatBytes(bytes) {
217
+ return bytes.toLocaleString('en-US');
218
+ }
219
+ formatKb(bytes) {
220
+ const kb = bytes / 1024;
221
+ return kb < 10 ? kb.toFixed(1) : Math.round(kb).toString();
222
+ }
223
+ chipClass(imp) {
224
+ if (imp.startsWith('@angular/'))
225
+ return 'angular';
226
+ if (imp.startsWith('rxjs'))
227
+ return 'rxjs';
228
+ return '';
229
+ }
230
+ static ɵfac = function BundleSectionComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || BundleSectionComponent)(); };
231
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: BundleSectionComponent, selectors: [["perf-bundle-section"]], inputs: { metrics: [1, "metrics"], warnKb: [1, "warnKb"], critKb: [1, "critKb"] }, decls: 2, vars: 1, consts: [[1, "prism-perf-empty"], [1, "prism-perf-bundle-hero"], [1, "prism-perf-bundle-metric"], [1, "prism-perf-label"], [1, "prism-perf-bundle-value"], [1, "prism-perf-bundle-unit"], [1, "prism-perf-sub"], [1, "prism-perf-bundle-section"], [1, "prism-perf-size-bars"], [1, "prism-perf-size-bar-row"], [1, "prism-perf-size-bar-label"], [1, "prism-perf-size-bar-track"], [1, "prism-perf-size-bar-fill", 2, "background", "linear-gradient(90deg, #a78bfa, #818cf8)"], [1, "prism-perf-size-bar-val", 2, "color", "#a78bfa"], [1, "prism-perf-size-bar-fill", 2, "background", "rgba(167,139,250,0.45)"], [1, "prism-perf-size-bar-val", 2, "color", "#8476a2"], [1, "prism-perf-threshold-legend"], [2, "color", "var(--perf-warn, #fbbf24)"], [2, "color", "var(--perf-crit, #f87171)"], [1, "prism-perf-bundle-section", 2, "border-bottom", "none"], [1, "prism-perf-depth-gauge"], [1, "prism-perf-depth-label"], [1, "prism-perf-depth-steps"], [1, "prism-perf-depth-step", 3, "filled", "deep"], [1, "prism-perf-depth-val"], [2, "margin-top", "14px"], [1, "prism-perf-import-list"], [1, "prism-perf-import-chip", 3, "class"], [1, "prism-perf-import-chip"], [1, "prism-perf-depth-step"], [1, "prism-perf-empty-icon"], [1, "prism-perf-empty-text"], [1, "prism-perf-empty-hint"]], template: function BundleSectionComponent_Template(rf, ctx) { if (rf & 1) {
232
+ i0.ɵɵconditionalCreate(0, BundleSectionComponent_Conditional_0_Template, 79, 19)(1, BundleSectionComponent_Conditional_1_Template, 7, 0, "div", 0);
233
+ } if (rf & 2) {
234
+ let tmp_0_0;
235
+ i0.ɵɵconditional((tmp_0_0 = ctx.metrics()) ? 0 : 1, tmp_0_0);
236
+ } }, styles: [".prism-perf-bundle-hero[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n }\n .prism-perf-bundle-metric[_ngcontent-%COMP%] {\n padding: 14px 16px;\n border-right: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n }\n .prism-perf-bundle-metric[_ngcontent-%COMP%]:last-child { border-right: none; }\n .prism-perf-bundle-value[_ngcontent-%COMP%] {\n font-size: 22px;\n font-family: var(--prism-font-mono, monospace);\n font-weight: 600;\n color: var(--prism-text, #ede9f8);\n line-height: 1;\n margin-top: 5px;\n }\n .prism-perf-bundle-unit[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 400;\n color: var(--prism-text-muted, #8476a2);\n margin-left: 3px;\n }\n .prism-perf-label[_ngcontent-%COMP%] {\n font-size: 9px;\n font-weight: 600;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--prism-text-ghost, #6a5d87);\n margin-bottom: 5px;\n }\n .prism-perf-sub[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--prism-text-ghost, #6a5d87);\n margin-top: 3px;\n font-family: var(--prism-font-mono, monospace);\n }\n .prism-perf-bundle-section[_ngcontent-%COMP%] {\n padding: 14px 16px;\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n }\n .prism-perf-size-bars[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 7px;\n margin-top: 10px;\n }\n .prism-perf-size-bar-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .prism-perf-size-bar-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--prism-text-muted, #8476a2);\n width: 90px;\n flex-shrink: 0;\n font-family: var(--prism-font-mono, monospace);\n }\n .prism-perf-size-bar-track[_ngcontent-%COMP%] {\n flex: 1;\n height: 6px;\n background: rgba(255,255,255,0.05);\n border-radius: 3px;\n overflow: hidden;\n }\n .prism-perf-size-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 3px;\n }\n .prism-perf-size-bar-val[_ngcontent-%COMP%] {\n font-size: 11px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-text-2, #b0a6c8);\n width: 58px;\n text-align: right;\n flex-shrink: 0;\n }\n .prism-perf-threshold-legend[_ngcontent-%COMP%] {\n margin-top: 10px;\n font-size: 10px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-text-ghost, #6a5d87);\n }\n .prism-perf-depth-gauge[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-top: 10px;\n }\n .prism-perf-depth-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--prism-text-muted, #8476a2);\n width: 90px;\n flex-shrink: 0;\n font-family: var(--prism-font-mono, monospace);\n }\n .prism-perf-depth-steps[_ngcontent-%COMP%] {\n display: flex;\n gap: 3px;\n flex: 1;\n }\n .prism-perf-depth-step[_ngcontent-%COMP%] {\n flex: 1;\n height: 6px;\n border-radius: 2px;\n background: rgba(255,255,255,0.05);\n }\n .prism-perf-depth-step.filled[_ngcontent-%COMP%] {\n background: var(--prism-primary, #a78bfa);\n opacity: 0.8;\n }\n .prism-perf-depth-step.deep[_ngcontent-%COMP%] {\n background: var(--perf-warn, #fbbf24);\n opacity: 0.5;\n }\n .prism-perf-depth-val[_ngcontent-%COMP%] {\n font-size: 11px;\n font-family: var(--prism-font-mono, monospace);\n width: 58px;\n text-align: right;\n flex-shrink: 0;\n }\n .prism-perf-import-list[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 5px;\n margin-top: 8px;\n }\n .prism-perf-import-chip[_ngcontent-%COMP%] {\n padding: 2px 8px;\n border-radius: 3px;\n background: rgba(255,255,255,0.04);\n border: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n font-size: 10px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-text-muted, #8476a2);\n }\n .prism-perf-import-chip.angular[_ngcontent-%COMP%] {\n color: var(--prism-primary, #a78bfa);\n border-color: color-mix(in srgb, var(--prism-primary, #a78bfa) 25%, transparent);\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 8%, transparent);\n }\n .prism-perf-import-chip.rxjs[_ngcontent-%COMP%] {\n color: #67e8f9;\n border-color: color-mix(in srgb, #67e8f9 25%, transparent);\n background: color-mix(in srgb, #67e8f9 8%, transparent);\n }\n .prism-perf-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .prism-perf-empty-icon[_ngcontent-%COMP%] { font-size: 28px; opacity: 0.2; }\n .prism-perf-empty-text[_ngcontent-%COMP%] { font-size: 12px; color: var(--prism-text-ghost, #6a5d87); }\n .prism-perf-empty-hint[_ngcontent-%COMP%] { font-size: 11px; color: var(--prism-text-ghost, #6a5d87); opacity: 0.6; }"], changeDetection: 0 });
237
+ }
238
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BundleSectionComponent, [{
239
+ type: Component,
240
+ args: [{ selector: 'perf-bundle-section', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
241
+ @if (metrics(); as m) {
242
+ <div class="prism-perf-bundle-hero">
243
+ <div class="prism-perf-bundle-metric">
244
+ <div class="prism-perf-label">Source Size</div>
245
+ <div class="prism-perf-bundle-value">{{ formatKb(m.sourceSize) }}<span class="prism-perf-bundle-unit">KB</span></div>
246
+ <div class="prism-perf-sub">{{ formatBytes(m.sourceSize) }} bytes</div>
247
+ </div>
248
+ <div class="prism-perf-bundle-metric">
249
+ <div class="prism-perf-label">Gzip Estimate</div>
250
+ <div class="prism-perf-bundle-value">{{ formatKb(m.gzipEstimate) }}<span class="prism-perf-bundle-unit">KB</span></div>
251
+ <div class="prism-perf-sub">~{{ gzipPercent() }}% of source</div>
252
+ </div>
253
+ <div class="prism-perf-bundle-metric">
254
+ <div class="prism-perf-label">Direct Imports</div>
255
+ <div class="prism-perf-bundle-value">{{ m.directImports }}</div>
256
+ <div class="prism-perf-sub">top-level only</div>
257
+ </div>
258
+ <div class="prism-perf-bundle-metric">
259
+ <div class="prism-perf-label">Tree Depth</div>
260
+ <div class="prism-perf-bundle-value">{{ m.treeDepth }}<span class="prism-perf-bundle-unit">/ 10</span></div>
261
+ <div class="prism-perf-sub">import levels</div>
262
+ </div>
263
+ </div>
264
+
265
+ <div class="prism-perf-bundle-section">
266
+ <div class="prism-perf-label">Size Breakdown</div>
267
+ <div class="prism-perf-size-bars">
268
+ <div class="prism-perf-size-bar-row">
269
+ <span class="prism-perf-size-bar-label">source</span>
270
+ <div class="prism-perf-size-bar-track">
271
+ <div class="prism-perf-size-bar-fill" [style.width.%]="sourceBarPercent()" style="background: linear-gradient(90deg, #a78bfa, #818cf8)"></div>
272
+ </div>
273
+ <span class="prism-perf-size-bar-val" style="color:#a78bfa">{{ formatKb(m.sourceSize) }} KB</span>
274
+ </div>
275
+ <div class="prism-perf-size-bar-row">
276
+ <span class="prism-perf-size-bar-label">gzip est.</span>
277
+ <div class="prism-perf-size-bar-track">
278
+ <div class="prism-perf-size-bar-fill" [style.width.%]="gzipBarPercent()" style="background: rgba(167,139,250,0.45)"></div>
279
+ </div>
280
+ <span class="prism-perf-size-bar-val" style="color:#8476a2">{{ formatKb(m.gzipEstimate) }} KB</span>
281
+ </div>
282
+ </div>
283
+ <div class="prism-perf-threshold-legend">
284
+ <span>Thresholds: <span style="color:var(--perf-warn, #fbbf24)">warn &ge; {{ warnKb() }} KB</span> · <span style="color:var(--perf-crit, #f87171)">crit &ge; {{ critKb() }} KB</span></span>
285
+ </div>
286
+ </div>
287
+
288
+ <div class="prism-perf-bundle-section" style="border-bottom:none">
289
+ <div class="prism-perf-label">Import Tree Depth</div>
290
+ <div class="prism-perf-depth-gauge">
291
+ <span class="prism-perf-depth-label">depth</span>
292
+ <div class="prism-perf-depth-steps">
293
+ @for (step of depthSteps(); track step.index) {
294
+ <div class="prism-perf-depth-step" [class.filled]="step.filled" [class.deep]="step.deep"></div>
295
+ }
296
+ </div>
297
+ <span class="prism-perf-depth-val" [style.color]="m.treeDepth >= 4 ? 'var(--perf-warn, #fbbf24)' : 'var(--prism-primary, #a78bfa)'">{{ m.treeDepth }} / 10</span>
298
+ </div>
299
+
300
+ <div style="margin-top:14px">
301
+ <div class="prism-perf-label">Direct Imports ({{ m.directImports }})</div>
302
+ <div class="prism-perf-import-list">
303
+ @for (imp of visibleImports(); track imp) {
304
+ <span class="prism-perf-import-chip" [class]="chipClass(imp)">{{ imp }}</span>
305
+ }
306
+ @if (hiddenImportCount() > 0) {
307
+ <span class="prism-perf-import-chip">+{{ hiddenImportCount() }} more</span>
308
+ }
309
+ </div>
310
+ </div>
311
+ </div>
312
+ } @else {
313
+ <div class="prism-perf-empty">
314
+ <div class="prism-perf-empty-icon">&#128230;</div>
315
+ <div class="prism-perf-empty-text">No bundle data available</div>
316
+ <div class="prism-perf-empty-hint">Bundle metrics are collected at build time</div>
317
+ </div>
318
+ }
319
+ `, styles: ["\n .prism-perf-bundle-hero {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n }\n .prism-perf-bundle-metric {\n padding: 14px 16px;\n border-right: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n }\n .prism-perf-bundle-metric:last-child { border-right: none; }\n .prism-perf-bundle-value {\n font-size: 22px;\n font-family: var(--prism-font-mono, monospace);\n font-weight: 600;\n color: var(--prism-text, #ede9f8);\n line-height: 1;\n margin-top: 5px;\n }\n .prism-perf-bundle-unit {\n font-size: 11px;\n font-weight: 400;\n color: var(--prism-text-muted, #8476a2);\n margin-left: 3px;\n }\n .prism-perf-label {\n font-size: 9px;\n font-weight: 600;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--prism-text-ghost, #6a5d87);\n margin-bottom: 5px;\n }\n .prism-perf-sub {\n font-size: 10px;\n color: var(--prism-text-ghost, #6a5d87);\n margin-top: 3px;\n font-family: var(--prism-font-mono, monospace);\n }\n .prism-perf-bundle-section {\n padding: 14px 16px;\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n }\n .prism-perf-size-bars {\n display: flex;\n flex-direction: column;\n gap: 7px;\n margin-top: 10px;\n }\n .prism-perf-size-bar-row {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .prism-perf-size-bar-label {\n font-size: 11px;\n color: var(--prism-text-muted, #8476a2);\n width: 90px;\n flex-shrink: 0;\n font-family: var(--prism-font-mono, monospace);\n }\n .prism-perf-size-bar-track {\n flex: 1;\n height: 6px;\n background: rgba(255,255,255,0.05);\n border-radius: 3px;\n overflow: hidden;\n }\n .prism-perf-size-bar-fill {\n height: 100%;\n border-radius: 3px;\n }\n .prism-perf-size-bar-val {\n font-size: 11px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-text-2, #b0a6c8);\n width: 58px;\n text-align: right;\n flex-shrink: 0;\n }\n .prism-perf-threshold-legend {\n margin-top: 10px;\n font-size: 10px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-text-ghost, #6a5d87);\n }\n .prism-perf-depth-gauge {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-top: 10px;\n }\n .prism-perf-depth-label {\n font-size: 11px;\n color: var(--prism-text-muted, #8476a2);\n width: 90px;\n flex-shrink: 0;\n font-family: var(--prism-font-mono, monospace);\n }\n .prism-perf-depth-steps {\n display: flex;\n gap: 3px;\n flex: 1;\n }\n .prism-perf-depth-step {\n flex: 1;\n height: 6px;\n border-radius: 2px;\n background: rgba(255,255,255,0.05);\n }\n .prism-perf-depth-step.filled {\n background: var(--prism-primary, #a78bfa);\n opacity: 0.8;\n }\n .prism-perf-depth-step.deep {\n background: var(--perf-warn, #fbbf24);\n opacity: 0.5;\n }\n .prism-perf-depth-val {\n font-size: 11px;\n font-family: var(--prism-font-mono, monospace);\n width: 58px;\n text-align: right;\n flex-shrink: 0;\n }\n .prism-perf-import-list {\n display: flex;\n flex-wrap: wrap;\n gap: 5px;\n margin-top: 8px;\n }\n .prism-perf-import-chip {\n padding: 2px 8px;\n border-radius: 3px;\n background: rgba(255,255,255,0.04);\n border: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n font-size: 10px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-text-muted, #8476a2);\n }\n .prism-perf-import-chip.angular {\n color: var(--prism-primary, #a78bfa);\n border-color: color-mix(in srgb, var(--prism-primary, #a78bfa) 25%, transparent);\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 8%, transparent);\n }\n .prism-perf-import-chip.rxjs {\n color: #67e8f9;\n border-color: color-mix(in srgb, #67e8f9 25%, transparent);\n background: color-mix(in srgb, #67e8f9 8%, transparent);\n }\n .prism-perf-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .prism-perf-empty-icon { font-size: 28px; opacity: 0.2; }\n .prism-perf-empty-text { font-size: 12px; color: var(--prism-text-ghost, #6a5d87); }\n .prism-perf-empty-hint { font-size: 11px; color: var(--prism-text-ghost, #6a5d87); opacity: 0.6; }\n "] }]
320
+ }], null, { metrics: [{ type: i0.Input, args: [{ isSignal: true, alias: "metrics", required: false }] }], warnKb: [{ type: i0.Input, args: [{ isSignal: true, alias: "warnKb", required: false }] }], critKb: [{ type: i0.Input, args: [{ isSignal: true, alias: "critKb", required: false }] }] }); })();
321
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(BundleSectionComponent, { className: "BundleSectionComponent", filePath: "bundle/bundle-section.component.ts", lineNumber: 252 }); })();
@@ -0,0 +1,5 @@
1
+ export { perfPlugin } from './perf-plugin.browser.js';
2
+ export { PerfPanelComponent } from './perf-panel.component.js';
3
+ export { PERF_PLUGIN_CONFIG } from './perf-config.token.js';
4
+ export type { PerfPluginOptions, PerfThresholds, BundleMetrics } from './perf.types.js';
5
+ //# sourceMappingURL=index.browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { perfPlugin } from './perf-plugin.browser.js';
2
+ export { PerfPanelComponent } from './perf-panel.component.js';
3
+ export { PERF_PLUGIN_CONFIG } from './perf-config.token.js';
@@ -0,0 +1,5 @@
1
+ export { perfPlugin } from './perf-plugin.js';
2
+ export { PerfPanelComponent } from './perf-panel.component.js';
3
+ export { PERF_PLUGIN_CONFIG } from './perf-config.token.js';
4
+ export type { PerfPluginOptions, PerfThresholds, BundleMetrics } from './perf.types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { perfPlugin } from './perf-plugin.js';
2
+ export { PerfPanelComponent } from './perf-panel.component.js';
3
+ export { PERF_PLUGIN_CONFIG } from './perf-config.token.js';
@@ -0,0 +1,14 @@
1
+ import type { PerfMemoryService } from './perf-memory.service.js';
2
+ import * as i0 from "@angular/core";
3
+ export declare class MemorySectionComponent {
4
+ readonly memoryService: import("@angular/core").InputSignal<PerfMemoryService>;
5
+ private readonly maxBytes;
6
+ readonly deltaText: import("@angular/core").Signal<string>;
7
+ readonly residualText: import("@angular/core").Signal<string>;
8
+ readonly destroyClass: import("@angular/core").Signal<"" | "delta-ok" | "delta-neg">;
9
+ barPercent(bytes: number | undefined): number;
10
+ formatMb(bytes: number | null | undefined): string;
11
+ static ɵfac: i0.ɵɵFactoryDeclaration<MemorySectionComponent, never>;
12
+ static ɵcmp: i0.ɵɵComponentDeclaration<MemorySectionComponent, "perf-memory-section", never, { "memoryService": { "alias": "memoryService"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
13
+ }
14
+ //# sourceMappingURL=memory-section.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-section.component.d.ts","sourceRoot":"","sources":["../../src/memory/memory-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;;AAElE,qBA4Na,sBAAsB;IACjC,QAAQ,CAAC,aAAa,yDAAuC;IAE7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAQtB;IAEH,QAAQ,CAAC,SAAS,yCAIf;IAEH,QAAQ,CAAC,YAAY,yCAIlB;IAEH,QAAQ,CAAC,YAAY,gEAKlB;IAEH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM;IAK7C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM;yCArCvC,sBAAsB;2CAAtB,sBAAsB;CAyClC"}