react-native-bundle-discovery 1.0.0-rc.10
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/.discoveryrc.js +21 -0
- package/README.md +51 -0
- package/index.js +1 -0
- package/lib/bin.js +52 -0
- package/lib/customSerializer.js +167 -0
- package/package.json +43 -0
- package/pages/_common.js +266 -0
- package/pages/default.js +194 -0
- package/pages/module.js +296 -0
- package/pages/package.js +150 -0
- package/prepare.js +105 -0
- package/queryHelpers.js +329 -0
- package/setup.js +12 -0
- package/vendors/foamtree.js +9008 -0
- package/views/_theme.js +35 -0
- package/views/foamtree.js +135 -0
- package/views/global.css +127 -0
- package/views/highcharts.css +1302 -0
- package/views/highcharts.js +46 -0
package/.discoveryrc.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const path = require("path");
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
name: "react-native-bundle-discovery",
|
|
5
|
+
data: () => require("./tmp/before_metro-stats.json"),
|
|
6
|
+
setup: path.resolve(__dirname, "setup.js"),
|
|
7
|
+
view: {
|
|
8
|
+
assets: [
|
|
9
|
+
// Global styles
|
|
10
|
+
path.resolve(__dirname, "views/global.css"),
|
|
11
|
+
// Pages
|
|
12
|
+
path.resolve(__dirname, "pages/default.js"),
|
|
13
|
+
path.resolve(__dirname, "pages/module.js"),
|
|
14
|
+
path.resolve(__dirname, "pages/package.js"),
|
|
15
|
+
// Custom views
|
|
16
|
+
path.resolve(__dirname, "views/highcharts.css"),
|
|
17
|
+
path.resolve(__dirname, "views/highcharts.js"),
|
|
18
|
+
path.resolve(__dirname, "views/foamtree.js"),
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
};
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# react-native-bundle-discovery
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> Currently, everything is in a very early stage. The project is not yet ready for use.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
<img width="800" src="https://github.com/user-attachments/assets/211145d4-8fe7-499b-a372-9d752e878772" />
|
|
8
|
+
|
|
9
|
+
### Setup:
|
|
10
|
+
|
|
11
|
+
#### 1. Install
|
|
12
|
+
```bash
|
|
13
|
+
yarn add -D react-native-bundle-discovery
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
#### 2. Add to your metro.config.js
|
|
17
|
+
|
|
18
|
+
```diff
|
|
19
|
+
// metro.config.js
|
|
20
|
+
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
21
|
+
+const {createSerializer} = require('react-native-bundle-discovery');
|
|
22
|
+
|
|
23
|
+
+const mySerializer = createSerializer({
|
|
24
|
+
+ includeCode: true, // Useful if you want to compare source/bundle code (but a report file will be larger)
|
|
25
|
+
+ projectRoot: __dirname,
|
|
26
|
+
+ //^^^ ⚠️ WARNING: In a monorepo setup, this should point to the monorepo root,
|
|
27
|
+
+ // not the individual package directory.
|
|
28
|
+
+})
|
|
29
|
+
|
|
30
|
+
-const config = {};
|
|
31
|
+
+const config = {
|
|
32
|
+
+ serializer: {
|
|
33
|
+
+ customSerializer: mySerializer
|
|
34
|
+
+ },
|
|
35
|
+
+};
|
|
36
|
+
|
|
37
|
+
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Similar projects:**
|
|
41
|
+
- https://github.com/expo/atlas
|
|
42
|
+
- https://github.com/v3ron/expo-atlas-without-expo
|
|
43
|
+
- https://github.com/callstack/react-native-bundle-visualizer
|
|
44
|
+
- https://github.com/webpack-contrib/webpack-bundle-analyzer
|
|
45
|
+
- https://github.com/statoscope/statoscope
|
|
46
|
+
- https://github.com/relative-ci/bundle-stats/tree/master/packages/cli
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
**Links:**
|
|
50
|
+
- Build blocks for pages: https://discoveryjs.github.io/discovery/#views-showcase
|
|
51
|
+
- Jora syntax: https://discoveryjs.github.io/jora/#article:jora-syntax-operators
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./lib/customSerializer");
|
package/lib/bin.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const chalk = require("chalk");
|
|
5
|
+
const { createServer } = require("@discoveryjs/cli");
|
|
6
|
+
const config = require("../.discoveryrc.js");
|
|
7
|
+
|
|
8
|
+
const filePath = process.argv[2];
|
|
9
|
+
|
|
10
|
+
if (!filePath) {
|
|
11
|
+
console.error(
|
|
12
|
+
`Usage: '${chalk.green("npx react-native-bundle-discovery <path-to-file>")}', Please provide a path to a JSON file.`,
|
|
13
|
+
);
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const jsonFilePath = path.resolve(process.cwd(), filePath);
|
|
18
|
+
|
|
19
|
+
let fullJsonPath;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
fullJsonPath = require.resolve(jsonFilePath);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.error(`Error loading file: ${chalk.red(jsonFilePath)}`);
|
|
25
|
+
console.error(err.message);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const PORT = process.env.PORT || 8079;
|
|
30
|
+
|
|
31
|
+
const configFile = path.resolve(__dirname, "./.tmp.js");
|
|
32
|
+
|
|
33
|
+
fs.writeFileSync(
|
|
34
|
+
configFile,
|
|
35
|
+
`module.exports = ${JSON.stringify(
|
|
36
|
+
{ ...config, data: "<tmp>" },
|
|
37
|
+
null,
|
|
38
|
+
1,
|
|
39
|
+
).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
createServer({
|
|
43
|
+
cache: false,
|
|
44
|
+
minify: true,
|
|
45
|
+
dev: false,
|
|
46
|
+
config: configFile,
|
|
47
|
+
configFile,
|
|
48
|
+
}).then((server) =>
|
|
49
|
+
server.listen(PORT, () =>
|
|
50
|
+
console.log(`Server listen on ${chalk.green(`http://localhost:${PORT}`)}`),
|
|
51
|
+
),
|
|
52
|
+
);
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const { writeFileSync, existsSync } = require("node:fs");
|
|
2
|
+
const { resolve } = require("node:path");
|
|
3
|
+
const { Buffer } = require("node:buffer");
|
|
4
|
+
|
|
5
|
+
const NAME = require("../package.json").name;
|
|
6
|
+
|
|
7
|
+
function getDefaultSerializer() {
|
|
8
|
+
const bundleToString = require("metro/src/lib/bundleToString");
|
|
9
|
+
const baseJSBundle = require("metro/src/DeltaBundler/Serializers/baseJSBundle");
|
|
10
|
+
|
|
11
|
+
return (entryPoint, preModules, graph, options) =>
|
|
12
|
+
bundleToString(baseJSBundle(entryPoint, preModules, graph, options)).code;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getStringSizeInBytes(str) {
|
|
16
|
+
return Buffer.byteLength(str, "utf8");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* `/Users/i/app/node_modules/metro/node_modules/@babel/runtime/helpers/createClass.js` -> `@babel/runtime`
|
|
21
|
+
* `/Users/i/app/node_modules/react/jsx-runtime.js` -> `react`
|
|
22
|
+
*/
|
|
23
|
+
function getPackageNameFromPath(path) {
|
|
24
|
+
const parts = path.split("node_modules/");
|
|
25
|
+
const lastPart = parts[parts.length - 1];
|
|
26
|
+
if (lastPart.startsWith("@")) {
|
|
27
|
+
return lastPart.split("/").slice(0, 2).join("/");
|
|
28
|
+
}
|
|
29
|
+
return lastPart.split("/")[0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* `/Users/i/app/node_modules/metro/node_modules/@babel/runtime/helpers/createClass.js` -> `/Users/i/app/node_modules/metro/node_modules/@babel/runtime`
|
|
34
|
+
* `/Users/i/app/node_modules/react/jsx-runtime.js` -> `/Users/i/app/node_modules/react`
|
|
35
|
+
*/
|
|
36
|
+
function getPackageAbsolutePath(path, pkgName) {
|
|
37
|
+
const parts = path.split("node_modules/");
|
|
38
|
+
parts[parts.length - 1] = pkgName;
|
|
39
|
+
return parts.join("node_modules/");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function toPackages(modules) {
|
|
43
|
+
const packages = new Map();
|
|
44
|
+
modules.forEach((module) => {
|
|
45
|
+
if (!module.path.includes("node_modules/")) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const pkgName = getPackageNameFromPath(module.path);
|
|
50
|
+
const absolutePkgPath = getPackageAbsolutePath(module.path, pkgName);
|
|
51
|
+
|
|
52
|
+
if (!packages.has(absolutePkgPath)) {
|
|
53
|
+
packages.set(absolutePkgPath, {
|
|
54
|
+
name: pkgName,
|
|
55
|
+
absolutePath: absolutePkgPath,
|
|
56
|
+
version: require(resolve(absolutePkgPath, "package.json")).version,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return Array.from(packages.values()).sort((a, b) =>
|
|
62
|
+
a.name.localeCompare(b.name),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function toModuleStruct(m, includeCode) {
|
|
67
|
+
const sourceCode = m.getSource().toString("utf8");
|
|
68
|
+
const outputCode = m.output[0].data.code;
|
|
69
|
+
return {
|
|
70
|
+
path: m.path,
|
|
71
|
+
source: {
|
|
72
|
+
code: includeCode ? sourceCode : "",
|
|
73
|
+
lineCount: sourceCode.split("\n").length,
|
|
74
|
+
sizeInBytes: getStringSizeInBytes(sourceCode),
|
|
75
|
+
},
|
|
76
|
+
output: {
|
|
77
|
+
code: includeCode ? outputCode : "",
|
|
78
|
+
lineCount: m.output[0].data.lineCount,
|
|
79
|
+
sizeInBytes: getStringSizeInBytes(outputCode),
|
|
80
|
+
},
|
|
81
|
+
dependencies: Array.from(m?.dependencies?.values?.() ?? []).map((e) => ({
|
|
82
|
+
absolutePath: e.absolutePath,
|
|
83
|
+
name: e.data.name,
|
|
84
|
+
})),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function createJsonReport({
|
|
89
|
+
graph,
|
|
90
|
+
entryPoint,
|
|
91
|
+
includeEnvs,
|
|
92
|
+
preModules,
|
|
93
|
+
includeCode,
|
|
94
|
+
outputJsonPath,
|
|
95
|
+
rootFolder,
|
|
96
|
+
}) {
|
|
97
|
+
const dependencies = Array.from(graph.dependencies.values());
|
|
98
|
+
|
|
99
|
+
const stats = {
|
|
100
|
+
date: Date.now(),
|
|
101
|
+
entryPoint,
|
|
102
|
+
transformOptions: graph.transformOptions,
|
|
103
|
+
envs: includeEnvs.reduce((acc, envName) => {
|
|
104
|
+
acc[envName] = process.env[envName];
|
|
105
|
+
return acc;
|
|
106
|
+
}, {}),
|
|
107
|
+
rootFolder,
|
|
108
|
+
packages: toPackages(dependencies),
|
|
109
|
+
modules: preModules
|
|
110
|
+
.map((m) => toModuleStruct(m, includeCode))
|
|
111
|
+
.concat(dependencies.map((m) => toModuleStruct(m, includeCode))),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
writeFileSync(outputJsonPath, JSON.stringify(stats));
|
|
115
|
+
|
|
116
|
+
console.log(`[${NAME}]: Saved stats to`, outputJsonPath);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Creates a custom serializer function for Metro bundler, which generates a JSON report
|
|
121
|
+
* and optionally modifies the serialization process.
|
|
122
|
+
*
|
|
123
|
+
* @param {Object} options - Configuration options for the serializer.
|
|
124
|
+
* @param {Function} [options.serializer] - A custom serializer function. If not provided, a default serializer is used.
|
|
125
|
+
* @param {string} options.projectRoot - The root directory of the project. Must exist.
|
|
126
|
+
* @param {string} [options.outputJsonPath] - The path where the JSON report will be saved. Defaults to "metro-stats.json" in the project root.
|
|
127
|
+
* @param {boolean} [options.includeCode=true] - Whether to include the source and output code in the JSON report.
|
|
128
|
+
* @param {string[]} [options.includeEnvs=[]] - A list of environment variable names to include in the JSON report.
|
|
129
|
+
* @returns {Function} - A custom serializer function to be used by Metro.
|
|
130
|
+
* @throws {Error} - Throws an error if the project root does not exist.
|
|
131
|
+
*/
|
|
132
|
+
function createSerializer({
|
|
133
|
+
serializer,
|
|
134
|
+
projectRoot,
|
|
135
|
+
outputJsonPath,
|
|
136
|
+
includeCode = true,
|
|
137
|
+
includeEnvs = [],
|
|
138
|
+
} = {}) {
|
|
139
|
+
const mySerializer = serializer || getDefaultSerializer();
|
|
140
|
+
|
|
141
|
+
if (!existsSync(projectRoot)) {
|
|
142
|
+
throw new Error(`[${NAME}]: Project root does not exist: ${projectRoot}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const myOutputJsonPath =
|
|
146
|
+
outputJsonPath ?? resolve(projectRoot, "metro-stats.json");
|
|
147
|
+
|
|
148
|
+
function customSerializer(entryPoint, preModules, graph, options) {
|
|
149
|
+
const code = mySerializer(entryPoint, preModules, graph, options);
|
|
150
|
+
|
|
151
|
+
createJsonReport({
|
|
152
|
+
graph,
|
|
153
|
+
entryPoint,
|
|
154
|
+
includeEnvs,
|
|
155
|
+
preModules,
|
|
156
|
+
includeCode,
|
|
157
|
+
outputJsonPath: myOutputJsonPath,
|
|
158
|
+
rootFolder: projectRoot,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return code;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return customSerializer;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = { createSerializer };
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-native-bundle-discovery",
|
|
3
|
+
"version": "1.0.0-rc.10",
|
|
4
|
+
"main": "index.js",
|
|
5
|
+
"bin": "lib/bin.js",
|
|
6
|
+
"repository": "git@github.com:retyui/react-native-bundle-discovery.git",
|
|
7
|
+
"author": "David <4661784+retyui@users.noreply.github.com>",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"format": "prettier --write .",
|
|
11
|
+
"start": "NODE_ENV=development npx discovery --config .discoveryrc.js",
|
|
12
|
+
"build": "npx discovery-build --config .discoveryrc.js --output build --serve-only-assets --single-file"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@discoveryjs/cli": "2.14.2",
|
|
16
|
+
"@discoveryjs/discovery": "1.0.0-beta.93",
|
|
17
|
+
"chalk": "^5.4.1",
|
|
18
|
+
"highcharts": "12.2.0"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"metro": "*"
|
|
22
|
+
},
|
|
23
|
+
"peerDependenciesMeta": {
|
|
24
|
+
"metro": {
|
|
25
|
+
"optional": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"vendors",
|
|
30
|
+
"lib",
|
|
31
|
+
"views",
|
|
32
|
+
"pages",
|
|
33
|
+
"index.js",
|
|
34
|
+
"setup.js",
|
|
35
|
+
"prepare.js",
|
|
36
|
+
"queryHelpers.js",
|
|
37
|
+
".discoveryrc.js"
|
|
38
|
+
],
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"prettier": "3.5.3"
|
|
41
|
+
},
|
|
42
|
+
"packageManager": "yarn@4.6.0"
|
|
43
|
+
}
|
package/pages/_common.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
const platformColor = `transformOptions.platform = 'android' ? 'rgba(194, 239, 116, .4)' : 'rgba(119, 31, 218, .4)'`;
|
|
2
|
+
|
|
3
|
+
function getPackage(entry) {
|
|
4
|
+
return `
|
|
5
|
+
$packages: $.packages;
|
|
6
|
+
$totalSize: $.modules.sum(=>output.sizeInBytes);
|
|
7
|
+
$toModule: => {
|
|
8
|
+
ext: $.path.getFileExtension(),
|
|
9
|
+
name: $.path,
|
|
10
|
+
size: $.output.sizeInBytes.formatBytes(),
|
|
11
|
+
percent: ($.output.sizeInBytes / $totalSize).percent(3),
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
${entry}.group(=> path.getModulesName())
|
|
15
|
+
.map(=> ({
|
|
16
|
+
$pkgName: $.key;
|
|
17
|
+
pkgName: $pkgName, // example: lodash
|
|
18
|
+
size: $.value.sum(=>output.sizeInBytes),
|
|
19
|
+
pkgInstances: $.value
|
|
20
|
+
.group(=> path.split($pkgName).pick(0) + $pkgName)
|
|
21
|
+
.map(=> {
|
|
22
|
+
$pkgNameWithPath: $.key;
|
|
23
|
+
pkgName: $pkgNameWithPath, // example: node_modules/lodash
|
|
24
|
+
version: $packages.[path = $pkgNameWithPath][0].version,
|
|
25
|
+
size: $.value.sum(=> output.sizeInBytes),
|
|
26
|
+
modules: $.value.map(=> $.$toModule()),
|
|
27
|
+
}),
|
|
28
|
+
}))`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getPackageList({
|
|
32
|
+
data,
|
|
33
|
+
itemPkgName,
|
|
34
|
+
showCopiesBadge,
|
|
35
|
+
expanded,
|
|
36
|
+
limit,
|
|
37
|
+
subLimit,
|
|
38
|
+
}) {
|
|
39
|
+
return {
|
|
40
|
+
view: "list",
|
|
41
|
+
data,
|
|
42
|
+
emptyText: "⚠️ No packages found",
|
|
43
|
+
limit,
|
|
44
|
+
item: {
|
|
45
|
+
view: "tree",
|
|
46
|
+
expanded,
|
|
47
|
+
itemConfig: {
|
|
48
|
+
content: [
|
|
49
|
+
itemPkgName,
|
|
50
|
+
"text: ' '",
|
|
51
|
+
"pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
|
|
52
|
+
showCopiesBadge
|
|
53
|
+
? {
|
|
54
|
+
view: "pill-badge",
|
|
55
|
+
when: "pkgInstances.size() > 1",
|
|
56
|
+
data: "(pkgInstances.size() - 1).pluralBadge(['copy','copies'], '+')",
|
|
57
|
+
color: "rgba(255, 0, 0, 0.35)",
|
|
58
|
+
}
|
|
59
|
+
: null,
|
|
60
|
+
{
|
|
61
|
+
view: "pill-badge",
|
|
62
|
+
data: "pkgInstances.modules.size().pluralBadge(['file','files'])",
|
|
63
|
+
},
|
|
64
|
+
].filter(Boolean),
|
|
65
|
+
children: `$.pkgInstances`,
|
|
66
|
+
itemConfig: {
|
|
67
|
+
view: "tree-leaf",
|
|
68
|
+
limit: subLimit,
|
|
69
|
+
content: [
|
|
70
|
+
//
|
|
71
|
+
"text:pkgName",
|
|
72
|
+
"text:' '",
|
|
73
|
+
"pill-badge:{ text: 'v' + version, color: '#0af' }",
|
|
74
|
+
"pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
|
|
75
|
+
{
|
|
76
|
+
view: "pill-badge",
|
|
77
|
+
data: "modules.size().pluralBadge(['file','files'])",
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
children: `$.modules`,
|
|
81
|
+
itemConfig: {
|
|
82
|
+
view: "tree-leaf",
|
|
83
|
+
content: getTreeModule({ hasPercent: true }),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getTreeModule({ hasTextMatch = false, hasPercent = false } = {}) {
|
|
92
|
+
return [
|
|
93
|
+
"pill-badge:{ text: ext, color: ext.getExtColor() }",
|
|
94
|
+
hasTextMatch
|
|
95
|
+
? {
|
|
96
|
+
view: "link",
|
|
97
|
+
content: hasTextMatch ? "text-match" : "text",
|
|
98
|
+
data: `{
|
|
99
|
+
href: name.pageLink("module", {}),
|
|
100
|
+
text: name,
|
|
101
|
+
match: #.filterByPathStr
|
|
102
|
+
}`,
|
|
103
|
+
}
|
|
104
|
+
: {
|
|
105
|
+
view: "link",
|
|
106
|
+
data: `{ href: $.name.pageLink("module", {}), text: $.name }`,
|
|
107
|
+
},
|
|
108
|
+
"text:' '",
|
|
109
|
+
{
|
|
110
|
+
view: "badge",
|
|
111
|
+
when: "isEntry",
|
|
112
|
+
text: "Entrypoint",
|
|
113
|
+
color: "gold",
|
|
114
|
+
textColor: "black",
|
|
115
|
+
},
|
|
116
|
+
"pill-badge:{ text: size, color: 'rgba(120, 177, 9, 0.35)' }",
|
|
117
|
+
hasPercent
|
|
118
|
+
? "pill-badge:{ text: percent, color: 'rgba(120, 177, 9, 0.35)' }"
|
|
119
|
+
: null,
|
|
120
|
+
].filter(Boolean);
|
|
121
|
+
}
|
|
122
|
+
function getModulesTree({ data, limit }) {
|
|
123
|
+
if (!data) {
|
|
124
|
+
throw new Error("[getModulesTree]: data is required");
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
view: "content-filter",
|
|
128
|
+
data,
|
|
129
|
+
name: "filterByPathStr",
|
|
130
|
+
content: {
|
|
131
|
+
view: "list",
|
|
132
|
+
limit,
|
|
133
|
+
data: ".[name ~= #.filterByPathStr]",
|
|
134
|
+
emptyText: "⚠️ No modules found",
|
|
135
|
+
item: {
|
|
136
|
+
view: "tree",
|
|
137
|
+
expanded: false,
|
|
138
|
+
itemConfig: {
|
|
139
|
+
content: getTreeModule({ hasTextMatch: true }),
|
|
140
|
+
children: `
|
|
141
|
+
[
|
|
142
|
+
{
|
|
143
|
+
title:'Imported by modules',
|
|
144
|
+
data: $.reasons,
|
|
145
|
+
type: 'reasons',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
title:'Similar copies',
|
|
149
|
+
data: $.duplicates,
|
|
150
|
+
type: 'duplicates',
|
|
151
|
+
}
|
|
152
|
+
].filter(=> $.data.size() > 0)
|
|
153
|
+
`,
|
|
154
|
+
itemConfig: {
|
|
155
|
+
view: "switch",
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
when: 'type="reasons"',
|
|
159
|
+
content: {
|
|
160
|
+
view: "tree-leaf",
|
|
161
|
+
content: [
|
|
162
|
+
"text:title",
|
|
163
|
+
"text:' '",
|
|
164
|
+
"badge:{ text: $.data.size() }",
|
|
165
|
+
],
|
|
166
|
+
children: `$.data`,
|
|
167
|
+
itemConfig: {
|
|
168
|
+
view: "tree-leaf",
|
|
169
|
+
content: getTreeModule(),
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
when: 'type="duplicates"',
|
|
175
|
+
content: {
|
|
176
|
+
view: "tree-leaf",
|
|
177
|
+
content: [
|
|
178
|
+
"text:title",
|
|
179
|
+
"text:' '",
|
|
180
|
+
"badge:{ text: $.data.size() }",
|
|
181
|
+
],
|
|
182
|
+
children: `$.data`,
|
|
183
|
+
itemConfig: {
|
|
184
|
+
view: "tree-leaf",
|
|
185
|
+
content: getTreeModule(),
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const metadata = {
|
|
198
|
+
platform: {
|
|
199
|
+
when: "transformOptions.platform",
|
|
200
|
+
view: "badge",
|
|
201
|
+
data: `{ prefix: 'Platform: ', text: transformOptions.platform, color: ${platformColor} }`,
|
|
202
|
+
},
|
|
203
|
+
size: {
|
|
204
|
+
when: "modules.filter(=> $.path has 'node_modules').size()",
|
|
205
|
+
view: "badge",
|
|
206
|
+
data: `{ prefix: 'Size: ', text: modules.sum(=>output.sizeInBytes).formatBytes(), color: ${platformColor} }`,
|
|
207
|
+
},
|
|
208
|
+
node_modules_size: {
|
|
209
|
+
when: "modules.filter(=> $.path has 'node_modules').size()",
|
|
210
|
+
view: "badge",
|
|
211
|
+
data: "{ prefix: 'node_modules: ', text: modules.filter(=> $.path has 'node_modules').sum(=>output.sizeInBytes).formatBytes(), color: 'rgba(255, 0, 0, 0.35)' }",
|
|
212
|
+
},
|
|
213
|
+
source_code_size: {
|
|
214
|
+
when: "modules.filter(=> $.path has 'node_modules').size()",
|
|
215
|
+
view: "badge",
|
|
216
|
+
data: `
|
|
217
|
+
// vars
|
|
218
|
+
$totalSize: modules.sum(=>output.sizeInBytes);
|
|
219
|
+
$thirdPartySize: modules.filter(=> $.path has 'node_modules').sum(=>output.sizeInBytes);
|
|
220
|
+
// return data
|
|
221
|
+
{ prefix: 'Source code: ', text: ($totalSize - $thirdPartySize).formatBytes(), color: 'rgba(148, 111, 234, 0.5)' }`,
|
|
222
|
+
},
|
|
223
|
+
is_dev: {
|
|
224
|
+
when: "transformOptions.dev != null",
|
|
225
|
+
view: "badge",
|
|
226
|
+
data: "{ prefix: '__DEV__: ', text: transformOptions.dev }",
|
|
227
|
+
},
|
|
228
|
+
is_minified: {
|
|
229
|
+
when: "transformOptions.minify != null",
|
|
230
|
+
view: "badge",
|
|
231
|
+
data: "{ prefix: 'Minify: ', text: transformOptions.minify }",
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const externalLinkHtml = `<svg class="my-icon my-icon-link" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14 21 3"></path></svg>`;
|
|
236
|
+
|
|
237
|
+
function getCopyToClipboardButton({ textToCopy, className }) {
|
|
238
|
+
return {
|
|
239
|
+
view: "button",
|
|
240
|
+
className: `${className ?? ""} copy-to-clipboard`,
|
|
241
|
+
data: `{ textToCopy: ${textToCopy} }`,
|
|
242
|
+
onClick(elm, data) {
|
|
243
|
+
clearTimeout(elm.__timer);
|
|
244
|
+
navigator.clipboard
|
|
245
|
+
.writeText(data.textToCopy)
|
|
246
|
+
.then(() => {
|
|
247
|
+
elm.classList.add("done");
|
|
248
|
+
elm.__timer = setTimeout(() => elm.classList.remove("done"), 2000);
|
|
249
|
+
})
|
|
250
|
+
.catch(() => {
|
|
251
|
+
elm.classList.add("err");
|
|
252
|
+
elm.__timer = setTimeout(() => elm.classList.remove("err"), 2000);
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
module.exports = {
|
|
259
|
+
getCopyToClipboardButton,
|
|
260
|
+
getPackage,
|
|
261
|
+
getPackageList,
|
|
262
|
+
externalLinkHtml,
|
|
263
|
+
getTreeModule,
|
|
264
|
+
getModulesTree,
|
|
265
|
+
metadata,
|
|
266
|
+
};
|