@nhonh/react-debugger 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/README.md +67 -0
- package/bin/cli.js +116 -0
- package/package.json +41 -0
- package/src/install.js +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# ⚛️ React Debugger
|
|
2
|
+
|
|
3
|
+
Advanced debugging & performance optimization tool for ReactJS applications.
|
|
4
|
+
|
|
5
|
+
## Quick Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx react-debugger
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This will download the Chrome extension to your local machine.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Interactive Mode
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx react-debugger
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Follow the prompts to choose installation directory.
|
|
22
|
+
|
|
23
|
+
### Direct Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx react-debugger ./my-extension
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Options
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
-v, --version Show version number
|
|
33
|
+
-h, --help Show help
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Loading the Extension in Chrome
|
|
37
|
+
|
|
38
|
+
After installation:
|
|
39
|
+
|
|
40
|
+
1. Open `chrome://extensions/` in Chrome
|
|
41
|
+
2. Enable **Developer mode** (toggle in top right)
|
|
42
|
+
3. Click **Load unpacked**
|
|
43
|
+
4. Select the folder where you installed the extension
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- 🎯 **UI & State Issues** - Detect direct state mutation, missing keys, index as key
|
|
48
|
+
- ⚡ **Performance Analysis** - Track re-renders, identify excessive renders
|
|
49
|
+
- 🔄 **Side Effects** - Find missing cleanup, dependency issues in useEffect
|
|
50
|
+
- 📐 **CLS Monitor** - Track Cumulative Layout Shift in real-time
|
|
51
|
+
- 🗄️ **Redux DevTools** - View state tree, dispatch actions manually
|
|
52
|
+
- 📊 **Timeline** - Visual timeline of all React events
|
|
53
|
+
- 💾 **Memory** - Monitor memory usage and detect leaks
|
|
54
|
+
|
|
55
|
+
## Requirements
|
|
56
|
+
|
|
57
|
+
- Node.js >= 18.0.0
|
|
58
|
+
- Chrome, Brave, or any Chromium-based browser
|
|
59
|
+
|
|
60
|
+
## Links
|
|
61
|
+
|
|
62
|
+
- [GitHub Repository](https://github.com/nhonh/react-debugger-extension)
|
|
63
|
+
- [Report Issues](https://github.com/nhonh/react-debugger-extension/issues)
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT © NhoNH
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { intro, outro, text, spinner, confirm, isCancel } from '@clack/prompts';
|
|
4
|
+
import pc from 'picocolors';
|
|
5
|
+
import { downloadAndExtract } from '../src/install.js';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
|
|
9
|
+
const VERSION = '1.0.0';
|
|
10
|
+
const EXTENSION_NAME = 'React Debugger';
|
|
11
|
+
|
|
12
|
+
async function main() {
|
|
13
|
+
const args = process.argv.slice(2);
|
|
14
|
+
|
|
15
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
16
|
+
console.log(`${EXTENSION_NAME} v${VERSION}`);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
21
|
+
console.log(`
|
|
22
|
+
${pc.cyan(EXTENSION_NAME)} - Chrome Extension Installer
|
|
23
|
+
|
|
24
|
+
${pc.yellow('Usage:')}
|
|
25
|
+
npx react-debugger [destination]
|
|
26
|
+
|
|
27
|
+
${pc.yellow('Options:')}
|
|
28
|
+
-v, --version Show version number
|
|
29
|
+
-h, --help Show help
|
|
30
|
+
|
|
31
|
+
${pc.yellow('Examples:')}
|
|
32
|
+
npx react-debugger # Interactive mode
|
|
33
|
+
npx react-debugger ./my-extension # Direct install to folder
|
|
34
|
+
`);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log();
|
|
39
|
+
intro(pc.bgCyan(pc.black(` ${EXTENSION_NAME} Extension Installer `)));
|
|
40
|
+
|
|
41
|
+
let destination = args[0];
|
|
42
|
+
|
|
43
|
+
if (!destination) {
|
|
44
|
+
const destInput = await text({
|
|
45
|
+
message: 'Where should we install the extension?',
|
|
46
|
+
placeholder: './react-debugger',
|
|
47
|
+
initialValue: './react-debugger',
|
|
48
|
+
validate: (value) => {
|
|
49
|
+
if (!value || value.trim() === '') {
|
|
50
|
+
return 'Please enter a destination path';
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (isCancel(destInput)) {
|
|
56
|
+
outro(pc.yellow('Installation cancelled.'));
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
destination = destInput;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const fullPath = path.resolve(destination);
|
|
64
|
+
|
|
65
|
+
if (fs.existsSync(fullPath)) {
|
|
66
|
+
const files = fs.readdirSync(fullPath);
|
|
67
|
+
if (files.length > 0) {
|
|
68
|
+
const shouldOverwrite = await confirm({
|
|
69
|
+
message: `Directory ${pc.yellow(fullPath)} is not empty. Overwrite?`,
|
|
70
|
+
initialValue: false,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (isCancel(shouldOverwrite) || !shouldOverwrite) {
|
|
74
|
+
outro(pc.yellow('Installation cancelled.'));
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const s = spinner();
|
|
81
|
+
s.start('Downloading React Debugger extension...');
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await downloadAndExtract(fullPath);
|
|
85
|
+
s.stop(pc.green('Download complete!'));
|
|
86
|
+
|
|
87
|
+
console.log();
|
|
88
|
+
console.log(pc.dim('─'.repeat(50)));
|
|
89
|
+
console.log();
|
|
90
|
+
console.log(pc.bold('Next steps to load the extension in Chrome:'));
|
|
91
|
+
console.log();
|
|
92
|
+
console.log(` ${pc.cyan('1.')} Open ${pc.yellow('chrome://extensions/')} in Chrome`);
|
|
93
|
+
console.log(` ${pc.cyan('2.')} Enable ${pc.yellow('Developer mode')} (toggle in top right)`);
|
|
94
|
+
console.log(` ${pc.cyan('3.')} Click ${pc.yellow('Load unpacked')}`);
|
|
95
|
+
console.log(` ${pc.cyan('4.')} Select the folder:`);
|
|
96
|
+
console.log(` ${pc.green(fullPath)}`);
|
|
97
|
+
console.log();
|
|
98
|
+
console.log(pc.dim('─'.repeat(50)));
|
|
99
|
+
console.log();
|
|
100
|
+
|
|
101
|
+
outro(pc.green('✓ Installation successful!'));
|
|
102
|
+
} catch (err) {
|
|
103
|
+
s.stop(pc.red('Download failed!'));
|
|
104
|
+
console.error();
|
|
105
|
+
console.error(pc.red('Error:'), err.message);
|
|
106
|
+
console.error();
|
|
107
|
+
console.error(pc.dim('If this persists, please report at:'));
|
|
108
|
+
console.error(pc.dim('https://github.com/nhonh/react-debugger-extension/issues'));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
main().catch((err) => {
|
|
114
|
+
console.error(pc.red('Unexpected error:'), err);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nhonh/react-debugger",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Advanced debugging & performance optimization tool for ReactJS applications - Chrome Extension",
|
|
5
|
+
"author": "NhoNH",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"react-debugger": "bin/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"src/"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"react",
|
|
17
|
+
"debugger",
|
|
18
|
+
"devtools",
|
|
19
|
+
"chrome-extension",
|
|
20
|
+
"performance",
|
|
21
|
+
"redux",
|
|
22
|
+
"profiler",
|
|
23
|
+
"developer-tools"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/nhonh/react-debugger-extension.git"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/nhonh/react-debugger-extension#readme",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/nhonh/react-debugger-extension/issues"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@clack/prompts": "^0.9.1",
|
|
38
|
+
"picocolors": "^1.1.1",
|
|
39
|
+
"decompress": "^4.2.1"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/install.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import decompress from 'decompress';
|
|
5
|
+
|
|
6
|
+
const GITHUB_REPO = 'nhonh/react-debugger-extension';
|
|
7
|
+
const RELEASE_TAG = 'latest';
|
|
8
|
+
|
|
9
|
+
async function getLatestReleaseUrl() {
|
|
10
|
+
const apiUrl = `https://api.github.com/repos/${GITHUB_REPO}/releases/${RELEASE_TAG}`;
|
|
11
|
+
|
|
12
|
+
const response = await fetch(apiUrl, {
|
|
13
|
+
headers: {
|
|
14
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
15
|
+
'User-Agent': 'react-debugger-cli',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
if (response.status === 404) {
|
|
21
|
+
return getFallbackUrl();
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`Failed to fetch release info: ${response.statusText}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const release = await response.json();
|
|
27
|
+
const asset = release.assets?.find(a => a.name === 'react-debugger.zip');
|
|
28
|
+
|
|
29
|
+
if (asset) {
|
|
30
|
+
return asset.browser_download_url;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return getFallbackUrl();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getFallbackUrl() {
|
|
37
|
+
return `https://github.com/${GITHUB_REPO}/releases/latest/download/react-debugger.zip`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function downloadFromUrl(url) {
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
headers: {
|
|
43
|
+
'User-Agent': 'react-debugger-cli',
|
|
44
|
+
},
|
|
45
|
+
redirect: 'follow',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`Download failed: ${response.status} ${response.statusText}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
53
|
+
return Buffer.from(arrayBuffer);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function downloadAndExtract(dest) {
|
|
57
|
+
await fs.mkdir(dest, { recursive: true });
|
|
58
|
+
|
|
59
|
+
const existingFiles = await fs.readdir(dest);
|
|
60
|
+
for (const file of existingFiles) {
|
|
61
|
+
await fs.rm(path.join(dest, file), { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let downloadUrl;
|
|
65
|
+
try {
|
|
66
|
+
downloadUrl = await getLatestReleaseUrl();
|
|
67
|
+
} catch {
|
|
68
|
+
downloadUrl = getFallbackUrl();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const buffer = await downloadFromUrl(downloadUrl);
|
|
72
|
+
|
|
73
|
+
await decompress(buffer, dest, {
|
|
74
|
+
strip: 0,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const files = await fs.readdir(dest);
|
|
78
|
+
if (!files.includes('manifest.json')) {
|
|
79
|
+
const subDirs = files.filter(async (f) => {
|
|
80
|
+
const stat = await fs.stat(path.join(dest, f));
|
|
81
|
+
return stat.isDirectory();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (subDirs.length === 1) {
|
|
85
|
+
const subDir = path.join(dest, subDirs[0]);
|
|
86
|
+
const subFiles = await fs.readdir(subDir);
|
|
87
|
+
|
|
88
|
+
for (const file of subFiles) {
|
|
89
|
+
await fs.rename(path.join(subDir, file), path.join(dest, file));
|
|
90
|
+
}
|
|
91
|
+
await fs.rmdir(subDir);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|