performance-budget-enforcer 1.0.1 → 1.1.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/README.md CHANGED
@@ -9,6 +9,7 @@ A CI plugin that scans React/Next.js builds, detects bundle size regressions, an
9
9
  - 🚫 Fails CI when budgets are exceeded
10
10
  - 📈 Compares against baseline for regression detection
11
11
  - 💬 Posts PR comments with detailed reports
12
+ - 📢 Sends Slack notifications on budget failures
12
13
 
13
14
  ## Installation
14
15
 
@@ -29,18 +30,28 @@ This creates a `perf-budget.json` config file.
29
30
  ### Analyze bundles
30
31
 
31
32
  ```bash
32
- npx perf-budget analyze --dir ./build
33
+ npx perf-budget analyze --dir .next
33
34
  ```
34
35
 
35
36
  ### Check budget
36
37
 
37
38
  ```bash
38
- npx perf-budget check --dir ./build
39
+ npx perf-budget check --dir .next
40
+ ```
41
+
42
+ ### Regression detection with baseline
43
+
44
+ ```bash
45
+ # First run: save baseline
46
+ npx perf-budget analyze --dir .next -o baseline.json
47
+
48
+ # Later runs: compare against baseline
49
+ npx perf-budget check --dir .next --baseline baseline.json
39
50
  ```
40
51
 
41
52
  ## Configuration
42
53
 
43
- Create `perf-budget.json` in your project root:
54
+ Edit `perf-budget.json`:
44
55
 
45
56
  ```json
46
57
  {
@@ -55,14 +66,110 @@ Create `perf-budget.json` in your project root:
55
66
  }
56
67
  ```
57
68
 
58
- Sizes are in bytes. The `thresholds` define regression detection - if bundle grows more than 20% vs baseline, it will error.
69
+ - All sizes are in **bytes**
70
+ - `maxTotalSize`: Maximum total bundle size
71
+ - `maxGzippedSize`: Maximum gzipped size
72
+ - `maxChunkSize`: Maximum size for any single chunk
73
+ - `maxDependencySize`: Maximum size for any dependency
74
+ - `thresholds.error`: % growth vs baseline that triggers CI failure (default: 20%)
75
+
76
+ ## GitHub Integration
77
+
78
+ The tool automatically posts PR comments when budgets fail.
79
+
80
+ Set these environment variables in your CI:
81
+
82
+ ```yaml
83
+ - name: Performance Budget Check
84
+ run: npx perf-budget check --dir .next
85
+ env:
86
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
87
+ GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
88
+ GITHUB_REPOSITORY: ${{ github.repository }}
89
+ ```
90
+
91
+ ## Slack Integration
92
+
93
+ Get a webhook URL: https://api.slack.com/messaging/webhooks
94
+
95
+ Add environment variable:
96
+
97
+ ```yaml
98
+ - name: Performance Budget Check
99
+ run: npx perf-budget check --dir .next
100
+ env:
101
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
102
+ SLACK_CHANNEL: #performance # optional
103
+ ```
104
+
105
+ Optional env vars:
106
+ - `SLACK_WEBHOOK_URL` - Required webhook URL
107
+ - `SLACK_CHANNEL` - Override default channel
108
+ - `SLACK_USERNAME` - Custom username
109
+ - `SLACK_ICON_EMOJI` - Custom emoji (default: :package:)
59
110
 
60
111
  ## GitHub Actions Integration
61
112
 
62
- See `.github/workflows/performance.yml` for CI setup.
113
+ ```yaml
114
+ name: Performance Budget
115
+
116
+ on:
117
+ push:
118
+ branches: [main]
119
+ pull_request:
120
+ branches: [main]
121
+
122
+ jobs:
123
+ perf-budget:
124
+ runs-on: ubuntu-latest
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+
128
+ - uses: actions/setup-node@v4
129
+ with:
130
+ node-version: '20'
131
+ cache: 'npm'
132
+
133
+ - name: Install dependencies
134
+ run: npm ci
135
+
136
+ - name: Build
137
+ run: npm run build
138
+
139
+ - name: Download baseline
140
+ if: github.event_name == 'pull_request'
141
+ uses: actions/download-artifact@v4
142
+ with:
143
+ name: bundle-baseline
144
+ path: .
145
+ continue-on-error: true
146
+
147
+ - name: Performance Budget Check
148
+ run: npx perf-budget check --dir .next
149
+ env:
150
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
151
+ GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
152
+ GITHUB_REPOSITORY: ${{ github.repository }}
153
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
154
+
155
+ - name: Upload baseline
156
+ if: github.ref == 'refs/heads/main'
157
+ uses: actions/upload-artifact@v4
158
+ with:
159
+ name: bundle-baseline
160
+ path: baseline.json
161
+ ```
63
162
 
64
163
  ## Commands
65
164
 
66
- - `perf-budget analyze` - Analyze bundle sizes
67
- - `perf-budget check` - Check against budget
165
+ - `perf-budget analyze [options]` - Analyze bundle sizes
166
+ - `perf-budget check [options]` - Check against budget
68
167
  - `perf-budget init` - Create config file
168
+
169
+ ### Options
170
+
171
+ - `-d, --dir <path>` - Build directory (default: ./build)
172
+ - `-c, --config <path>` - Config file (default: ./perf-budget.json)
173
+ - `-b, --baseline <path>` - Baseline file for regression detection
174
+ - `-o, --output <path>` - Output JSON file
175
+ - `--fail` - Exit with code 1 if budget exceeded (default: true)
package/dist/cli/index.js CHANGED
@@ -45,11 +45,12 @@ const fs = __importStar(require("fs"));
45
45
  const analyzer_1 = require("../lib/analyzer");
46
46
  const budget_1 = require("../lib/budget");
47
47
  const reporter_1 = require("../github/reporter");
48
+ const slack_1 = require("../github/slack");
48
49
  const program = new commander_1.Command();
49
50
  program
50
51
  .name('perf-budget')
51
52
  .description('Frontend Performance Budget Enforcer - CI Plugin for React/Next.js')
52
- .version('1.0.0');
53
+ .version('1.1.0');
53
54
  program
54
55
  .command('analyze')
55
56
  .description('Analyze bundle sizes')
@@ -137,6 +138,11 @@ program
137
138
  const reporter = new reporter_1.GitHubReporter(githubConfig);
138
139
  await reporter.report(result);
139
140
  }
141
+ const slackConfig = (0, slack_1.getSlackEnv)();
142
+ if (slackConfig) {
143
+ const slackReporter = new slack_1.SlackReporter(slackConfig);
144
+ await slackReporter.report(result);
145
+ }
140
146
  }
141
147
  if (!result.passed && options.fail) {
142
148
  spinner.fail('Budget exceeded!');
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -1 +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"}
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;YAyBP,WAAW;CA4D1B;AAED,wBAAgB,YAAY,IAAI,YAAY,GAAG,IAAI,CAiBlD"}
@@ -32,36 +32,61 @@ class GitHubReporter {
32
32
  comment += `| ${v.type} | ${v.name} | ${(0, analyzer_1.formatSize)(v.current)} | ${(0, analyzer_1.formatSize)(v.limit)} | +${v.percentageOver.toFixed(1)}% |\n`;
33
33
  }
34
34
  }
35
+ comment += `\n---\n*Powered by [performance-budget-enforcer](https://github.com/performance-budget-enforcer)*`;
35
36
  return comment;
36
37
  }
37
38
  async postComment(body) {
38
39
  const { execSync } = require('child_process');
40
+ const prIdQuery = JSON.stringify({
41
+ query: `query($owner: String!, $repo: String!, $prNumber: Int!) {
42
+ repository(owner: $owner, name: $repo) {
43
+ pullRequest(number: $prNumber) {
44
+ id
45
+ }
46
+ }
47
+ }`,
48
+ variables: {
49
+ owner: this.config.owner,
50
+ repo: this.config.repo,
51
+ prNumber: this.config.prNumber
52
+ }
53
+ });
39
54
  try {
40
- const query = `
41
- mutation($pullRequestId: ID!, $body: String!) {
42
- addComment(input: { subjectId: $pullRequestId, body: $body }) {
55
+ const prIdCmd = `curl -s -X POST -H "Authorization: bearer ${this.config.token}" -H "Content-Type: application/json" -d '${prIdQuery}' https://api.github.com/graphql`;
56
+ const prIdResult = execSync(prIdCmd, { encoding: 'utf-8' });
57
+ const prIdData = JSON.parse(prIdResult);
58
+ if (!prIdData.data?.repository?.pullRequest?.id) {
59
+ console.log('Could not find PR ID. Make sure GITHUB_PR_NUMBER is set correctly.');
60
+ console.log(body);
61
+ return;
62
+ }
63
+ const prId = prIdData.data.repository.pullRequest.id;
64
+ const addCommentMutation = JSON.stringify({
65
+ query: `mutation($input: AddCommentInput!) {
66
+ addComment(input: $input) {
43
67
  commentEdge {
44
68
  node {
45
69
  id
70
+ body
46
71
  }
47
72
  }
48
73
  }
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);
74
+ }`,
75
+ variables: {
76
+ input: {
77
+ subjectId: prId,
78
+ body: body
79
+ }
80
+ }
81
+ });
82
+ const commentCmd = `curl -s -X POST -H "Authorization: bearer ${this.config.token}" -H "Content-Type: application/json" -d '${addCommentMutation}' https://api.github.com/graphql`;
83
+ execSync(commentCmd, { encoding: 'utf-8' });
84
+ console.log(`✅ Posted comment to PR #${this.config.prNumber}`);
62
85
  }
63
86
  catch (error) {
64
- console.error('Failed to post GitHub comment:', error);
87
+ console.error('Failed to post GitHub comment:', error.message);
88
+ console.log('Comment preview:');
89
+ console.log(body);
65
90
  }
66
91
  }
67
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/github/reporter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../src/github/reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { BudgetResult } from '../lib/types';
2
+ export interface SlackConfig {
3
+ webhookUrl: string;
4
+ channel?: string;
5
+ username?: string;
6
+ iconEmoji?: string;
7
+ }
8
+ export declare class SlackReporter {
9
+ private config;
10
+ constructor(config: SlackConfig);
11
+ report(result: BudgetResult): Promise<void>;
12
+ private formatPayload;
13
+ private send;
14
+ }
15
+ export declare function getSlackEnv(): SlackConfig | null;
16
+ //# sourceMappingURL=slack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/github/slack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAG5C,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAc;gBAEhB,MAAM,EAAE,WAAW;IAIzB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjD,OAAO,CAAC,aAAa;YA+DP,IAAI;CAWnB;AAED,wBAAgB,WAAW,IAAI,WAAW,GAAG,IAAI,CAahD"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SlackReporter = void 0;
4
+ exports.getSlackEnv = getSlackEnv;
5
+ const analyzer_1 = require("../lib/analyzer");
6
+ class SlackReporter {
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ async report(result) {
11
+ const payload = this.formatPayload(result);
12
+ await this.send(payload);
13
+ }
14
+ formatPayload(result) {
15
+ const status = result.passed ? '✅ *PASSED*' : '❌ *FAILED*';
16
+ let blocks = [
17
+ {
18
+ type: 'header',
19
+ text: {
20
+ type: 'plain_text',
21
+ text: '📦 Bundle Size Report',
22
+ emoji: true
23
+ }
24
+ },
25
+ {
26
+ type: 'section',
27
+ fields: [
28
+ {
29
+ type: 'mrkdwn',
30
+ text: `*Status:*\n${status}`
31
+ },
32
+ {
33
+ type: 'mrkdwn',
34
+ text: `*Total Size:*\n${(0, analyzer_1.formatSize)(result.totalSize)}`
35
+ },
36
+ {
37
+ type: 'mrkdwn',
38
+ text: `*Gzipped:*\n${(0, analyzer_1.formatSize)(result.totalGzipped)}`
39
+ }
40
+ ]
41
+ }
42
+ ];
43
+ if (!result.passed && result.violations.length > 0) {
44
+ const violationTexts = result.violations.slice(0, 10).map(v => `• *${v.name}*: ${(0, analyzer_1.formatSize)(v.current)} (limit: ${(0, analyzer_1.formatSize)(v.limit)}, +${v.percentageOver.toFixed(1)}%)`).join('\n');
45
+ blocks.push({
46
+ type: 'section',
47
+ text: {
48
+ type: 'mrkdwn',
49
+ text: `*⚠️ Budget Violations:*\n${violationTexts}`
50
+ }
51
+ });
52
+ }
53
+ blocks.push({
54
+ type: 'context',
55
+ elements: [
56
+ {
57
+ type: 'mrkdwn',
58
+ text: `Triggered by performance-budget-enforcer`
59
+ }
60
+ ]
61
+ });
62
+ return JSON.stringify({
63
+ channel: this.config.channel,
64
+ username: this.config.username || 'Performance Budget',
65
+ icon_emoji: this.config.iconEmoji || ':package:',
66
+ blocks
67
+ });
68
+ }
69
+ async send(payload) {
70
+ const { execSync } = require('child_process');
71
+ try {
72
+ const cmd = `curl -s -X POST -H 'Content-Type: application/json' -d '${payload}' ${this.config.webhookUrl}`;
73
+ execSync(cmd, { encoding: 'utf-8' });
74
+ console.log('✅ Slack notification sent');
75
+ }
76
+ catch (error) {
77
+ console.error('Failed to send Slack notification:', error.message);
78
+ }
79
+ }
80
+ }
81
+ exports.SlackReporter = SlackReporter;
82
+ function getSlackEnv() {
83
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL;
84
+ if (!webhookUrl) {
85
+ return null;
86
+ }
87
+ return {
88
+ webhookUrl,
89
+ channel: process.env.SLACK_CHANNEL,
90
+ username: process.env.SLACK_USERNAME,
91
+ iconEmoji: process.env.SLACK_ICON_EMOJI
92
+ };
93
+ }
94
+ //# sourceMappingURL=slack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/github/slack.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { BundleAnalyzer, formatSize, saveAnalysis, loadAnalysis } from './lib/analyzer';
2
2
  export { BudgetChecker, loadConfig } from './lib/budget';
3
3
  export { GitHubReporter, getGitHubEnv } from './github/reporter';
4
+ export { SlackReporter, getSlackEnv } from './github/slack';
4
5
  export * from './lib/types';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +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"}
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,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC5D,cAAc,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
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;
17
+ exports.getSlackEnv = exports.SlackReporter = exports.getGitHubEnv = exports.GitHubReporter = exports.loadConfig = exports.BudgetChecker = exports.loadAnalysis = exports.saveAnalysis = exports.formatSize = exports.BundleAnalyzer = void 0;
18
18
  var analyzer_1 = require("./lib/analyzer");
19
19
  Object.defineProperty(exports, "BundleAnalyzer", { enumerable: true, get: function () { return analyzer_1.BundleAnalyzer; } });
20
20
  Object.defineProperty(exports, "formatSize", { enumerable: true, get: function () { return analyzer_1.formatSize; } });
@@ -26,5 +26,8 @@ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function (
26
26
  var reporter_1 = require("./github/reporter");
27
27
  Object.defineProperty(exports, "GitHubReporter", { enumerable: true, get: function () { return reporter_1.GitHubReporter; } });
28
28
  Object.defineProperty(exports, "getGitHubEnv", { enumerable: true, get: function () { return reporter_1.getGitHubEnv; } });
29
+ var slack_1 = require("./github/slack");
30
+ Object.defineProperty(exports, "SlackReporter", { enumerable: true, get: function () { return slack_1.SlackReporter; } });
31
+ Object.defineProperty(exports, "getSlackEnv", { enumerable: true, get: function () { return slack_1.getSlackEnv; } });
29
32
  __exportStar(require("./lib/types"), exports);
30
33
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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"}
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,wCAA4D;AAAnD,sGAAA,aAAa,OAAA;AAAE,oGAAA,WAAW,OAAA;AACnC,8CAA4B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "performance-budget-enforcer",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "CI plugin that scans React/Next.js builds, detects bundle size regressions, and fails CI if budgets are exceeded",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/cli/index.ts CHANGED
@@ -8,6 +8,7 @@ import * as fs from 'fs';
8
8
  import { BundleAnalyzer, formatSize, saveAnalysis } from '../lib/analyzer';
9
9
  import { BudgetChecker, loadConfig } from '../lib/budget';
10
10
  import { GitHubReporter, getGitHubEnv } from '../github/reporter';
11
+ import { SlackReporter, getSlackEnv } from '../github/slack';
11
12
  import { BundleAnalysis, BudgetConfig } from '../lib/types';
12
13
 
13
14
  const program = new Command();
@@ -15,7 +16,7 @@ const program = new Command();
15
16
  program
16
17
  .name('perf-budget')
17
18
  .description('Frontend Performance Budget Enforcer - CI Plugin for React/Next.js')
18
- .version('1.0.0');
19
+ .version('1.1.0');
19
20
 
20
21
  program
21
22
  .command('analyze')
@@ -117,6 +118,12 @@ program
117
118
  const reporter = new GitHubReporter(githubConfig);
118
119
  await reporter.report(result);
119
120
  }
121
+
122
+ const slackConfig = getSlackEnv();
123
+ if (slackConfig) {
124
+ const slackReporter = new SlackReporter(slackConfig);
125
+ await slackReporter.report(result);
126
+ }
120
127
  }
121
128
 
122
129
  if (!result.passed && options.fail) {
@@ -45,39 +45,69 @@ export class GitHubReporter {
45
45
  }
46
46
  }
47
47
 
48
+ comment += `\n---\n*Powered by [performance-budget-enforcer](https://github.com/performance-budget-enforcer)*`;
49
+
48
50
  return comment;
49
51
  }
50
52
 
51
53
  private async postComment(body: string): Promise<void> {
52
54
  const { execSync } = require('child_process');
53
-
55
+
56
+ const prIdQuery = JSON.stringify({
57
+ query: `query($owner: String!, $repo: String!, $prNumber: Int!) {
58
+ repository(owner: $owner, name: $repo) {
59
+ pullRequest(number: $prNumber) {
60
+ id
61
+ }
62
+ }
63
+ }`,
64
+ variables: {
65
+ owner: this.config.owner,
66
+ repo: this.config.repo,
67
+ prNumber: this.config.prNumber
68
+ }
69
+ });
70
+
54
71
  try {
55
- const query = `
56
- mutation($pullRequestId: ID!, $body: String!) {
57
- addComment(input: { subjectId: $pullRequestId, body: $body }) {
72
+ const prIdCmd = `curl -s -X POST -H "Authorization: bearer ${this.config.token}" -H "Content-Type: application/json" -d '${prIdQuery}' https://api.github.com/graphql`;
73
+ const prIdResult = execSync(prIdCmd, { encoding: 'utf-8' });
74
+ const prIdData = JSON.parse(prIdResult);
75
+
76
+ if (!prIdData.data?.repository?.pullRequest?.id) {
77
+ console.log('Could not find PR ID. Make sure GITHUB_PR_NUMBER is set correctly.');
78
+ console.log(body);
79
+ return;
80
+ }
81
+
82
+ const prId = prIdData.data.repository.pullRequest.id;
83
+
84
+ const addCommentMutation = JSON.stringify({
85
+ query: `mutation($input: AddCommentInput!) {
86
+ addComment(input: $input) {
58
87
  commentEdge {
59
88
  node {
60
89
  id
90
+ body
61
91
  }
62
92
  }
63
93
  }
64
- }
65
- `;
66
-
67
- const prIdQuery = `
68
- query($owner: String!, $repo: String!, $prNumber: Int!) {
69
- repository(owner: $owner, name: $repo) {
70
- pullRequest(number: $prNumber) {
71
- id
72
- }
94
+ }`,
95
+ variables: {
96
+ input: {
97
+ subjectId: prId,
98
+ body: body
73
99
  }
74
100
  }
75
- `;
101
+ });
76
102
 
77
- console.log(`Posting comment to PR #${this.config.prNumber}...`);
103
+ const commentCmd = `curl -s -X POST -H "Authorization: bearer ${this.config.token}" -H "Content-Type: application/json" -d '${addCommentMutation}' https://api.github.com/graphql`;
104
+
105
+ execSync(commentCmd, { encoding: 'utf-8' });
106
+ console.log(`✅ Posted comment to PR #${this.config.prNumber}`);
107
+ } catch (error: any) {
108
+ console.error('Failed to post GitHub comment:', error.message);
109
+ console.log('Comment preview:');
78
110
  console.log(body);
79
- } catch (error) {
80
- console.error('Failed to post GitHub comment:', error);
81
111
  }
82
112
  }
83
113
  }
@@ -0,0 +1,112 @@
1
+ import { BudgetResult } from '../lib/types';
2
+ import { formatSize } from '../lib/analyzer';
3
+
4
+ export interface SlackConfig {
5
+ webhookUrl: string;
6
+ channel?: string;
7
+ username?: string;
8
+ iconEmoji?: string;
9
+ }
10
+
11
+ export class SlackReporter {
12
+ private config: SlackConfig;
13
+
14
+ constructor(config: SlackConfig) {
15
+ this.config = config;
16
+ }
17
+
18
+ async report(result: BudgetResult): Promise<void> {
19
+ const payload = this.formatPayload(result);
20
+ await this.send(payload);
21
+ }
22
+
23
+ private formatPayload(result: BudgetResult): string {
24
+ const status = result.passed ? '✅ *PASSED*' : '❌ *FAILED*';
25
+
26
+ let blocks: any[] = [
27
+ {
28
+ type: 'header',
29
+ text: {
30
+ type: 'plain_text',
31
+ text: '📦 Bundle Size Report',
32
+ emoji: true
33
+ }
34
+ },
35
+ {
36
+ type: 'section',
37
+ fields: [
38
+ {
39
+ type: 'mrkdwn',
40
+ text: `*Status:*\n${status}`
41
+ },
42
+ {
43
+ type: 'mrkdwn',
44
+ text: `*Total Size:*\n${formatSize(result.totalSize)}`
45
+ },
46
+ {
47
+ type: 'mrkdwn',
48
+ text: `*Gzipped:*\n${formatSize(result.totalGzipped)}`
49
+ }
50
+ ]
51
+ }
52
+ ];
53
+
54
+ if (!result.passed && result.violations.length > 0) {
55
+ const violationTexts = result.violations.slice(0, 10).map(v =>
56
+ `• *${v.name}*: ${formatSize(v.current)} (limit: ${formatSize(v.limit)}, +${v.percentageOver.toFixed(1)}%)`
57
+ ).join('\n');
58
+
59
+ blocks.push({
60
+ type: 'section',
61
+ text: {
62
+ type: 'mrkdwn',
63
+ text: `*⚠️ Budget Violations:*\n${violationTexts}`
64
+ }
65
+ });
66
+ }
67
+
68
+ blocks.push({
69
+ type: 'context',
70
+ elements: [
71
+ {
72
+ type: 'mrkdwn',
73
+ text: `Triggered by performance-budget-enforcer`
74
+ }
75
+ ]
76
+ });
77
+
78
+ return JSON.stringify({
79
+ channel: this.config.channel,
80
+ username: this.config.username || 'Performance Budget',
81
+ icon_emoji: this.config.iconEmoji || ':package:',
82
+ blocks
83
+ });
84
+ }
85
+
86
+ private async send(payload: string): Promise<void> {
87
+ const { execSync } = require('child_process');
88
+
89
+ try {
90
+ const cmd = `curl -s -X POST -H 'Content-Type: application/json' -d '${payload}' ${this.config.webhookUrl}`;
91
+ execSync(cmd, { encoding: 'utf-8' });
92
+ console.log('✅ Slack notification sent');
93
+ } catch (error: any) {
94
+ console.error('Failed to send Slack notification:', error.message);
95
+ }
96
+ }
97
+ }
98
+
99
+ export function getSlackEnv(): SlackConfig | null {
100
+ const webhookUrl = process.env.SLACK_WEBHOOK_URL;
101
+
102
+ if (!webhookUrl) {
103
+ return null;
104
+ }
105
+
106
+ return {
107
+ webhookUrl,
108
+ channel: process.env.SLACK_CHANNEL,
109
+ username: process.env.SLACK_USERNAME,
110
+ iconEmoji: process.env.SLACK_ICON_EMOJI
111
+ };
112
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { BundleAnalyzer, formatSize, saveAnalysis, loadAnalysis } from './lib/analyzer';
2
2
  export { BudgetChecker, loadConfig } from './lib/budget';
3
3
  export { GitHubReporter, getGitHubEnv } from './github/reporter';
4
+ export { SlackReporter, getSlackEnv } from './github/slack';
4
5
  export * from './lib/types';