react-native-bundle-discovery 1.0.0 → 1.1.0-rc.14

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 CHANGED
@@ -2,7 +2,7 @@ const path = require("path");
2
2
 
3
3
  module.exports = {
4
4
  name: "react-native-bundle-discovery",
5
- data: () => require("./tmp/before_metro-stats.json"),
5
+ data: () => require("./tmp/rainbow-metro-stats.json"),
6
6
  setup: path.resolve(__dirname, "setup.js"),
7
7
  view: {
8
8
  assets: [
@@ -14,6 +14,7 @@ module.exports = {
14
14
  path.resolve(__dirname, "pages/package.js"),
15
15
  // Custom views
16
16
  path.resolve(__dirname, "views/highcharts.css"),
17
+ path.resolve(__dirname, "views/prettify.js"),
17
18
  path.resolve(__dirname, "views/highcharts.js"),
18
19
  path.resolve(__dirname, "views/foamtree.js"),
19
20
  ],
package/README.md CHANGED
@@ -1,17 +1,25 @@
1
1
  # react-native-bundle-discovery
2
2
 
3
- > [!WARNING]
4
- > Currently, everything is in a very early stage. The project is not yet ready for use.
5
-
6
-
7
3
  A simple package that helps developers visualize and analyze the bundle size of React Native apps.
8
4
  With this tool, you can easily explore your app's codebase, identify large or heavy packages, and inspect the structure of modules and code within your project.
9
5
 
10
6
  <img width="800" alt="" src="./assets/img.png" />
11
7
 
8
+
9
+ ### What you should consider when analyzing your bundle
10
+
11
+ 1. if you use **React 19** then it's time to remove a `prop-types` package from your bundle.
12
+ 2. Search for `development|debug|dev|storybook` modules in your production JS bundle (it's a dead code that should not be there).
13
+ 3. Also check polyfills duplicates (search example: `url|fetch|crypto|buffer|base-?64`).
14
+ 4. Verify that you don't have any similar packages in your bundle (search example: `object|just-|debounce|ramda|lodash|underscore`). I often see that devs use both `lodash/debounce`, `lodash.debounce` and `debounce` in their projects. Or mix `ramda`, `underscore` and `lodash`._
15
+ 5. Try to find `package.json$` files in your bundle. They are often used to get only the package `version/name` but all other fields are not needed in the bundle.
16
+ 6. Please provide your ideas soo other devs can benefit from them :)
17
+
18
+
12
19
  ### Setup:
13
20
 
14
21
  #### 1. Install
22
+
15
23
  ```bash
16
24
  yarn add -D react-native-bundle-discovery
17
25
  ```
@@ -23,9 +31,9 @@ yarn add -D react-native-bundle-discovery
23
31
  const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
24
32
  +const {createSerializer} = require('react-native-bundle-discovery');
25
33
 
26
- +const mySerializer = createSerializer({
34
+ +const mySerializer = createSerializer({
27
35
  + includeCode: true, // Useful if you want to compare source/bundle code (but a report file will be larger)
28
- + projectRoot: __dirname,
36
+ + projectRoot: __dirname,
29
37
  + //^^^ ⚠️ WARNING: In a monorepo setup, this should point to the monorepo root,
30
38
  + // not the individual package directory.
31
39
  +});
@@ -53,16 +61,40 @@ npx react-native bundle \
53
61
  --assets-dest ios/assets
54
62
  ```
55
63
 
56
- #### 4. View the report
64
+ #### 4. View the report in the browser
57
65
 
58
66
  Run webserver to view the report:
59
67
 
60
68
  ```bash
61
- npx react-native-bundle-discovery metro-stats.json
69
+ npx react-native-bundle-discovery server metro-stats.json [--port <port>]
70
+ ```
71
+
72
+ #### 4. Build the HTML report
73
+
74
+ Run the following command to generate an HTML report from the JSON file:
75
+
76
+ ```bash
77
+ npx react-native-bundle-discovery build metro-stats.json
62
78
  ```
63
79
 
80
+
64
81
  ---
65
82
 
83
+ ### `createSerializer(optiosn: Options)`
84
+
85
+ | Prop | Default value | Description |
86
+ | ---------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
87
+ | serializer: Function | Default serializer | A custom serializer function. If not provided, a default serializer is used. |
88
+ | projectRoot: string | Required | The root directory of the project. ⚠️ In a monorepo setup, this should point to the monorepo root, not the individual package directory. |
89
+ | outputJsonPath: string | `<root>/metro-stats.json` | The path where the JSON report will be saved. Defaults to `metro-stats.json` in project root. |
90
+ | includeCode: boolean | `true` | Whether to include the source and output code in the JSON report. |
91
+
92
+ ### Financial Contributors
93
+
94
+ Become a financial contributor at [OpenCollective](https://opencollective.com/react-native-bundle-discovery) or [GitHub Sponsors](https://github.com/sponsors/retyui)
95
+
96
+ ### Other
97
+
66
98
  **Similar projects:**
67
99
 
68
100
  - https://github.com/expo/atlas
@@ -72,8 +104,7 @@ npx react-native-bundle-discovery metro-stats.json
72
104
  - https://github.com/statoscope/statoscope
73
105
  - https://github.com/relative-ci/bundle-stats/tree/master/packages/cli
74
106
 
75
- ---
107
+ **Built using [Discovery.js](https://github.com/discoveryjs/discovery):**
76
108
 
77
- **Built using Discovery.js:**
78
109
  - Build blocks for pages: https://discoveryjs.github.io/discovery/#views-showcase
79
110
  - Jora syntax: https://discoveryjs.github.io/jora/#article:jora-syntax-operators
package/lib/.tmp.js ADDED
@@ -0,0 +1,17 @@
1
+ module.exports = {
2
+ "name": "react-native-bundle-discovery",
3
+ "data": () => require("/Users/davyd.narbutovich/open-source/bundle-discovery/tmp/rocket-chat-metro-stats.json"),
4
+ "setup": "/Users/davyd.narbutovich/open-source/bundle-discovery/setup.js",
5
+ "view": {
6
+ "assets": [
7
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/views/global.css",
8
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/pages/default.js",
9
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/pages/module.js",
10
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/pages/package.js",
11
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/views/highcharts.css",
12
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/views/prettify.js",
13
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/views/highcharts.js",
14
+ "/Users/davyd.narbutovich/open-source/bundle-discovery/views/foamtree.js"
15
+ ]
16
+ }
17
+ };
package/lib/bin.js CHANGED
@@ -1,52 +1,74 @@
1
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");
2
+ const yargs = require("yargs");
3
+ const { hideBin } = require("yargs/helpers");
7
4
 
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
- );
5
+ yargs(hideBin(process.argv))
6
+ .command(
7
+ "server <file> [port]",
8
+ "🚀 Run a web server to show a Metro bundler stat report",
9
+ (yargs) => {
10
+ return yargs
11
+ .positional("file", {
12
+ describe: "Path to the Metro bundler report json file",
13
+ type: "string",
14
+ demandOption: true,
15
+ })
16
+ .positional("port", {
17
+ describe:
18
+ "🔌 Port to start the server on (also can be set with PORT env. var.)",
19
+ type: "number",
20
+ default: 8079,
21
+ });
22
+ },
23
+ (argv) => {
24
+ const { serve } = require("./server.js");
25
+ serve(argv.file, argv.port, argv.verbose);
26
+ },
27
+ )
28
+ .command(
29
+ "build <file>",
30
+ "📦 Build a HTML report from the Metro bundler stat file",
31
+ (yargs) => {
32
+ return yargs
33
+ .positional("file", {
34
+ describe: "Path to the Metro bundler report json file",
35
+ type: "string",
36
+ demandOption: true,
37
+ })
38
+ .option("output", {
39
+ alias: "o",
40
+ describe: "Path for a build result",
41
+ type: "string",
42
+ default: ".bundle-discovery",
43
+ })
44
+ .option("clean", {
45
+ alias: "c",
46
+ describe: "Clean the output directory before emit a build files",
47
+ type: "boolean",
48
+ default: true,
49
+ })
50
+ .option("single-file", {
51
+ alias: "s",
52
+ describe: "Output a report build as a single HTML file",
53
+ type: "boolean",
54
+ default: false,
55
+ });
56
+ },
57
+ (argv) => {
58
+ const { buildHtmlPage } = require("./build.js");
59
+ buildHtmlPage(
60
+ argv.file,
61
+ argv.output,
62
+ argv.clean,
63
+ argv.singleFile,
64
+ argv.verbose,
65
+ );
66
+ },
67
+ )
68
+ .option("verbose", {
69
+ alias: "v",
70
+ type: "boolean",
71
+ description: "Run with verbose logging",
72
+ })
73
+ .help()
74
+ .parse();
package/lib/build.js ADDED
@@ -0,0 +1,130 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const chalk = require("chalk");
4
+ const { build } = require("@discoveryjs/cli");
5
+ const config = require("../.discoveryrc.js");
6
+
7
+ function buildHtmlPage(filePath, output, clean, singleFile, verbose) {
8
+ if (verbose) {
9
+ console.info(
10
+ `Building HTML page from file: ${chalk.green(filePath)} options: ${JSON.stringify(
11
+ { output, clean, singleFile },
12
+ null,
13
+ 2,
14
+ )}`,
15
+ );
16
+ }
17
+
18
+ if (!filePath) {
19
+ console.error(
20
+ `Usage: '${chalk.green("npx react-native-bundle-discovery build <path-to-file>")}', Please provide a path to a JSON file.`,
21
+ );
22
+ process.exit(1);
23
+ }
24
+
25
+ const jsonFilePath = path.resolve(process.cwd(), filePath);
26
+ if (verbose) {
27
+ console.info(
28
+ `Loading JSON file from: ${chalk.green(jsonFilePath)}, base directory: ${chalk.green(process.cwd())}`,
29
+ );
30
+ }
31
+
32
+ let fullJsonPath;
33
+
34
+ try {
35
+ fullJsonPath = require.resolve(jsonFilePath);
36
+ } catch (err) {
37
+ console.error(`Error loading file: ${chalk.red(jsonFilePath)}`);
38
+ console.error(err.message);
39
+ process.exit(1);
40
+ }
41
+
42
+ const configFile = path.resolve(__dirname, "./.tmp.js");
43
+
44
+ if (verbose) {
45
+ console.info(
46
+ `Creating temporary config file at: ${chalk.green(configFile)}, for discovery.js`,
47
+ );
48
+ }
49
+ fs.writeFileSync(
50
+ configFile,
51
+ `module.exports = ${JSON.stringify(
52
+ { ...config, data: "<tmp>" },
53
+ null,
54
+ 1,
55
+ ).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
56
+ );
57
+
58
+ if (verbose) {
59
+ console.info(`Building HTML page with options:`, {
60
+ output,
61
+ clean,
62
+ singleFile,
63
+ });
64
+ }
65
+
66
+ const _config = {
67
+ name: "Bundle Discovery",
68
+ // version: null,
69
+ // description: null,
70
+ mode: "single",
71
+ // models: [ [Object] ],
72
+ colorScheme: "auto",
73
+ download: true,
74
+ upload: false,
75
+ embed: false,
76
+ encodings: false,
77
+ // view: {
78
+ // inspector: true,
79
+ // router: true,
80
+ // noscript: null,
81
+ // serveOnlyAssets: [],
82
+ // // assets: [],
83
+ // bundles: null,
84
+ // // warnings: []
85
+ // },
86
+ // favicon: '/Users/davyd.narbutovich/open-source/bb-recap/node_modules/@discoveryjs/discovery/lib/favicon.png',
87
+ // icon: '/Users/davyd.narbutovich/open-source/bb-recap/node_modules/@discoveryjs/discovery/lib/logo.svg',
88
+ // routers: []
89
+ };
90
+ const _options = {
91
+ singleFile,
92
+ clean,
93
+ output,
94
+ //
95
+ cache: true,
96
+ // cachedir: '/Users/davyd.narbutovich/open-source/bb-recap/.discoveryjs-cache',
97
+ checkCacheTtl: false,
98
+ minify: true,
99
+ dataCompression: true,
100
+ sourcemap: false,
101
+ embed: "by-config",
102
+ experimentalJsonxl: false,
103
+ // output: '/Users/davyd.narbutovich/open-source/bb-recap/build',
104
+ scriptFormat: "esm",
105
+ scriptExternal: [],
106
+ // singleFile: true,
107
+ // clean: false,
108
+ data: true,
109
+ excludeModelOnDataFail: false,
110
+ prettyData: false,
111
+ modelDownload: false,
112
+ modelDataUpload: true,
113
+ modelResetCache: false,
114
+ serveOnlyAssets: true,
115
+ dev: true,
116
+ config: configFile,
117
+ configFile: configFile,
118
+ };
119
+ build(_options, _config, configFile).then((result) => {
120
+ console.log(
121
+ `✅ HTML page built successfully at: ${chalk.green(
122
+ path.resolve(output, "index.html").replace(process.cwd() + "/", ""),
123
+ )}`,
124
+ );
125
+ });
126
+ }
127
+
128
+ module.exports = {
129
+ buildHtmlPage,
130
+ };
@@ -106,7 +106,7 @@ function createJsonReport({
106
106
  return acc;
107
107
  }, {}),
108
108
  rootFolder,
109
- packages: toPackages(dependencies),
109
+ packages: toPackages(preModules).concat(toPackages(dependencies)),
110
110
  modules: preModules
111
111
  .map((m) => toModuleStruct(m, includeCode))
112
112
  .concat(dependencies.map((m) => toModuleStruct(m, includeCode))),
package/lib/server.js ADDED
@@ -0,0 +1,77 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const chalk = require("chalk");
4
+ const { createServer } = require("@discoveryjs/cli");
5
+ const config = require("../.discoveryrc.js");
6
+
7
+ function serve(filePath, port, verbose) {
8
+ const PORT = process.env.PORT || port;
9
+
10
+ if (verbose) {
11
+ console.info(
12
+ `start server with file: ${filePath} on port: ${port} ${process.env.PORT ? `(${chalk.yellow("PORT")} env)` : ""}`,
13
+ );
14
+ }
15
+
16
+ if (!filePath) {
17
+ console.error(
18
+ `Usage: '${chalk.green("npx react-native-bundle-discovery server <path-to-file>")}', Please provide a path to a JSON file.`,
19
+ );
20
+ process.exit(1);
21
+ }
22
+
23
+ const jsonFilePath = path.resolve(process.cwd(), filePath);
24
+ if (verbose) {
25
+ console.info(
26
+ `Loading JSON file from: ${chalk.green(jsonFilePath)}, base directory: ${chalk.green(process.cwd())}`,
27
+ );
28
+ }
29
+
30
+ let fullJsonPath;
31
+
32
+ try {
33
+ fullJsonPath = require.resolve(jsonFilePath);
34
+ } catch (err) {
35
+ console.error(`❌ Error loading file: ${chalk.red(jsonFilePath)}`);
36
+ console.error(err.message);
37
+ process.exit(1);
38
+ }
39
+
40
+ const configFile = path.resolve(__dirname, "./.tmp.js");
41
+
42
+ if (verbose) {
43
+ console.info(
44
+ `Creating temporary config file at: ${chalk.green(configFile)}, for discovery.js`,
45
+ );
46
+ }
47
+ fs.writeFileSync(
48
+ configFile,
49
+ `module.exports = ${JSON.stringify(
50
+ { ...config, data: "<tmp>" },
51
+ null,
52
+ 1,
53
+ ).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
54
+ );
55
+
56
+ if (verbose) {
57
+ console.info(`Running server with config: ${chalk.green(configFile)}`);
58
+ }
59
+
60
+ createServer({
61
+ cache: false,
62
+ minify: true,
63
+ dev: false,
64
+ config: configFile,
65
+ configFile,
66
+ }).then((server) =>
67
+ server.listen(PORT, () =>
68
+ console.log(
69
+ `✅ Server listen on ${chalk.green(`http://localhost:${PORT}`)}`,
70
+ ),
71
+ ),
72
+ );
73
+ }
74
+
75
+ module.exports = {
76
+ serve,
77
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-bundle-discovery",
3
- "version": "1.0.0",
3
+ "version": "1.1.0-rc.14",
4
4
  "main": "index.js",
5
5
  "bin": "lib/bin.js",
6
6
  "repository": "git@github.com:retyui/react-native-bundle-discovery.git",
@@ -12,10 +12,13 @@
12
12
  "build": "npx discovery-build --config .discoveryrc.js --output build --serve-only-assets --single-file"
13
13
  },
14
14
  "dependencies": {
15
+ "@babel/parser": "^7.0.0",
15
16
  "@discoveryjs/cli": "2.14.2",
16
17
  "@discoveryjs/discovery": "1.0.0-beta.93",
17
18
  "chalk": "^4.1.2",
18
- "highcharts": "12.2.0"
19
+ "highcharts": "12.2.0",
20
+ "prettier": "^3.5.3",
21
+ "yargs": "^17.7.2"
19
22
  },
20
23
  "peerDependencies": {
21
24
  "metro": "*"
@@ -35,8 +38,5 @@
35
38
  "prepare.js",
36
39
  "queryHelpers.js",
37
40
  ".discoveryrc.js"
38
- ],
39
- "devDependencies": {
40
- "prettier": "3.5.3"
41
- }
41
+ ]
42
42
  }
package/pages/_common.js CHANGED
@@ -234,10 +234,11 @@ const metadata = {
234
234
 
235
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
236
 
237
- function getCopyToClipboardButton({ textToCopy, className }) {
237
+ function getCopyToClipboardButton({ textToCopy, className, text }) {
238
238
  return {
239
239
  view: "button",
240
240
  className: `${className ?? ""} copy-to-clipboard`,
241
+ text,
241
242
  data: `{ textToCopy: ${textToCopy} }`,
242
243
  onClick(elm, data) {
243
244
  clearTimeout(elm.__timer);
package/pages/default.js CHANGED
@@ -3,6 +3,7 @@ const {
3
3
  getModulesTree,
4
4
  getPackage,
5
5
  getPackageList,
6
+ getCopyToClipboardButton,
6
7
  } = require("./_common");
7
8
 
8
9
  const topMetaData = [
@@ -141,9 +142,25 @@ discovery.page.define("default", [
141
142
  {
142
143
  when: `#.id="${TABS.PACKAGES}"`,
143
144
  content: [
145
+ getCopyToClipboardButton({
146
+ text: "Copy list",
147
+ className: "copy-before-ask-chatgpt",
148
+ textToCopy: `"- " + modules.filter(=> path has "node_modules").group(=> path.getModulesName()).map(=> $.key).join("\\n- ")`,
149
+ }),
150
+ {
151
+ view: "link",
152
+ className: "ask-chatgpt view-button",
153
+ text: "and Ask ChatGPT",
154
+ external: true,
155
+ data: `{
156
+ href: $.askChatGPTAboutPackages()
157
+ }`,
158
+ },
144
159
  {
145
160
  view: "content-filter",
161
+ //
146
162
  data: `${getPackage(`modules.filter(=> path has "node_modules")`)}.sort(pkgInstances desc, size desc)`,
163
+ className: "packages-content",
147
164
  name: "filterByPathStr",
148
165
  content: getPackageList({
149
166
  showCopiesBadge: true,
package/pages/module.js CHANGED
@@ -119,11 +119,29 @@ discovery.page.define("module", {
119
119
  view: "block",
120
120
  className: "width-50p",
121
121
  content: [
122
- "h5: 'Output'",
123
122
  {
124
- view: "source",
125
- syntax: "ts",
126
- source: "=$.currentModule.output.code",
123
+ view: "h5",
124
+ content: 'text:"Output"',
125
+ },
126
+ {
127
+ view: "context",
128
+ modifiers: [
129
+ {
130
+ view: "checkbox",
131
+ name: "prettify",
132
+ checked: true,
133
+ className: "prettify-checkbox",
134
+ content: 'text:"Prettify"',
135
+ },
136
+ ],
137
+ content: [
138
+ {
139
+ view: "source-prettify",
140
+ syntax: "ts",
141
+ source:
142
+ "=(#.prettify ? $.currentModule.output.code.prettifyJS() : $.currentModule.output.code)",
143
+ },
144
+ ],
127
145
  },
128
146
  ],
129
147
  },
package/pages/package.js CHANGED
@@ -98,6 +98,17 @@ discovery.page.define("package", {
98
98
  `html: '<span class="my-icon-inline my-icon-12">${externalLinkHtml}</span>'`,
99
99
  ],
100
100
  },
101
+ {
102
+ view: "link",
103
+ external: true,
104
+ data: `{ href: "https://bundlejs.com/?q=" + currentPkgWithUniqVer, q: currentPkgWithUniqVer }`,
105
+ content: [
106
+ {
107
+ view: "html",
108
+ data: `'<img class="bundlejs-badge-img" src="https://deno.bundlejs.com/?q=' + q + '&badge=detailed" />'`,
109
+ },
110
+ ],
111
+ },
101
112
  ],
102
113
  },
103
114
 
package/prepare.js CHANGED
@@ -66,9 +66,6 @@ function prepare(data) {
66
66
  m.source.code = m.source.code
67
67
  .replaceAll(";", ";\n\n")
68
68
  .replaceAll(",", ",\n ");
69
- m.output.code = m.output.code
70
- .replaceAll(";", ";\n\n")
71
- .replaceAll(",", ",\n ");
72
69
  }
73
70
  });
74
71
 
package/queryHelpers.js CHANGED
@@ -1,4 +1,35 @@
1
1
  const helpers = {
2
+ prettifyMap: new Map(),
3
+ prettifyJS(sourceStr) {
4
+ if (helpers.prettifyMap.has(sourceStr)) {
5
+ return helpers.prettifyMap.get(sourceStr);
6
+ }
7
+
8
+ const prettier = require("prettier/standalone");
9
+ const prettierPluginBabel = require("prettier/plugins/babel");
10
+ const prettierPluginEstree = require("prettier/plugins/estree");
11
+
12
+ return prettier
13
+ .format(sourceStr, {
14
+ parser: "babel",
15
+ plugins: [prettierPluginBabel, prettierPluginEstree],
16
+ })
17
+ .then((formatted) => {
18
+ helpers.prettifyMap.set(sourceStr, formatted);
19
+ return formatted;
20
+ });
21
+ },
22
+ askChatGPTAboutPackages() {
23
+ const prompt = `I have a list of JavaScript packages from my React Native bundle (see below). I want to optimize bundle size by identifying similar or redundant packages — such as multiple versions of similar libraries (e.g., lodash, lodash-es, underscore, etc.), duplicate utilities, or overlapping functionality (e.g., date libraries like moment, dayjs, date-fns).
24
+
25
+ Please do the following:
26
+
27
+ 1. Group similar or overlapping packages together.
28
+ 2. For each group, suggest which one to keep and which ones to consider removing.
29
+ 3. For each group, provide a regex string that can be used to filter those packages from the list (e.g., in grep, find, or search tools).
30
+ 4. Keep the output concise and copy-paste friendly.`;
31
+ return `https://chat.openai.com/?prompt=${encodeURIComponent(prompt)}`;
32
+ },
2
33
  plural(count, [singular, plural]) {
3
34
  return count === 1 ? singular : plural;
4
35
  },
package/views/global.css CHANGED
@@ -125,3 +125,45 @@ hr{
125
125
  background: red;
126
126
  opacity: 1;
127
127
  }
128
+ .prettify-checkbox{
129
+ position: absolute;
130
+ right: 31px;
131
+ margin-top: -26px;
132
+ z-index: 1;
133
+ }
134
+ .ask-chatgpt{
135
+ color: var(--discovery-view-button-color, #000);
136
+ text-decoration: none;
137
+ vertical-align: middle;
138
+ position: absolute;
139
+ z-index: 9;
140
+ margin-top: 12px;
141
+ margin-left: 105px !important;
142
+ height: 35px;
143
+ }
144
+ .ask-chatgpt::before {
145
+ content: '”';
146
+ font-size: 2em;
147
+ vertical-align: middle;
148
+ font-family: math;
149
+ margin-right: 0.2em;
150
+ display: inline-block;
151
+ height: 15px;
152
+ }
153
+ .packages-content>.view-input{
154
+ padding-left: 265px;
155
+ }
156
+ .copy-before-ask-chatgpt{
157
+ width: 95px;
158
+ background-position: left center;
159
+ padding-left: 25px;
160
+ position: absolute;
161
+ z-index: 9;
162
+ margin-top: 12px;
163
+ height: 35px;
164
+ }
165
+ .bundlejs-badge-img{
166
+ line-height: 0;
167
+ margin-bottom: -5px;
168
+ margin-left: 10px;
169
+ }
@@ -0,0 +1,17 @@
1
+ discovery.view.define(
2
+ "source-prettify",
3
+ async function renderPrettify(el, config, data, context) {
4
+ await this.render(
5
+ el,
6
+ this.composeConfig([
7
+ {
8
+ ...config,
9
+ source: await config.source,
10
+ view: "source",
11
+ },
12
+ ]),
13
+ data,
14
+ context,
15
+ );
16
+ },
17
+ );