performance-budget-enforcer 1.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.
- package/.github/workflows/performance.yml +41 -0
- package/README.md +68 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +176 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/github/reporter.d.ts +16 -0
- package/dist/github/reporter.d.ts.map +1 -0
- package/dist/github/reporter.js +84 -0
- package/dist/github/reporter.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/analyzer.d.ts +21 -0
- package/dist/lib/analyzer.d.ts.map +1 -0
- package/dist/lib/analyzer.js +240 -0
- package/dist/lib/analyzer.js.map +1 -0
- package/dist/lib/budget.d.ts +11 -0
- package/dist/lib/budget.d.ts.map +1 -0
- package/dist/lib/budget.js +131 -0
- package/dist/lib/budget.js.map +1 -0
- package/dist/lib/types.d.ts +56 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +3 -0
- package/dist/lib/types.js.map +1 -0
- package/package.json +39 -0
- package/perf-budget.example.json +10 -0
- package/perf-budget.json +10 -0
- package/src/cli/index.ts +161 -0
- package/src/github/reporter.ts +102 -0
- package/src/index.ts +4 -0
- package/src/lib/analyzer.ts +237 -0
- package/src/lib/budget.ts +145 -0
- package/src/lib/types.ts +63 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: Performance Budget Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
perf-budget:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Setup Node.js
|
|
17
|
+
uses: actions/setup-node@v4
|
|
18
|
+
with:
|
|
19
|
+
node-version: '20'
|
|
20
|
+
cache: 'npm'
|
|
21
|
+
|
|
22
|
+
- name: Install dependencies
|
|
23
|
+
run: npm ci
|
|
24
|
+
|
|
25
|
+
- name: Build
|
|
26
|
+
run: npm run build
|
|
27
|
+
|
|
28
|
+
- name: Run performance budget check
|
|
29
|
+
run: npx perf-budget check --dir .next
|
|
30
|
+
env:
|
|
31
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
32
|
+
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
33
|
+
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
34
|
+
|
|
35
|
+
- name: Upload baseline
|
|
36
|
+
if: github.ref == 'refs/heads/main'
|
|
37
|
+
uses: actions/upload-artifact@v4
|
|
38
|
+
with:
|
|
39
|
+
name: bundle-analysis
|
|
40
|
+
path: bundle-analysis.json
|
|
41
|
+
retention-days: 30
|
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Performance Budget Enforcer
|
|
2
|
+
|
|
3
|
+
A CI plugin that scans React/Next.js builds, detects bundle size regressions, and fails CI if performance budgets are exceeded.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔍 Analyzes bundle sizes from Webpack, Vite, and Next.js builds
|
|
8
|
+
- 📊 Identifies which dependencies caused bloat
|
|
9
|
+
- 🚫 Fails CI when budgets are exceeded
|
|
10
|
+
- 📈 Compares against baseline for regression detection
|
|
11
|
+
- 💬 Posts PR comments with detailed reports
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install performance-budget-enforcer --save-dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### Initialize config
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx perf-budget init
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This creates a `perf-budget.json` config file.
|
|
28
|
+
|
|
29
|
+
### Analyze bundles
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx perf-budget analyze --dir ./build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Check budget
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx perf-budget check --dir ./build
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
Create `perf-budget.json` in your project root:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"maxTotalSize": 500000,
|
|
48
|
+
"maxGzippedSize": 150000,
|
|
49
|
+
"maxChunkSize": 250000,
|
|
50
|
+
"maxDependencySize": 100000,
|
|
51
|
+
"thresholds": {
|
|
52
|
+
"warning": 10,
|
|
53
|
+
"error": 20
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Sizes are in bytes. The `thresholds` define regression detection - if bundle grows more than 20% vs baseline, it will error.
|
|
59
|
+
|
|
60
|
+
## GitHub Actions Integration
|
|
61
|
+
|
|
62
|
+
See `.github/workflows/performance.yml` for CI setup.
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
- `perf-budget analyze` - Analyze bundle sizes
|
|
67
|
+
- `perf-budget check` - Check against budget
|
|
68
|
+
- `perf-budget init` - Create config file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const ora_1 = __importDefault(require("ora"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const analyzer_1 = require("../lib/analyzer");
|
|
46
|
+
const budget_1 = require("../lib/budget");
|
|
47
|
+
const reporter_1 = require("../github/reporter");
|
|
48
|
+
const program = new commander_1.Command();
|
|
49
|
+
program
|
|
50
|
+
.name('perf-budget')
|
|
51
|
+
.description('Frontend Performance Budget Enforcer - CI Plugin for React/Next.js')
|
|
52
|
+
.version('1.0.0');
|
|
53
|
+
program
|
|
54
|
+
.command('analyze')
|
|
55
|
+
.description('Analyze bundle sizes')
|
|
56
|
+
.option('-d, --dir <path>', 'Build directory', './build')
|
|
57
|
+
.option('-o, --output <path>', 'Output JSON file')
|
|
58
|
+
.action(async (options) => {
|
|
59
|
+
const spinner = (0, ora_1.default)('Analyzing bundle...').start();
|
|
60
|
+
try {
|
|
61
|
+
const buildDir = path.resolve(process.cwd(), options.dir);
|
|
62
|
+
if (!fs.existsSync(buildDir)) {
|
|
63
|
+
spinner.fail(`Build directory not found: ${buildDir}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
const analyzer = new analyzer_1.BundleAnalyzer(buildDir);
|
|
67
|
+
const analysis = await analyzer.analyze();
|
|
68
|
+
spinner.succeed(`Analyzed ${analysis.chunks.length} chunks`);
|
|
69
|
+
console.log('\n📊 Bundle Analysis');
|
|
70
|
+
console.log('─'.repeat(50));
|
|
71
|
+
console.log(`Total Size: ${(0, analyzer_1.formatSize)(analysis.totalSize)}`);
|
|
72
|
+
console.log(`Gzipped: ${(0, analyzer_1.formatSize)(analysis.totalGzipped)}`);
|
|
73
|
+
console.log(`Chunks: ${analysis.chunks.length}`);
|
|
74
|
+
console.log(`Dependencies: ${analysis.dependencies.length}`);
|
|
75
|
+
console.log('─'.repeat(50));
|
|
76
|
+
if (analysis.dependencies.length > 0) {
|
|
77
|
+
console.log('\n🔍 Top Dependencies:');
|
|
78
|
+
for (const dep of analysis.dependencies.slice(0, 10)) {
|
|
79
|
+
console.log(` ${dep.name}: ${(0, analyzer_1.formatSize)(dep.size)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (options.output) {
|
|
83
|
+
const outputPath = path.resolve(process.cwd(), options.output);
|
|
84
|
+
await (0, analyzer_1.saveAnalysis)(analysis, outputPath);
|
|
85
|
+
console.log(chalk_1.default.green(`\n✅ Results saved to ${outputPath}`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
spinner.fail(`Analysis failed: ${error.message}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
program
|
|
94
|
+
.command('check')
|
|
95
|
+
.description('Check bundle against performance budget')
|
|
96
|
+
.option('-d, --dir <path>', 'Build directory', './build')
|
|
97
|
+
.option('-c, --config <path>', 'Budget config file', './perf-budget.json')
|
|
98
|
+
.option('-b, --baseline <path>', 'Baseline analysis file')
|
|
99
|
+
.option('--fail', 'Exit with code 1 if budget exceeded', true)
|
|
100
|
+
.action(async (options) => {
|
|
101
|
+
const spinner = (0, ora_1.default)('Checking budget...').start();
|
|
102
|
+
try {
|
|
103
|
+
const buildDir = path.resolve(process.cwd(), options.dir);
|
|
104
|
+
const configPath = path.resolve(process.cwd(), options.config);
|
|
105
|
+
if (!fs.existsSync(buildDir)) {
|
|
106
|
+
spinner.fail(`Build directory not found: ${buildDir}`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
let config;
|
|
110
|
+
if (fs.existsSync(configPath)) {
|
|
111
|
+
config = (0, budget_1.loadConfig)(configPath);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
config = {
|
|
115
|
+
maxTotalSize: 500 * 1024,
|
|
116
|
+
maxGzippedSize: 150 * 1024,
|
|
117
|
+
maxChunkSize: 250 * 1024,
|
|
118
|
+
thresholds: { warning: 10, error: 20 }
|
|
119
|
+
};
|
|
120
|
+
console.log(chalk_1.default.yellow(`Using default budget config. Create ${options.config} to customize.`));
|
|
121
|
+
}
|
|
122
|
+
let baseline;
|
|
123
|
+
if (options.baseline) {
|
|
124
|
+
const baselinePath = path.resolve(process.cwd(), options.baseline);
|
|
125
|
+
if (fs.existsSync(baselinePath)) {
|
|
126
|
+
baseline = JSON.parse(fs.readFileSync(baselinePath, 'utf-8'));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const analyzer = new analyzer_1.BundleAnalyzer(buildDir);
|
|
130
|
+
const analysis = await analyzer.analyze();
|
|
131
|
+
const checker = new budget_1.BudgetChecker(config, baseline);
|
|
132
|
+
const result = checker.check(analysis);
|
|
133
|
+
budget_1.BudgetChecker.printResults(result);
|
|
134
|
+
if (!result.passed) {
|
|
135
|
+
const githubConfig = (0, reporter_1.getGitHubEnv)();
|
|
136
|
+
if (githubConfig) {
|
|
137
|
+
const reporter = new reporter_1.GitHubReporter(githubConfig);
|
|
138
|
+
await reporter.report(result);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!result.passed && options.fail) {
|
|
142
|
+
spinner.fail('Budget exceeded!');
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
spinner.succeed('Budget check complete');
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
spinner.fail(`Check failed: ${error.message}`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
program
|
|
153
|
+
.command('init')
|
|
154
|
+
.description('Initialize config file')
|
|
155
|
+
.action(() => {
|
|
156
|
+
const configPath = path.resolve(process.cwd(), 'perf-budget.json');
|
|
157
|
+
if (fs.existsSync(configPath)) {
|
|
158
|
+
console.log(chalk_1.default.yellow('Config already exists!'));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const defaultConfig = {
|
|
162
|
+
maxTotalSize: 500000,
|
|
163
|
+
maxGzippedSize: 150000,
|
|
164
|
+
maxChunkSize: 250000,
|
|
165
|
+
maxDependencySize: 100000,
|
|
166
|
+
thresholds: {
|
|
167
|
+
warning: 10,
|
|
168
|
+
error: 20
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
172
|
+
console.log(chalk_1.default.green(`✅ Created ${configPath}`));
|
|
173
|
+
console.log('\nAdjust the values in the config file to match your requirements.');
|
|
174
|
+
});
|
|
175
|
+
program.parse(process.argv);
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BudgetResult } from '../lib/types';
|
|
2
|
+
export interface GitHubConfig {
|
|
3
|
+
token: string;
|
|
4
|
+
owner: string;
|
|
5
|
+
repo: string;
|
|
6
|
+
prNumber?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class GitHubReporter {
|
|
9
|
+
private config;
|
|
10
|
+
constructor(config: GitHubConfig);
|
|
11
|
+
report(result: BudgetResult): Promise<void>;
|
|
12
|
+
private formatComment;
|
|
13
|
+
private postComment;
|
|
14
|
+
}
|
|
15
|
+
export declare function getGitHubEnv(): GitHubConfig | null;
|
|
16
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../src/github/reporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,EAAE,YAAY;IAI1B,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAUjD,OAAO,CAAC,aAAa;YAuBP,WAAW;CAgC1B;AAED,wBAAgB,YAAY,IAAI,YAAY,GAAG,IAAI,CAiBlD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitHubReporter = void 0;
|
|
4
|
+
exports.getGitHubEnv = getGitHubEnv;
|
|
5
|
+
const analyzer_1 = require("../lib/analyzer");
|
|
6
|
+
class GitHubReporter {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
}
|
|
10
|
+
async report(result) {
|
|
11
|
+
const body = this.formatComment(result);
|
|
12
|
+
if (this.config.prNumber) {
|
|
13
|
+
await this.postComment(body);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
console.log(body);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
formatComment(result) {
|
|
20
|
+
const status = result.passed ? '✅ Passed' : '❌ Failed';
|
|
21
|
+
let comment = `## 📦 Bundle Size Report\n\n`;
|
|
22
|
+
comment += `**Status:** ${status}\n\n`;
|
|
23
|
+
comment += `| Metric | Size |\n`;
|
|
24
|
+
comment += `|--------|------|\n`;
|
|
25
|
+
comment += `| Total | ${(0, analyzer_1.formatSize)(result.totalSize)} |\n`;
|
|
26
|
+
comment += `| Gzipped | ${(0, analyzer_1.formatSize)(result.totalGzipped)} |\n\n`;
|
|
27
|
+
if (!result.passed && result.violations.length > 0) {
|
|
28
|
+
comment += `### ⚠️ Budget Violations\n\n`;
|
|
29
|
+
comment += `| Type | Name | Current | Limit | Over |\n`;
|
|
30
|
+
comment += `|------|------|---------|-------|------|\n`;
|
|
31
|
+
for (const v of result.violations) {
|
|
32
|
+
comment += `| ${v.type} | ${v.name} | ${(0, analyzer_1.formatSize)(v.current)} | ${(0, analyzer_1.formatSize)(v.limit)} | +${v.percentageOver.toFixed(1)}% |\n`;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return comment;
|
|
36
|
+
}
|
|
37
|
+
async postComment(body) {
|
|
38
|
+
const { execSync } = require('child_process');
|
|
39
|
+
try {
|
|
40
|
+
const query = `
|
|
41
|
+
mutation($pullRequestId: ID!, $body: String!) {
|
|
42
|
+
addComment(input: { subjectId: $pullRequestId, body: $body }) {
|
|
43
|
+
commentEdge {
|
|
44
|
+
node {
|
|
45
|
+
id
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
const prIdQuery = `
|
|
52
|
+
query($owner: String!, $repo: String!, $prNumber: Int!) {
|
|
53
|
+
repository(owner: $owner, name: $repo) {
|
|
54
|
+
pullRequest(number: $prNumber) {
|
|
55
|
+
id
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
console.log(`Posting comment to PR #${this.config.prNumber}...`);
|
|
61
|
+
console.log(body);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('Failed to post GitHub comment:', error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.GitHubReporter = GitHubReporter;
|
|
69
|
+
function getGitHubEnv() {
|
|
70
|
+
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
71
|
+
const repo = process.env.GITHUB_REPOSITORY;
|
|
72
|
+
const prNumber = process.env.GITHUB_PR_NUMBER ? parseInt(process.env.GITHUB_PR_NUMBER) : undefined;
|
|
73
|
+
if (!token || !repo) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const [owner, repoName] = repo.split('/');
|
|
77
|
+
return {
|
|
78
|
+
token,
|
|
79
|
+
owner,
|
|
80
|
+
repo: repoName,
|
|
81
|
+
prNumber
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/github/reporter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { BundleAnalyzer, formatSize, saveAnalysis, loadAnalysis } from './lib/analyzer';
|
|
2
|
+
export { BudgetChecker, loadConfig } from './lib/budget';
|
|
3
|
+
export { GitHubReporter, getGitHubEnv } from './github/reporter';
|
|
4
|
+
export * from './lib/types';
|
|
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,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjE,cAAc,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.getGitHubEnv = exports.GitHubReporter = exports.loadConfig = exports.BudgetChecker = exports.loadAnalysis = exports.saveAnalysis = exports.formatSize = exports.BundleAnalyzer = void 0;
|
|
18
|
+
var analyzer_1 = require("./lib/analyzer");
|
|
19
|
+
Object.defineProperty(exports, "BundleAnalyzer", { enumerable: true, get: function () { return analyzer_1.BundleAnalyzer; } });
|
|
20
|
+
Object.defineProperty(exports, "formatSize", { enumerable: true, get: function () { return analyzer_1.formatSize; } });
|
|
21
|
+
Object.defineProperty(exports, "saveAnalysis", { enumerable: true, get: function () { return analyzer_1.saveAnalysis; } });
|
|
22
|
+
Object.defineProperty(exports, "loadAnalysis", { enumerable: true, get: function () { return analyzer_1.loadAnalysis; } });
|
|
23
|
+
var budget_1 = require("./lib/budget");
|
|
24
|
+
Object.defineProperty(exports, "BudgetChecker", { enumerable: true, get: function () { return budget_1.BudgetChecker; } });
|
|
25
|
+
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return budget_1.loadConfig; } });
|
|
26
|
+
var reporter_1 = require("./github/reporter");
|
|
27
|
+
Object.defineProperty(exports, "GitHubReporter", { enumerable: true, get: function () { return reporter_1.GitHubReporter; } });
|
|
28
|
+
Object.defineProperty(exports, "getGitHubEnv", { enumerable: true, get: function () { return reporter_1.getGitHubEnv; } });
|
|
29
|
+
__exportStar(require("./lib/types"), exports);
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAAwF;AAA/E,0GAAA,cAAc,OAAA;AAAE,sGAAA,UAAU,OAAA;AAAE,wGAAA,YAAY,OAAA;AAAE,wGAAA,YAAY,OAAA;AAC/D,uCAAyD;AAAhD,uGAAA,aAAa,OAAA;AAAE,oGAAA,UAAU,OAAA;AAClC,8CAAiE;AAAxD,0GAAA,cAAc,OAAA;AAAE,wGAAA,YAAY,OAAA;AACrC,8CAA4B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BundleAnalysis, BuildTool } from './types';
|
|
2
|
+
export declare class BundleAnalyzer {
|
|
3
|
+
private buildDir;
|
|
4
|
+
private buildTool;
|
|
5
|
+
constructor(buildDir: string);
|
|
6
|
+
private detectBuildTool;
|
|
7
|
+
getBuildTool(): BuildTool;
|
|
8
|
+
analyze(): Promise<BundleAnalysis>;
|
|
9
|
+
private analyzeNextJs;
|
|
10
|
+
private analyzeVite;
|
|
11
|
+
private analyzeWebpack;
|
|
12
|
+
private analyzeGeneric;
|
|
13
|
+
private findChunks;
|
|
14
|
+
private getAllFiles;
|
|
15
|
+
private estimateDependencies;
|
|
16
|
+
private parseWebpackStats;
|
|
17
|
+
}
|
|
18
|
+
export declare function formatSize(bytes: number): string;
|
|
19
|
+
export declare function saveAnalysis(analysis: BundleAnalysis, outputPath: string): Promise<void>;
|
|
20
|
+
export declare function loadAnalysis(inputPath: string): Promise<BundleAnalysis>;
|
|
21
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/lib/analyzer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAkB,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpE,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAwB;gBAE7B,QAAQ,EAAE,MAAM;IAK5B,OAAO,CAAC,eAAe;IAmBvB,YAAY,IAAI,SAAS;IAInB,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC;YAa1B,aAAa;YAmBb,WAAW;YAgBX,cAAc;YAWd,cAAc;YAed,UAAU;IAyBxB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,iBAAiB;CA2B1B;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAE7E"}
|