brick-break 1.0.0 → 1.2.1

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
@@ -27,24 +27,63 @@ or just run it directly:
27
27
  npx brick-break next build
28
28
  ```
29
29
 
30
+ ## global install (recommended for non-js projects)
31
+
32
+ ```bash
33
+ npm i -g brick-break
34
+ ```
35
+
36
+ now you can use `bb` anywhere:
37
+
38
+ ```bash
39
+ bb cargo build
40
+ bb go build
41
+ bb pytest
42
+ bb make
43
+ ```
44
+
30
45
  ## works with everything
31
46
 
32
47
  ```bash
33
- brick-break next build
34
- brick-break npm run build
35
- brick-break tsc
36
- brick-break cargo build
37
- brick-break go build
48
+ bb next build
49
+ bb npm run build
50
+ bb tsc
51
+ bb cargo build
52
+ bb go build
38
53
  ```
39
54
 
40
55
  if it can fail, brick-break can make it funnier.
41
56
 
57
+ ## next.js hmr support
58
+
59
+ for errors that happen while the dev server is running (hot reload), add the component to your layout:
60
+
61
+ ```tsx
62
+ // app/layout.tsx
63
+ import { BrickBreak } from 'brick-break/next'
64
+
65
+ export default function RootLayout({ children }) {
66
+ return (
67
+ <html>
68
+ <body>
69
+ <BrickBreak />
70
+ {children}
71
+ </body>
72
+ </html>
73
+ )
74
+ }
75
+ ```
76
+
77
+ now errors play the sound even during hot reload.
78
+
42
79
  ## how it works
43
80
 
44
81
  1. runs your command
45
82
  2. build fails? plays the sound
46
83
  3. thats it
47
84
 
85
+ (for next.js hmr: watches for the error overlay in the browser)
86
+
48
87
  ## requirements
49
88
 
50
89
  uses your system audio player (already installed):
package/dist/cli.js CHANGED
@@ -4,8 +4,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const index_1 = require("./index");
5
5
  const args = process.argv.slice(2);
6
6
  if (!args.length) {
7
- console.log('usage: brick-break <command>');
8
- console.log('example: brick-break npm run build');
7
+ console.log('usage: bb <command>');
8
+ console.log('example: bb npm run build');
9
9
  process.exit(1);
10
10
  }
11
+ // detect next.js dev mode
12
+ const isNextDev = args.some(a => a.includes('next')) && args.some(a => a === 'dev');
13
+ if (isNextDev) {
14
+ console.log('\x1b[36m');
15
+ console.log('brick-break: for hmr error sounds, add to your layout.tsx:');
16
+ console.log('');
17
+ console.log(" import { BrickBreak } from 'brick-break/next'");
18
+ console.log('');
19
+ console.log(' // then add <BrickBreak /> inside <body>');
20
+ console.log('\x1b[0m');
21
+ }
11
22
  (0, index_1.runCommand)(args);
package/dist/index.js CHANGED
@@ -8,7 +8,13 @@ exports.runCommand = runCommand;
8
8
  const child_process_1 = require("child_process");
9
9
  const path_1 = __importDefault(require("path"));
10
10
  const sound = path_1.default.join(__dirname, '..', 'sound.mp3');
11
+ let lastPlayed = 0;
12
+ const cooldown = 3000; // don't spam the sound
11
13
  function playSound() {
14
+ const now = Date.now();
15
+ if (now - lastPlayed < cooldown)
16
+ return;
17
+ lastPlayed = now;
12
18
  const p = process.platform;
13
19
  if (p === 'darwin') {
14
20
  (0, child_process_1.spawn)('afplay', [sound]);
@@ -17,15 +23,44 @@ function playSound() {
17
23
  (0, child_process_1.spawn)('powershell', ['-c', `(New-Object Media.SoundPlayer '${sound}').PlaySync()`]);
18
24
  }
19
25
  else {
20
- // linux - try pulseaudio first, fallback to alsa
21
26
  const player = (0, child_process_1.spawn)('paplay', [sound]);
22
27
  player.on('error', () => (0, child_process_1.spawn)('aplay', [sound]));
23
28
  }
24
29
  }
30
+ // patterns that indicate an error
31
+ const errorPatterns = [
32
+ /\u{2A2F}/u, // ⨯ (nextjs error)
33
+ /error(\[|:|\s)/i, // error: or error[ or error
34
+ /failed/i,
35
+ /exception/i,
36
+ /panic/i, // rust/go
37
+ /cannot find/i,
38
+ /not found/i,
39
+ /undefined/i,
40
+ /compilation failed/i,
41
+ ];
42
+ function hasError(text) {
43
+ return errorPatterns.some(p => p.test(text));
44
+ }
25
45
  function runCommand(args) {
26
46
  const cmd = args[0];
27
47
  const rest = args.slice(1);
28
- const proc = (0, child_process_1.spawn)(cmd, rest, { stdio: 'inherit', shell: true });
48
+ const proc = (0, child_process_1.spawn)(cmd, rest, {
49
+ stdio: ['inherit', 'pipe', 'pipe'],
50
+ shell: true
51
+ });
52
+ proc.stdout?.on('data', (data) => {
53
+ const text = data.toString();
54
+ process.stdout.write(text);
55
+ if (hasError(text))
56
+ playSound();
57
+ });
58
+ proc.stderr?.on('data', (data) => {
59
+ const text = data.toString();
60
+ process.stderr.write(text);
61
+ if (hasError(text))
62
+ playSound();
63
+ });
29
64
  proc.on('close', code => {
30
65
  if (code !== 0)
31
66
  playSound();
package/dist/next.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function BrickBreak({ children }: {
3
+ children?: ReactNode;
4
+ }): import("react/jsx-runtime").JSX.Element;
package/dist/next.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ 'use client';
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.BrickBreak = BrickBreak;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const react_1 = require("react");
7
+ let lastPlayed = 0;
8
+ const cooldown = 3000;
9
+ function playSound() {
10
+ const now = Date.now();
11
+ if (now - lastPlayed < cooldown)
12
+ return;
13
+ lastPlayed = now;
14
+ const audio = new Audio('https://unpkg.com/brick-break/sound.mp3');
15
+ audio.play().catch(() => { });
16
+ }
17
+ function BrickBreak({ children }) {
18
+ const seenErrors = (0, react_1.useRef)(new Set());
19
+ (0, react_1.useEffect)(() => {
20
+ if (process.env.NODE_ENV !== 'development')
21
+ return;
22
+ const observer = new MutationObserver(() => {
23
+ // nextjs error overlay
24
+ const dialog = document.querySelector('[data-nextjs-dialog]');
25
+ if (dialog) {
26
+ const errorText = dialog.textContent || '';
27
+ if (!seenErrors.current.has(errorText)) {
28
+ seenErrors.current.add(errorText);
29
+ playSound();
30
+ }
31
+ }
32
+ });
33
+ observer.observe(document.body, { childList: true, subtree: true });
34
+ return () => observer.disconnect();
35
+ }, []);
36
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
37
+ }
package/package.json CHANGED
@@ -1,11 +1,22 @@
1
1
  {
2
2
  "name": "brick-break",
3
- "version": "1.0.0",
3
+ "version": "1.2.1",
4
4
  "description": "Play a Lego break sound when your build fails",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./next": {
13
+ "types": "./dist/next.d.ts",
14
+ "default": "./dist/next.js"
15
+ }
16
+ },
7
17
  "bin": {
8
- "brick-break": "dist/cli.js"
18
+ "brick-break": "dist/cli.js",
19
+ "bb": "dist/cli.js"
9
20
  },
10
21
  "files": [
11
22
  "dist",
@@ -22,7 +33,8 @@
22
33
  "error",
23
34
  "fun",
24
35
  "developer-experience",
25
- "nextjs"
36
+ "nextjs",
37
+ "react"
26
38
  ],
27
39
  "author": "ShinniUwU",
28
40
  "license": "MIT",
@@ -34,8 +46,17 @@
34
46
  "bugs": {
35
47
  "url": "https://github.com/ShinniUwU/brick-break/issues"
36
48
  },
49
+ "peerDependencies": {
50
+ "react": ">=18.0.0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "react": {
54
+ "optional": true
55
+ }
56
+ },
37
57
  "devDependencies": {
38
58
  "@types/node": "^20.0.0",
59
+ "@types/react": "^18.0.0",
39
60
  "typescript": "^5.0.0"
40
61
  }
41
62
  }