Skip to content

Commit 9080b92

Browse files
committed
fix: move code
1 parent f9f80d6 commit 9080b92

22 files changed

+1380
-797
lines changed

packages/plugin-bundle-stats/code-pushup.large-angular.config.ts

Lines changed: 148 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,138 @@
11
import bundleStatsPlugin, { type GroupingRule } from './src';
2+
import { BlacklistEntry } from './src/lib/runner/types';
3+
4+
const sharedHint =
5+
'Remove or conditionally load. Supported natively in modern browsers or replace with modern Angular architecture. See: https://blog.angular.io/zone-less-angular-explained-5fa951ce6f6e';
6+
7+
const blacklist: BlacklistEntry[] = [
8+
// 🧹 Remove or conditionally load. Supported natively in modern browsers or replace with modern Angular architecture.
9+
{
10+
pattern: '**/node_modules/core-js/**',
11+
hint: sharedHint,
12+
},
13+
{
14+
pattern: '**/node_modules/zone.js/**',
15+
hint: sharedHint,
16+
},
17+
{
18+
pattern: '**/node_modules/smoothscroll-polyfill/**',
19+
hint: sharedHint,
20+
},
21+
{
22+
pattern: '**/node_modules/web-animations-js/**',
23+
hint: sharedHint,
24+
},
25+
26+
// 🔁 Replace with modern alternatives
27+
{
28+
pattern: '**/node_modules/lodash/**',
29+
hint: 'Replace with `lodash-es` and use selective imports like `import isEmpty from "lodash-es/isEmpty"`.',
30+
},
31+
{
32+
pattern: '**/node_modules/underscore/**',
33+
hint: 'Replace with `lodash-es` or native utilities like `Object.keys`, `Array.prototype.map`, etc.',
34+
},
35+
{
36+
pattern: '**/node_modules/decimal.js/**',
37+
hint: 'Replace with native `BigInt` if full decimal precision isn’t required.',
38+
},
39+
{
40+
pattern: '**/node_modules/angular2-toaster/**',
41+
hint: 'Replace with actively maintained alternatives like `ngx-toastr` or `ngneat/hot-toast`.',
42+
},
43+
44+
// 🕒 Use lazy loading for heavy dependencies
45+
{
46+
pattern: '**/node_modules/socket.io-client/**',
47+
hint: 'Use lazy loading for real-time features. Replace with native WebSocket API or `ws` where possible.',
48+
},
49+
{
50+
pattern: '**/node_modules/launchdarkly-js-client-sdk/**',
51+
hint: 'Use lazy loading for feature flag SDKs to reduce initial bundle size.',
52+
},
53+
{
54+
pattern: '**/node_modules/@microsoft/**',
55+
hint: 'Use lazy loading for Microsoft SDKs due to their large size.',
56+
},
57+
{
58+
pattern: '**/node_modules/ngx-toastr/**',
59+
hint: 'Use lazy loading for notifications. Consider `ngneat/hot-toast` or `notiflix` as lighter alternatives.',
60+
},
61+
{
62+
pattern: '**/node_modules/ngx-scrollbar/**',
63+
hint: 'Use lazy loading for custom scrollbars. Replace with native CSS where possible.',
64+
},
65+
{
66+
pattern: '**/node_modules/@angular-slider/**',
67+
hint: 'Use lazy loading for sliders. Consider `ngx-slider-v2` or custom lightweight slider components.',
68+
},
69+
{
70+
pattern: '**/node_modules/howler/**',
71+
hint: 'Use lazy loading for audio libraries to reduce bundle size.',
72+
},
73+
{
74+
pattern: '**/node_modules/hls.js/**',
75+
hint: 'Use lazy loading for video streaming libraries like `hls.js`.',
76+
},
77+
{
78+
pattern: '**/node_modules/lottie-web/**',
79+
hint: 'Use lazy loading for animations. Consider `ngx-lottie` wrapper for Angular.',
80+
},
81+
{
82+
pattern: '**/node_modules/ngx-device-detector/**',
83+
hint: 'Use lazy loading for device detection logic or move detection server-side.',
84+
},
85+
{
86+
pattern: '**/node_modules/ua-parser-js/**',
87+
hint: 'Use lazy loading or replace with server-side detection if possible.',
88+
},
89+
90+
// 🚫 Exclude from production builds
91+
{
92+
pattern: '**/node_modules/@storybook/**',
93+
hint: 'Exclude Storybook packages from production bundles.',
94+
},
95+
{
96+
pattern: '**/node_modules/jest/**',
97+
hint: 'Exclude test frameworks like Jest from production builds.',
98+
},
99+
];
2100

3101
const nodeModulesGroup: GroupingRule[] = [
4-
// General node_modules group (processed third due to reverse order)
102+
// Angular packages (processed first)
5103
{
6-
patterns: ['**/node_modules/**', '**/node_modules/@*/**/*'],
7-
icon: '📦',
8-
maxDepth: 2,
104+
patterns: [
105+
'**/node_modules/@angular/**', // Catches @angular/core, @angular/common, @angular/router, etc.
106+
'**/node_modules/@ng*/**', // Catches @ngrx/store, @ngrx/effects, etc.#
107+
'**/node_modules/@ngx-*/**',
108+
'**/node_modules/@angular-*/**', // Catches @angular-slider, @angular-devkit, etc.
109+
'**/node_modules/@*angular*/**', // Catches @rx-angular, @push-based-angular, etc.
110+
],
111+
icon: '🅰️',
112+
numSegments: 3, // Shows @angular/router, @angular/common, @ngrx/store, etc.
9113
},
10-
// Non-scoped Angular packages (processed second due to reverse order)
114+
// Non-scoped Angular packages (processed second)
11115
{
12116
patterns: [
13117
'**/node_modules/ngx-*/**',
14118
'**/node_modules/ng-*/**',
15-
'**/node_modules/*angular*',
119+
'**/node_modules/*angular*/**', // Non-scoped packages containing "angular"
16120
],
17121
icon: '🅰️',
18-
maxDepth: 2, // Gets ngx-toastr, ng-bootstrap, etc.
122+
numSegments: 2, // Shows ngx-toastr, ng-bootstrap, etc.
19123
},
20-
// Scoped Angular packages (processed first due to reverse order, takes precedence)
124+
// All scoped packages (processed third) - no title allows auto-derivation of individual package names
21125
{
22-
patterns: ['**/node_modules/@angular/**', '**/node_modules/@ngrx/**'],
23-
icon: '🅰️',
24-
maxDepth: 3, // Gets @angular/router, @angular/common, @ngrx/store, etc.
126+
patterns: ['**/node_modules/@*/**'],
127+
icon: '📦',
128+
numSegments: 3, // Shows @rx-angular/state, @push-based/test-lib, @ngx-translate/core, etc.
129+
},
130+
// General node_modules group (processed last - catches everything else)
131+
{
132+
title: 'Node Modules',
133+
patterns: ['**/node_modules/**'],
134+
icon: '📦',
135+
numSegments: 1, // Shows individual unscoped packages
25136
},
26137
];
27138

@@ -192,7 +303,7 @@ const badGroups: GroupingRule[] = [
192303
],
193304
},
194305
{
195-
title: '📚 Documentation in Bundle',
306+
title: '🚨 📚 Documentation in Production',
196307
patterns: [
197308
'**/node_modules/**/demo/**',
198309
'**/node_modules/**/examples/**',
@@ -233,43 +344,50 @@ const config = {
233344
artefactsPath:
234345
'./packages/plugin-bundle-stats/mocks/fixtures/stats/angular-large.stats.json',
235346
selection: {
236-
excludeOutputs: ['**/*.map'], // Only exclude source maps as they're not part of runtime bundle
347+
excludeOutputs: ['**/*.map', '**/*.d.ts'], // Only exclude source maps as they're not part of runtime bundle
237348
},
238349
scoring: {
239350
penalty: {
240351
// Penalty for individual files that are too large
241352
// This highlights large files without hiding them
242-
artefactSize: [0, 3000000], // 0-3MB range - penalty for files > 3MB
243-
blacklist: [],
353+
artefactSize: [0, 3_000_000], // 0-3MB range - penalty for files > 3MB
354+
blacklist,
244355
},
245356
},
246-
insights: [...productGroups, ...badGroups, ...nodeModulesGroup],
357+
insights: [
358+
...productGroups, // Process product groups first for better specificity
359+
...badGroups, // Process bad/warning groups second
360+
...nodeModulesGroup, // Process general node_modules groups last
361+
],
247362
artefactTree: {
248-
groups: [...productGroups, ...badGroups, ...nodeModulesGroup],
363+
groups: [
364+
...productGroups, // Process product groups first for better specificity
365+
...nodeModulesGroup, // Process general node_modules groups last
366+
],
249367
pruning: {
250-
maxDepth: 5,
368+
maxChildren: 10,
369+
maxDepth: 3,
251370
minSize: 1_000, // Reduced from 50_000 to show smaller files (1KB threshold)
252371
},
253372
},
254373
audits: [
255-
/* {
374+
{
256375
title: 'All Files',
257376
description: 'All files in the bundle',
258377
selection: {
259-
includeOutputs: ['**\/*'],
378+
// Use specific includeOutputs instead of global include for better control
379+
include: ['**/*.js'],
380+
// Exclude patterns to filter out unwanted files
381+
excludeOutputs: [
382+
'**/*.map', // Source maps
383+
'**/*.d.ts', // TypeScript declarations
384+
],
260385
},
261386
scoring: {
262387
// Main bundle size threshold - warn when total exceeds 80MB
263-
totalSize: 80000000, // 80MB in bytes
388+
totalSize: 80_000_000, // 80MB in bytes
264389
},
265-
artefactTree: {
266-
pruning: {
267-
pathLength: 100,
268-
maxChildren: 20,
269-
maxDepth: 3,
270-
},
271-
},
272-
},*/
390+
},
273391
// Initial bundle size audit
274392
{
275393
title: 'Initial Bundle Size',
@@ -284,14 +402,7 @@ const config = {
284402
],
285403
},
286404
scoring: {
287-
totalSize: 80_000_000,
288-
},
289-
artefactTree: {
290-
groups: [
291-
...nodeModulesGroup,
292-
...badGroups,
293-
...productGroups, // Process product groups first for better specificity (last in array = first processed)
294-
],
405+
totalSize: 8_000_000,
295406
},
296407
},
297408
],

packages/plugin-bundle-stats/src/lib/normalize.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Audit } from '@code-pushup/models';
22
import { slugify } from '@code-pushup/utils';
33
import { formatBytes } from '@code-pushup/utils';
4-
import type { ScoringConfig } from './runner/audits/utils/scoring.js';
5-
import { normalizeSelectionOptions } from './runner/audits/utils/selection.js';
4+
import type { ScoringConfig } from './runner/audits/scoring.js';
5+
import { normalizeSelectionOptions } from './runner/audits/selection.js';
66
import type { BundleStatsConfig, MinMax } from './runner/types.js';
77
import type { BundleStatsOptions } from './types.js';
88

packages/plugin-bundle-stats/src/lib/runner/audits/audit-outputs.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { createDisplayValue } from '../utils.js';
55
import { createEmptyAudit } from '../utils.js';
66
import { createAuditOutputDetails } from './details/audit-details.js';
77
import { getIssues } from './details/issues.js';
8-
import { createBundleStatsScoring } from './utils/scoring.js';
9-
import { selectBundles } from './utils/selection.js';
8+
import { createBundleStatsScoring } from './scoring.js';
9+
import { selectBundles } from './selection.js';
1010

1111
/**
1212
* Calculates total bytes from unified stats tree. Aggregates byte counts across all artefacts.
@@ -24,7 +24,10 @@ export function createAuditOutput(
2424
): AuditOutput {
2525
const trees = Object.values(statsSlice);
2626
const totalBytes = calculateTotalBytes(statsSlice);
27+
28+
console.time('⚡ GET_ISSUES');
2729
const issues = getIssues(statsSlice, config);
30+
console.timeEnd('⚡ GET_ISSUES');
2831

2932
const calculateScore = createBundleStatsScoring({
3033
totalSize: config.scoring.totalSize,
@@ -48,12 +51,18 @@ export function generateAuditOutputs(
4851
configs: BundleStatsConfig[],
4952
): AuditOutput[] {
5053
return configs.map(config => {
54+
console.time(`🔍 SELECT_BUNDLES`);
5155
const filteredTree = selectBundles(bundleStatsTree, config.selection);
56+
console.timeEnd(`🔍 SELECT_BUNDLES`);
5257

5358
if (!filteredTree || Object.keys(filteredTree).length === 0) {
5459
return createEmptyAudit(config);
5560
}
5661

57-
return createAuditOutput(filteredTree, config);
62+
console.time(`📝 CREATE_AUDIT_OUTPUT - ${config.slug}`);
63+
const result = createAuditOutput(filteredTree, config);
64+
console.timeEnd(`📝 CREATE_AUDIT_OUTPUT - ${config.slug}`);
65+
66+
return result;
5867
});
5968
}

packages/plugin-bundle-stats/src/lib/runner/audits/audit-outputs.unit.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ describe('createAuditOutput', () => {
102102
scoring: {
103103
totalSize: [1000, 10000],
104104
penalty: {
105-
errorWeight: 0.5,
106-
warningWeight: 0.2,
107-
blacklist: ['main.js'],
105+
blacklist: [
106+
{
107+
pattern: 'main.js',
108+
hint: 'Main bundle should be optimized for size',
109+
},
110+
],
108111
},
109112
},
110113
},

packages/plugin-bundle-stats/src/lib/runner/audits/details/audit-details.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,21 @@ export function createAuditOutputDetails(
1818
};
1919

2020
if (config.insights && config.insights.length > 0) {
21+
console.time('📊 CREATE_INSIGHTS_TABLE');
2122
details.table = createInsightsTable(statsSlice, config.insights);
23+
console.timeEnd('📊 CREATE_INSIGHTS_TABLE');
2224
}
2325

2426
if (config.artefactTree) {
27+
console.time('🌳 CREATE_TREE');
2528
details.trees = [
2629
createTree(statsSlice, {
2730
title: config.slug,
2831
pruning: config.artefactTree.pruning ?? {},
2932
groups: config.artefactTree.groups ?? [],
3033
}),
3134
];
35+
console.timeEnd('🌳 CREATE_TREE');
3236
}
3337

3438
return details;

packages/plugin-bundle-stats/src/lib/runner/audits/details/utils/formatting.ts renamed to packages/plugin-bundle-stats/src/lib/runner/audits/details/formatting.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,19 @@ import {
44
findSegmentIndex,
55
normalizePathForMatching,
66
splitPathSegments,
7-
} from './grouping-engine';
7+
} from './grouping';
8+
9+
// Regex cache to avoid recreating the same regex patterns
10+
const REGEX_CACHE = new Map<string, RegExp>();
11+
12+
function getCachedRegex(pattern: string): RegExp {
13+
const cached = REGEX_CACHE.get(pattern);
14+
if (cached) return cached;
15+
16+
const regex = new RegExp(`${pattern}\\/([^\\/]+)`);
17+
REGEX_CACHE.set(pattern, regex);
18+
return regex;
19+
}
820

921
export function removeFileExtension(name: string): string {
1022
return name.replace(/\.(js|ts|jsx|tsx|css|scss|json)$/, '');
@@ -113,7 +125,7 @@ export function extractGroupKeyFromPattern(
113125
}
114126

115127
for (const segment of concreteSegments) {
116-
const regex = new RegExp(`${segment}\/([^\/]+)`);
128+
const regex = getCachedRegex(segment); // Use cached regex
117129
const match = filePath.match(regex) || normalizedPath.match(regex);
118130

119131
if (match?.[1]) {

0 commit comments

Comments
 (0)