rn-bundle-analyzer 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 +80 -0
- package/dist/builders/treeBuilder.d.ts +2 -0
- package/dist/builders/treeBuilder.js +47 -0
- package/dist/collectors/fileCollector.d.ts +2 -0
- package/dist/collectors/fileCollector.js +27 -0
- package/dist/collectors/inverseDependencyCollector.d.ts +2 -0
- package/dist/collectors/inverseDependencyCollector.js +29 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +15 -0
- package/dist/fileInfoCollector.d.ts +4 -0
- package/dist/fileInfoCollector.js +44 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +48 -0
- package/dist/templates/analyse.html +1082 -0
- package/dist/templates/analyse.pug +321 -0
- package/dist/templates/analyseTemplate.d.ts +3 -0
- package/dist/templates/analyseTemplate.js +41 -0
- package/dist/templates/icons-inline.js +27 -0
- package/dist/templates/lucideIcons.d.ts +2 -0
- package/dist/templates/lucideIcons.js +28 -0
- package/dist/templates/tailwindcss.js +83 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.js +2 -0
- package/dist/utils/envSetup.d.ts +1 -0
- package/dist/utils/envSetup.js +21 -0
- package/dist/utils/fileUtils.d.ts +3 -0
- package/dist/utils/fileUtils.js +27 -0
- package/dist/writers/fileInfoWriter.d.ts +12 -0
- package/dist/writers/fileInfoWriter.js +42 -0
- package/dist/writers/inverseDependencyWriter.d.ts +2 -0
- package/dist/writers/inverseDependencyWriter.js +48 -0
- package/dist/writers/treeWriter.d.ts +2 -0
- package/dist/writers/treeWriter.js +26 -0
- package/index.js +1 -0
- package/package.json +24 -0
- package/scripts/compile-pug.js +17 -0
- package/src/builders/treeBuilder.ts +57 -0
- package/src/collectors/fileCollector.ts +24 -0
- package/src/collectors/inverseDependencyCollector.ts +32 -0
- package/src/config.ts +14 -0
- package/src/fileInfoCollector.ts +50 -0
- package/src/index.ts +15 -0
- package/src/templates/analyse-app.js +988 -0
- package/src/templates/analyse.pug +321 -0
- package/src/templates/analyseTemplate.ts +45 -0
- package/src/templates/icons-inline.js +27 -0
- package/src/templates/tailwindcss.js +83 -0
- package/src/types.ts +43 -0
- package/src/utils/envSetup.ts +18 -0
- package/src/utils/fileUtils.ts +21 -0
- package/src/writers/fileInfoWriter.ts +44 -0
- package/src/writers/inverseDependencyWriter.ts +50 -0
- package/src/writers/treeWriter.ts +24 -0
- package/tsconfig.json +20 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface FileInfo {
|
|
2
|
+
path: string;
|
|
3
|
+
size: number;
|
|
4
|
+
sizeKB: number;
|
|
5
|
+
lastModified: string;
|
|
6
|
+
extension: string;
|
|
7
|
+
}
|
|
8
|
+
export interface FileTypeStats {
|
|
9
|
+
[ext: string]: {
|
|
10
|
+
count: number;
|
|
11
|
+
size: number;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export interface InverseDependencyInfo {
|
|
15
|
+
path: string;
|
|
16
|
+
dependents: string[];
|
|
17
|
+
dependentCount: number;
|
|
18
|
+
}
|
|
19
|
+
export interface TreeNode {
|
|
20
|
+
path: string;
|
|
21
|
+
size: number;
|
|
22
|
+
files: FileInfo[];
|
|
23
|
+
children: {
|
|
24
|
+
[name: string]: TreeNode;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface TreeOutputNode {
|
|
28
|
+
path: string;
|
|
29
|
+
size: number;
|
|
30
|
+
ratio: number;
|
|
31
|
+
child?: TreeOutputNode[];
|
|
32
|
+
}
|
|
33
|
+
export interface TreeData {
|
|
34
|
+
totalSize: number;
|
|
35
|
+
pathAnalyse: TreeOutputNode[];
|
|
36
|
+
}
|
|
37
|
+
export interface MetroModule {
|
|
38
|
+
path: string;
|
|
39
|
+
inverseDependencies: Iterable<string>;
|
|
40
|
+
}
|
|
41
|
+
export type ProcessModuleFilter = (module: MetroModule) => boolean;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function prepareSaveEnv(): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.prepareSaveEnv = prepareSaveEnv;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const fileUtils_1 = require("./fileUtils");
|
|
10
|
+
const analyseTemplate_1 = require("../templates/analyseTemplate");
|
|
11
|
+
let isPrepared = false;
|
|
12
|
+
function prepareSaveEnv() {
|
|
13
|
+
if (isPrepared)
|
|
14
|
+
return;
|
|
15
|
+
isPrepared = true;
|
|
16
|
+
const saveFullPath = path_1.default.join(process.cwd(), config_1.SAVE_DIR);
|
|
17
|
+
(0, fileUtils_1.ensureDir)(saveFullPath);
|
|
18
|
+
(0, fileUtils_1.writeFile)(path_1.default.join(saveFullPath, config_1.OUTPUT_FILES.HTML), analyseTemplate_1.analyseHtml);
|
|
19
|
+
(0, fileUtils_1.writeFile)(path_1.default.join(saveFullPath, config_1.OUTPUT_FILES.ICONS_INLINE), analyseTemplate_1.iconsInlineJs);
|
|
20
|
+
(0, fileUtils_1.writeFile)(path_1.default.join(saveFullPath, config_1.OUTPUT_FILES.TAILWIND_JS), analyseTemplate_1.tailwindJs);
|
|
21
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ensureDir = ensureDir;
|
|
7
|
+
exports.writeFile = writeFile;
|
|
8
|
+
exports.toRelativePath = toRelativePath;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
function ensureDir(dirPath) {
|
|
12
|
+
if (!fs_1.default.existsSync(dirPath)) {
|
|
13
|
+
try {
|
|
14
|
+
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
15
|
+
console.log(`✅ 创建目录: ${dirPath}`);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
console.error(`❌ 创建目录失败: ${dirPath}`, error.message);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function writeFile(filePath, content) {
|
|
23
|
+
fs_1.default.writeFileSync(filePath, content, 'utf8');
|
|
24
|
+
}
|
|
25
|
+
function toRelativePath(filePath) {
|
|
26
|
+
return path_1.default.relative(process.cwd(), filePath);
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FileInfo, FileTypeStats } from '../types';
|
|
2
|
+
export declare function writeFileInfo(fileInfoArray: FileInfo[]): {
|
|
3
|
+
timestamp: string;
|
|
4
|
+
buildInfo: {
|
|
5
|
+
totalFiles: number;
|
|
6
|
+
totalSize: number;
|
|
7
|
+
totalSizeKB: number;
|
|
8
|
+
totalSizeMB: number;
|
|
9
|
+
};
|
|
10
|
+
fileTypeStats: FileTypeStats;
|
|
11
|
+
files: FileInfo[];
|
|
12
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeFileInfo = writeFileInfo;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const fileUtils_1 = require("../utils/fileUtils");
|
|
10
|
+
const envSetup_1 = require("../utils/envSetup");
|
|
11
|
+
function writeFileInfo(fileInfoArray) {
|
|
12
|
+
(0, envSetup_1.prepareSaveEnv)();
|
|
13
|
+
const totalSize = fileInfoArray.reduce((sum, file) => sum + file.size, 0);
|
|
14
|
+
const fileTypeStats = {};
|
|
15
|
+
fileInfoArray.forEach(file => {
|
|
16
|
+
const ext = file.extension || 'unknown';
|
|
17
|
+
if (!fileTypeStats[ext])
|
|
18
|
+
fileTypeStats[ext] = { count: 0, size: 0 };
|
|
19
|
+
fileTypeStats[ext].count++;
|
|
20
|
+
fileTypeStats[ext].size += file.size;
|
|
21
|
+
});
|
|
22
|
+
const fileInfo = {
|
|
23
|
+
timestamp: new Date().toISOString(),
|
|
24
|
+
buildInfo: {
|
|
25
|
+
totalFiles: fileInfoArray.length,
|
|
26
|
+
totalSize,
|
|
27
|
+
totalSizeKB: Math.round(totalSize / 1024 * 100) / 100,
|
|
28
|
+
totalSizeMB: Math.round(totalSize / (1024 * 1024) * 100) / 100,
|
|
29
|
+
},
|
|
30
|
+
fileTypeStats,
|
|
31
|
+
files: fileInfoArray.slice().sort((a, b) => b.size - a.size),
|
|
32
|
+
};
|
|
33
|
+
const content = `window.packageAnalyseData = ${JSON.stringify(fileInfo, null, 2)};\n`;
|
|
34
|
+
(0, fileUtils_1.writeFile)(path_1.default.join(process.cwd(), config_1.SAVE_DIR, config_1.OUTPUT_FILES.PACKAGE_DATA), content);
|
|
35
|
+
console.log(`✅ RN 包文件信息已保存到 ${config_1.OUTPUT_FILES.PACKAGE_DATA}`);
|
|
36
|
+
console.log(`📊 统计信息: ${fileInfo.buildInfo.totalFiles} 个文件, 总大小 ${fileInfo.buildInfo.totalSizeKB} KB`);
|
|
37
|
+
console.log('📈 文件类型统计:');
|
|
38
|
+
Object.entries(fileTypeStats).forEach(([ext, stats]) => {
|
|
39
|
+
console.log(` ${ext}: ${stats.count} 个文件, ${Math.round(stats.size / 1024 * 100) / 100} KB`);
|
|
40
|
+
});
|
|
41
|
+
return fileInfo;
|
|
42
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeInverseDependencies = writeInverseDependencies;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const fileUtils_1 = require("../utils/fileUtils");
|
|
10
|
+
const envSetup_1 = require("../utils/envSetup");
|
|
11
|
+
function writeInverseDependencies(inverseDepsArray) {
|
|
12
|
+
if (!inverseDepsArray || inverseDepsArray.length === 0) {
|
|
13
|
+
console.log('⚠️ 没有收集到反向依赖信息');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
(0, envSetup_1.prepareSaveEnv)();
|
|
17
|
+
const sortedInverseDeps = inverseDepsArray.slice().sort((a, b) => b.dependentCount - a.dependentCount);
|
|
18
|
+
const totalFiles = sortedInverseDeps.length;
|
|
19
|
+
const totalDependencies = sortedInverseDeps.reduce((sum, item) => sum + item.dependentCount, 0);
|
|
20
|
+
const avgDependencies = totalFiles > 0 ? Math.round((totalDependencies / totalFiles) * 100) / 100 : 0;
|
|
21
|
+
const mostDependedFiles = sortedInverseDeps.slice(0, config_1.TOP_DEPENDED_LIMIT);
|
|
22
|
+
const inverseDependencyData = {
|
|
23
|
+
timestamp: new Date().toISOString(),
|
|
24
|
+
summary: {
|
|
25
|
+
totalFiles,
|
|
26
|
+
totalDependencies,
|
|
27
|
+
averageDependencies: avgDependencies,
|
|
28
|
+
mostDependedFiles: mostDependedFiles.map(item => ({
|
|
29
|
+
path: item.path,
|
|
30
|
+
dependentCount: item.dependentCount,
|
|
31
|
+
})),
|
|
32
|
+
},
|
|
33
|
+
inverseDependencies: sortedInverseDeps,
|
|
34
|
+
};
|
|
35
|
+
const content = `window.inverseDependencyData = ${JSON.stringify(inverseDependencyData, null, 2)};\n`;
|
|
36
|
+
try {
|
|
37
|
+
(0, fileUtils_1.writeFile)(path_1.default.join(process.cwd(), config_1.SAVE_DIR, config_1.OUTPUT_FILES.INVERSE_DEPS), content);
|
|
38
|
+
console.log(`✅ RN 包反向依赖分析已保存到 ${config_1.OUTPUT_FILES.INVERSE_DEPS}`);
|
|
39
|
+
console.log(`🔗 反向依赖统计: ${totalFiles} 个文件, ${totalDependencies} 个依赖关系`);
|
|
40
|
+
console.log('📊 最受依赖的文件 (前5个):');
|
|
41
|
+
mostDependedFiles.slice(0, 5).forEach((item, index) => {
|
|
42
|
+
console.log(` ${index + 1}. ${item.path}: ${item.dependentCount} 个依赖者`);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('❌ 保存反向依赖分析失败:', error.message);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.writeTreeAnalysis = writeTreeAnalysis;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const fileUtils_1 = require("../utils/fileUtils");
|
|
10
|
+
const envSetup_1 = require("../utils/envSetup");
|
|
11
|
+
function writeTreeAnalysis(treeData) {
|
|
12
|
+
(0, envSetup_1.prepareSaveEnv)();
|
|
13
|
+
const content = `window.treeAnalyseData = ${JSON.stringify(treeData, null, 2)};\n`;
|
|
14
|
+
try {
|
|
15
|
+
(0, fileUtils_1.writeFile)(path_1.default.join(process.cwd(), config_1.SAVE_DIR, config_1.OUTPUT_FILES.TREE_DATA), content);
|
|
16
|
+
console.log(`✅ RN 包树形分析已保存到 ${config_1.OUTPUT_FILES.TREE_DATA}`);
|
|
17
|
+
console.log(`🌳 树形结构: ${treeData.pathAnalyse.length} 个根目录/文件`);
|
|
18
|
+
console.log('📊 根级目录大小统计:');
|
|
19
|
+
treeData.pathAnalyse.slice(0, config_1.TREE_DISPLAY_LIMIT).forEach(item => {
|
|
20
|
+
console.log(` ${item.path}: ${Math.round(item.size / 1024 * 100) / 100} KB (${(item.ratio * 100).toFixed(2)}%)`);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
console.error('❌ 保存树形分析失败:', error.message);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./dist/index')
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rn-bundle-analyzer",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "React Native bundle analysis tool with file, tree, and inverse dependency insights.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc && cp src/templates/analyse.pug src/templates/icons-inline.js src/templates/tailwindcss.js dist/templates/",
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"author": "",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"feather-icons": "^4.29.2",
|
|
17
|
+
"pug": "^3.0.4"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^25.6.0",
|
|
21
|
+
"@types/pug": "^2.0.10",
|
|
22
|
+
"typescript": "^6.0.2"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const pug = require('pug')
|
|
4
|
+
const { icons } = require('./lucide-icons.cjs')
|
|
5
|
+
|
|
6
|
+
const root = path.join(__dirname, '..')
|
|
7
|
+
const src = path.join(root, 'src/templates/analyse.pug')
|
|
8
|
+
const out = path.join(root, 'src/templates/analyse.html')
|
|
9
|
+
|
|
10
|
+
const html = pug.renderFile(src, {
|
|
11
|
+
pretty: true,
|
|
12
|
+
basedir: path.join(root, 'src/templates'),
|
|
13
|
+
icons,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
fs.writeFileSync(out, html, 'utf8')
|
|
17
|
+
console.log('Wrote', out)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { FileInfo, TreeNode, TreeOutputNode, TreeData } from '../types'
|
|
3
|
+
|
|
4
|
+
export function buildTreeStructure(fileInfoArray: FileInfo[], totalSize: number): TreeData {
|
|
5
|
+
const tree: { [name: string]: TreeNode } = {}
|
|
6
|
+
|
|
7
|
+
fileInfoArray.forEach(fileInfo => {
|
|
8
|
+
const pathParts = fileInfo.path.split(path.sep).filter(part => part !== '')
|
|
9
|
+
let currentLevel = tree
|
|
10
|
+
let currentPath = ''
|
|
11
|
+
|
|
12
|
+
pathParts.forEach((part, index) => {
|
|
13
|
+
currentPath = currentPath ? path.join(currentPath, part) : part
|
|
14
|
+
|
|
15
|
+
if (!currentLevel[part]) {
|
|
16
|
+
currentLevel[part] = {
|
|
17
|
+
path: currentPath,
|
|
18
|
+
size: 0,
|
|
19
|
+
files: [],
|
|
20
|
+
children: {},
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
currentLevel[part].size += fileInfo.size
|
|
25
|
+
|
|
26
|
+
if (index === pathParts.length - 1) {
|
|
27
|
+
currentLevel[part].files.push(fileInfo)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
currentLevel = currentLevel[part].children
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const convertToTargetFormat = (treeNode: TreeNode, nodePath: string): TreeOutputNode => {
|
|
35
|
+
const result: TreeOutputNode = {
|
|
36
|
+
path: nodePath,
|
|
37
|
+
size: treeNode.size,
|
|
38
|
+
ratio: Math.round((treeNode.size / totalSize) * 10000) / 10000,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const childrenArray = Object.values(treeNode.children).map(childNode =>
|
|
42
|
+
convertToTargetFormat(childNode, childNode.path)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
if (childrenArray.length > 0) {
|
|
46
|
+
result.child = childrenArray.sort((a, b) => b.size - a.size)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pathAnalyse = Object.values(tree)
|
|
53
|
+
.map(rootNode => convertToTargetFormat(rootNode, rootNode.path))
|
|
54
|
+
.sort((a, b) => b.size - a.size)
|
|
55
|
+
|
|
56
|
+
return { totalSize, pathAnalyse }
|
|
57
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { toRelativePath } from '../utils/fileUtils'
|
|
4
|
+
import { FileInfo } from '../types'
|
|
5
|
+
|
|
6
|
+
export function collectFileInfo(filePath: string): FileInfo | null {
|
|
7
|
+
try {
|
|
8
|
+
const stats = fs.statSync(filePath)
|
|
9
|
+
const relativePath = toRelativePath(filePath)
|
|
10
|
+
|
|
11
|
+
console.log('📦 RN 打包统计:', relativePath, `${(stats.size / 1024).toFixed(2)} KB`)
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
path: relativePath,
|
|
15
|
+
size: stats.size,
|
|
16
|
+
sizeKB: Math.round(stats.size / 1024 * 100) / 100,
|
|
17
|
+
lastModified: stats.mtime.toISOString(),
|
|
18
|
+
extension: path.extname(filePath),
|
|
19
|
+
}
|
|
20
|
+
} catch (error: any) {
|
|
21
|
+
console.warn(`⚠️ 无法获取文件信息: ${filePath}`, error.message)
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { toRelativePath } from '../utils/fileUtils'
|
|
2
|
+
import { InverseDependencyInfo } from '../types'
|
|
3
|
+
|
|
4
|
+
export function collectInverseDependencies(filePath: string, inverseDependencies: string[]): InverseDependencyInfo | null {
|
|
5
|
+
try {
|
|
6
|
+
const relativePath = toRelativePath(filePath)
|
|
7
|
+
|
|
8
|
+
if (!inverseDependencies || inverseDependencies.length === 0) {
|
|
9
|
+
return null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const relativeInverseDeps = inverseDependencies
|
|
13
|
+
.filter(dep => dep && typeof dep === 'string')
|
|
14
|
+
.map(dep => toRelativePath(dep))
|
|
15
|
+
.filter(dep => dep && !dep.startsWith('..'))
|
|
16
|
+
|
|
17
|
+
if (relativeInverseDeps.length === 0) {
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log(`🔗 反向依赖收集: ${relativePath} <- ${relativeInverseDeps.length} 个依赖者`)
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
path: relativePath,
|
|
25
|
+
dependents: relativeInverseDeps,
|
|
26
|
+
dependentCount: relativeInverseDeps.length,
|
|
27
|
+
}
|
|
28
|
+
} catch (error: any) {
|
|
29
|
+
console.warn(`⚠️ 收集反向依赖失败: ${filePath}`, error.message)
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const SAVE_DIR = 'rn-package-analyse'
|
|
2
|
+
|
|
3
|
+
export const OUTPUT_FILES = {
|
|
4
|
+
HTML: 'rn-package-analyse.html',
|
|
5
|
+
ICONS_INLINE: 'icons-inline.js',
|
|
6
|
+
TAILWIND_JS: 'tailwindcss.js',
|
|
7
|
+
PACKAGE_DATA: 'rn-bundle-files-data.js',
|
|
8
|
+
TREE_DATA: 'rn-bundle-tree-data.js',
|
|
9
|
+
INVERSE_DEPS: 'rn-bundle-inverse-deps-data.js',
|
|
10
|
+
} as const
|
|
11
|
+
|
|
12
|
+
export const SAVE_DELAY = 3000
|
|
13
|
+
export const TREE_DISPLAY_LIMIT = 5
|
|
14
|
+
export const TOP_DEPENDED_LIMIT = 10
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { SAVE_DELAY } from './config'
|
|
2
|
+
import { collectFileInfo } from './collectors/fileCollector'
|
|
3
|
+
import { collectInverseDependencies } from './collectors/inverseDependencyCollector'
|
|
4
|
+
import { buildTreeStructure } from './builders/treeBuilder'
|
|
5
|
+
import { writeFileInfo } from './writers/fileInfoWriter'
|
|
6
|
+
import { writeTreeAnalysis } from './writers/treeWriter'
|
|
7
|
+
import { writeInverseDependencies } from './writers/inverseDependencyWriter'
|
|
8
|
+
import { FileInfo, InverseDependencyInfo } from './types'
|
|
9
|
+
|
|
10
|
+
const files = new Map<string, FileInfo>()
|
|
11
|
+
const inverseDependenciesMap = new Map<string, InverseDependencyInfo>()
|
|
12
|
+
let saveTimeout: ReturnType<typeof setTimeout> | null = null
|
|
13
|
+
|
|
14
|
+
function onCollectFileInfo(filePath: string): void {
|
|
15
|
+
const info = collectFileInfo(filePath)
|
|
16
|
+
if (info) {
|
|
17
|
+
files.set(info.path, info)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function onCollectInverseDependencies(filePath: string, deps: string[]): void {
|
|
22
|
+
const info = collectInverseDependencies(filePath, deps)
|
|
23
|
+
if (info) {
|
|
24
|
+
inverseDependenciesMap.set(info.path, info)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function scheduleSave(): void {
|
|
29
|
+
if (saveTimeout) clearTimeout(saveTimeout)
|
|
30
|
+
saveTimeout = setTimeout(saveAll, SAVE_DELAY)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function saveAll(): void {
|
|
34
|
+
if (files.size === 0) {
|
|
35
|
+
console.log('⚠️ 没有收集到文件信息')
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const fileInfoArray = Array.from(files.values())
|
|
40
|
+
const totalSize = fileInfoArray.reduce((sum, f) => sum + f.size, 0)
|
|
41
|
+
|
|
42
|
+
writeFileInfo(fileInfoArray)
|
|
43
|
+
|
|
44
|
+
const treeData = buildTreeStructure(fileInfoArray, totalSize)
|
|
45
|
+
writeTreeAnalysis(treeData)
|
|
46
|
+
|
|
47
|
+
writeInverseDependencies(Array.from(inverseDependenciesMap.values()))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export { onCollectFileInfo as collectFileInfo, onCollectInverseDependencies as collectInverseDependencies, scheduleSave }
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as fileInfoCollector from './fileInfoCollector'
|
|
2
|
+
import { MetroModule, ProcessModuleFilter } from './types'
|
|
3
|
+
|
|
4
|
+
export const createProcessModuleFilter = (originalProcessModuleFilter?: ProcessModuleFilter): ProcessModuleFilter => {
|
|
5
|
+
return function (module: MetroModule): boolean {
|
|
6
|
+
if (module && module.path) {
|
|
7
|
+
fileInfoCollector.collectFileInfo(module.path)
|
|
8
|
+
fileInfoCollector.collectInverseDependencies(module.path, Array.from(module.inverseDependencies))
|
|
9
|
+
fileInfoCollector.scheduleSave()
|
|
10
|
+
}
|
|
11
|
+
return originalProcessModuleFilter ? originalProcessModuleFilter(module) : true
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type { FileInfo, FileTypeStats, InverseDependencyInfo, TreeNode, TreeOutputNode, TreeData, MetroModule, ProcessModuleFilter } from './types'
|