From b98d467bcff2723b46634c1dcb11d63c9bc38358 Mon Sep 17 00:00:00 2001 From: alexander-akait Date: Tue, 20 Jan 2026 20:50:02 +0300 Subject: [PATCH 1/4] refactor: apply `prettier` --- .babelrc | 9 +- .gitignore | 1 + .prettierignore | 5 + CHANGELOG.md | 1 + README.md | 55 ++-- client/.eslintrc.json | 5 +- client/components/Button.css | 5 +- client/components/Button.jsx | 27 +- client/components/Checkbox.jsx | 22 +- client/components/CheckboxList.jsx | 58 +++-- client/components/CheckboxListItem.jsx | 19 +- client/components/ContextMenu.css | 4 +- client/components/ContextMenu.jsx | 82 +++--- client/components/ContextMenuItem.jsx | 14 +- client/components/Dropdown.css | 5 +- client/components/Dropdown.jsx | 58 +++-- client/components/Icon.jsx | 43 ++- client/components/ModuleItem.jsx | 61 +++-- client/components/ModulesList.jsx | 22 +- client/components/ModulesTreemap.jsx | 310 ++++++++++++---------- client/components/Search.css | 5 +- client/components/Search.jsx | 45 ++-- client/components/Sidebar.css | 6 +- client/components/Sidebar.jsx | 118 +++++---- client/components/Switcher.jsx | 24 +- client/components/SwitcherItem.jsx | 8 +- client/components/ThemeToggle.jsx | 26 +- client/components/Tooltip.css | 6 +- client/components/Tooltip.jsx | 34 ++- client/components/Treemap.jsx | 87 ++++--- client/lib/PureComponent.jsx | 2 +- client/localStorage.js | 19 +- client/store.js | 84 +++--- client/utils.js | 2 +- client/viewer.css | 4 +- client/viewer.jsx | 47 ++-- gulpfile.js | 74 +++--- jest.config.js | 16 +- package-lock.json | 35 ++- package.json | 8 +- prettier.config.mjs | 6 + src/BundleAnalyzerPlugin.js | 87 ++++--- src/Logger.js | 29 +-- src/analyzer.js | 139 ++++++---- src/bin/analyzer.js | 151 ++++++----- src/index.js | 4 +- src/parseUtils.js | 345 ++++++++++++------------- src/sizeUtils.js | 16 +- src/statsUtils.js | 33 ++- src/template.js | 76 +++--- src/tree/BaseFolder.js | 36 +-- src/tree/ConcatenatedModule.js | 45 ++-- src/tree/ContentFolder.js | 16 +- src/tree/ContentModule.js | 16 +- src/tree/Folder.js | 43 +-- src/tree/Module.js | 28 +- src/tree/Node.js | 6 +- src/tree/utils.js | 6 +- src/utils.js | 39 ++- src/viewer.js | 130 ++++++---- test/Logger.js | 67 +++-- test/analyzer.js | 303 ++++++++++++---------- test/dev-server.js | 24 +- test/dev-server/webpack.config.js | 16 +- test/helpers.js | 91 ++++--- test/parseUtils.js | 28 +- test/plugin.js | 229 ++++++++-------- test/statsUtils.js | 63 ++--- test/utils.js | 72 +++--- test/viewer.js | 133 +++++----- webpack.config.js | 158 +++++------ 71 files changed, 2139 insertions(+), 1752 deletions(-) create mode 100644 .prettierignore create mode 100644 prettier.config.mjs diff --git a/.babelrc b/.babelrc index 249047b6..0cfadb6b 100644 --- a/.babelrc +++ b/.babelrc @@ -2,8 +2,11 @@ // Compiles sources, gulpfile and tests { "presets": [ - ["@babel/preset-env", { - "targets": {"node": "16.20.2"} - }] + [ + "@babel/preset-env", + { + "targets": { "node": "16.20.2" } + } + ] ] } diff --git a/.gitignore b/.gitignore index 9de1553d..8bfd8b36 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /samples node_modules npm-debug.log +.eslintcache diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..9c80b52e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +test/bundles/** +test/stats/** +test/output/** +samples/** +CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 744bf125..d1c0e104 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ _Note: Gaps between patch versions are faulty, broken or test releases._ * Add support for Zstandard compression ([#693](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/693) by [@bjohansebas](https://github.com/bjohansebas)) * **Internal** + * Prettier applied to the code base ([#693](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/693) by [@alexander-akait](https://github.com/alexander-akait)) * Update `sirv` dependency ([#692](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/692) by [@bjohansebas](https://github.com/bjohansebas)) * Update `ws` dependency ([#691](https://github.com/webpack-contrib/webpack-bundle-analyzer/pull/691) by [@bjohansebas](https://github.com/bjohansebas)) diff --git a/README.md b/README.md index 662ceeba..5c1d28ff 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,12 @@ yarn add -D webpack-bundle-analyzer

Usage (as a plugin)

```js -const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; +const BundleAnalyzerPlugin = + require("webpack-bundle-analyzer").BundleAnalyzerPlugin; module.exports = { - plugins: [ - new BundleAnalyzerPlugin() - ] -} + plugins: [new BundleAnalyzerPlugin()], +}; ``` It will create an interactive treemap visualization of the contents of all your bundles. @@ -39,7 +38,7 @@ It will create an interactive treemap visualization of the contents of all your This module will help you: -1. Realize what's *really* inside your bundle +1. Realize what's _really_ inside your bundle 2. Find out what modules make up the most of its size 3. Find modules that got there by mistake 4. Optimize it! @@ -53,22 +52,22 @@ And it also shows their gzipped, Brotli, or Zstandard sizes! new BundleAnalyzerPlugin(options?: object) ``` -|Name|Type|Description| -|:--:|:--:|:----------| -|**`analyzerMode`**|One of: `server`, `static`, `json`, `disabled`|Default: `server`. In `server` mode analyzer will start HTTP server to show bundle report. In `static` mode single HTML file with bundle report will be generated. In `json` mode single JSON file with bundle report will be generated. In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. | -|**`analyzerHost`**|`{String}`|Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server.| -|**`analyzerPort`**|`{Number}` or `auto`|Default: `8888`. Port that will be used in `server` mode to start HTTP server. If `analyzerPort` is `auto`, the operating system will assign an arbitrary unused port | -|**`analyzerUrl`**|`{Function}` called with `{ listenHost: string, listenHost: string, boundAddress: server.address}`. [server.address comes from Node.js](https://nodejs.org/api/net.html#serveraddress)| Default: `http://${listenHost}:${boundAddress.port}`. The URL printed to console with server mode.| -|**`reportFilename`**|`{String}`|Default: `report.html`. Path to bundle report file that will be generated in `static` mode. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).| -|**`reportTitle`**|`{String\|function}`|Default: function that returns pretty printed current date and time. Content of the HTML `title` element; or a function of the form `() => string` that provides the content.| -|**`defaultSizes`**|One of: `stat`, `parsed`, `gzip`, `brotli`|Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean.| -|**`compressionAlgorithm`**|One of: `gzip`, `brotli`, `zstd`|Default: `gzip`. Compression type used to calculate the compressed module sizes.| -|**`openAnalyzer`**|`{Boolean}`|Default: `true`. Automatically open report in default browser.| -|**`generateStatsFile`**|`{Boolean}`|Default: `false`. If `true`, webpack stats JSON file will be generated in bundle output directory| -|**`statsFilename`**|`{String}`|Default: `stats.json`. Name of webpack stats JSON file that will be generated if `generateStatsFile` is `true`. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).| -|**`statsOptions`**|`null` or `{Object}`|Default: `null`. Options for `stats.toJson()` method. For example you can exclude sources of your modules from stats file with `source: false` option. [See more options here](https://webpack.js.org/configuration/stats/). | -|**`excludeAssets`**|`{null\|pattern\|pattern[]}` where `pattern` equals to `{String\|RegExp\|function}`|Default: `null`. Patterns that will be used to match against asset names to exclude them from the report. If pattern is a string it will be converted to RegExp via `new RegExp(str)`. If pattern is a function it should have the following signature `(assetName: string) => boolean` and should return `true` to *exclude* matching asset. If multiple patterns are provided asset should match at least one of them to be excluded. | -|**`logLevel`**|One of: `info`, `warn`, `error`, `silent`|Default: `info`. Used to control how much details the plugin outputs.| +| Name | Type | Description | +| :------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`analyzerMode`** | One of: `server`, `static`, `json`, `disabled` | Default: `server`. In `server` mode analyzer will start HTTP server to show bundle report. In `static` mode single HTML file with bundle report will be generated. In `json` mode single JSON file with bundle report will be generated. In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. | +| **`analyzerHost`** | `{String}` | Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server. | +| **`analyzerPort`** | `{Number}` or `auto` | Default: `8888`. Port that will be used in `server` mode to start HTTP server. If `analyzerPort` is `auto`, the operating system will assign an arbitrary unused port | +| **`analyzerUrl`** | `{Function}` called with `{ listenHost: string, listenHost: string, boundAddress: server.address}`. [server.address comes from Node.js](https://nodejs.org/api/net.html#serveraddress) | Default: `http://${listenHost}:${boundAddress.port}`. The URL printed to console with server mode. | +| **`reportFilename`** | `{String}` | Default: `report.html`. Path to bundle report file that will be generated in `static` mode. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config). | +| **`reportTitle`** | `{String\|function}` | Default: function that returns pretty printed current date and time. Content of the HTML `title` element; or a function of the form `() => string` that provides the content. | +| **`defaultSizes`** | One of: `stat`, `parsed`, `gzip`, `brotli` | Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean. | +| **`compressionAlgorithm`** | One of: `gzip`, `brotli`, `zstd` | Default: `gzip`. Compression type used to calculate the compressed module sizes. | +| **`openAnalyzer`** | `{Boolean}` | Default: `true`. Automatically open report in default browser. | +| **`generateStatsFile`** | `{Boolean}` | Default: `false`. If `true`, webpack stats JSON file will be generated in bundle output directory | +| **`statsFilename`** | `{String}` | Default: `stats.json`. Name of webpack stats JSON file that will be generated if `generateStatsFile` is `true`. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config). | +| **`statsOptions`** | `null` or `{Object}` | Default: `null`. Options for `stats.toJson()` method. For example you can exclude sources of your modules from stats file with `source: false` option. [See more options here](https://webpack.js.org/configuration/stats/). | +| **`excludeAssets`** | `{null\|pattern\|pattern[]}` where `pattern` equals to `{String\|RegExp\|function}` | Default: `null`. Patterns that will be used to match against asset names to exclude them from the report. If pattern is a string it will be converted to RegExp via `new RegExp(str)`. If pattern is a function it should have the following signature `(assetName: string) => boolean` and should return `true` to _exclude_ matching asset. If multiple patterns are provided asset should match at least one of them to be excluded. | +| **`logLevel`** | One of: `info`, `warn`, `error`, `silent` | Default: `info`. Used to control how much details the plugin outputs. |

Usage (as a CLI utility)

@@ -174,19 +173,21 @@ The Sidebar Menu can be opened by clicking the `>` button at the top left of the The Chunk Context Menu can be opened by right-clicking or `Ctrl`-clicking on a specific chunk in the report. It provides the following options: - * **Hide chunk:** Hides the selected chunk - * **Hide all other chunks:** Hides all chunks besides the selected one - * **Show all chunks:** Un-hides any hidden chunks, returning the report to its initial, unfiltered view +- **Hide chunk:** Hides the selected chunk +- **Hide all other chunks:** Hides all chunks besides the selected one +- **Show all chunks:** Un-hides any hidden chunks, returning the report to its initial, unfiltered view

Troubleshooting

### I don't see `gzip` or `parsed` sizes, it only shows `stat` size It happens when `webpack-bundle-analyzer` analyzes files that don't actually exist in your file system, for example when you work with `webpack-dev-server` that keeps all the files in RAM. If you use `webpack-bundle-analyzer` as a plugin you won't get any errors, however if you run it via CLI you get the error message in terminal: + ``` Error parsing bundle asset "your_bundle_name.bundle.js": no such file No bundles were parsed. Analyzer will show only original module sizes from stats file. ``` + To get more information about it you can read [issue #147](https://github.com/webpack/webpack-bundle-analyzer/issues/147).

Other tools

@@ -214,16 +215,12 @@ To get more information about it you can read [issue #147](https://github.com/we - [npm]: https://img.shields.io/npm/v/webpack-bundle-analyzer.svg [npm-url]: https://npmjs.com/package/webpack-bundle-analyzer - [node]: https://img.shields.io/node/v/webpack-bundle-analyzer.svg [node-url]: https://nodejs.org - [tests]: https://github.com/webpack/webpack-bundle-analyzer/actions/workflows/main.yml/badge.svg [tests-url]: https://github.com/webpack/webpack-bundle-analyzer/actions/workflows/main.yml - [downloads]: https://img.shields.io/npm/dt/webpack-bundle-analyzer.svg [downloads-url]: https://npmjs.com/package/webpack-bundle-analyzer diff --git a/client/.eslintrc.json b/client/.eslintrc.json index 667e6796..f2e071f1 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -1,8 +1,5 @@ { - "extends": [ - "th0r-react", - "../.eslintrc.json" - ], + "extends": ["th0r-react", "../.eslintrc.json"], "settings": { "react": { "version": "16.2" diff --git a/client/components/Button.css b/client/components/Button.css index 618baa60..6ed9213c 100644 --- a/client/components/Button.css +++ b/client/components/Button.css @@ -7,7 +7,10 @@ font: var(--main-font); outline: none; padding: 5px 7px; - transition: background .3s ease, border-color .3s ease, color .3s ease; + transition: + background 0.3s ease, + border-color 0.3s ease, + color 0.3s ease; white-space: nowrap; color: var(--text-primary); } diff --git a/client/components/Button.jsx b/client/components/Button.jsx index 2e00c7b5..ad9bdc06 100644 --- a/client/components/Button.jsx +++ b/client/components/Button.jsx @@ -1,39 +1,38 @@ -import cls from 'classnames'; -import s from './Button.css'; -import PureComponent from '../lib/PureComponent'; +import cls from "classnames"; +import s from "./Button.css"; +import PureComponent from "../lib/PureComponent"; export default class Button extends PureComponent { - render({active, toggle, className, children, ...props}) { + render({ active, toggle, className, children, ...props }) { const classes = cls(className, { [s.button]: true, [s.active]: active, - [s.toggle]: toggle + [s.toggle]: toggle, }); return ( - ); } get disabled() { - const {props} = this; - return ( - props.disabled || - (props.active && !props.toggle) - ); + const { props } = this; + return props.disabled || (props.active && !props.toggle); } handleClick = (event) => { this.elem.blur(); this.props.onClick(event); - } + }; - saveRef = elem => this.elem = elem; + saveRef = (elem) => (this.elem = elem); } diff --git a/client/components/Checkbox.jsx b/client/components/Checkbox.jsx index dfcd88e5..206620a9 100644 --- a/client/components/Checkbox.jsx +++ b/client/components/Checkbox.jsx @@ -1,28 +1,26 @@ -import {Component} from 'preact'; -import cls from 'classnames'; +import { Component } from "preact"; +import cls from "classnames"; -import s from './Checkbox.css'; +import s from "./Checkbox.css"; export default class Checkbox extends Component { - render() { - const {checked, className, children} = this.props; + const { checked, className, children } = this.props; return ( ); } handleChange = () => { this.props.onChange(!this.props.checked); - } - + }; } diff --git a/client/components/CheckboxList.jsx b/client/components/CheckboxList.jsx index 95c4a5bc..f8959be9 100644 --- a/client/components/CheckboxList.jsx +++ b/client/components/CheckboxList.jsx @@ -1,17 +1,16 @@ -import CheckboxListItem from './CheckboxListItem'; -import s from './CheckboxList.css'; -import PureComponent from '../lib/PureComponent'; +import CheckboxListItem from "./CheckboxListItem"; +import s from "./CheckboxList.css"; +import PureComponent from "../lib/PureComponent"; -const ALL_ITEM = Symbol('ALL_ITEM'); +const ALL_ITEM = Symbol("ALL_ITEM"); export default class CheckboxList extends PureComponent { - static ALL_ITEM = ALL_ITEM; constructor(props) { super(props); this.state = { - checkedItems: props.checkedItems || props.items + checkedItems: props.checkedItems || props.items, }; } @@ -19,44 +18,48 @@ export default class CheckboxList extends PureComponent { if (newProps.items !== this.props.items) { if (this.isAllChecked()) { // Preserving `all checked` state - this.setState({checkedItems: newProps.items}); + this.setState({ checkedItems: newProps.items }); this.informAboutChange(newProps.items); } else if (this.state.checkedItems.length) { // Checking only items that are in the new `items` array - const checkedItems = newProps.items.filter(item => - this.state.checkedItems.find(checkedItem => checkedItem.label === item.label) + const checkedItems = newProps.items.filter((item) => + this.state.checkedItems.find( + (checkedItem) => checkedItem.label === item.label, + ), ); - this.setState({checkedItems}); + this.setState({ checkedItems }); this.informAboutChange(checkedItems); } } else if (newProps.checkedItems !== this.props.checkedItems) { - this.setState({checkedItems: newProps.checkedItems}); + this.setState({ checkedItems: newProps.checkedItems }); } } render() { - const {label, items, renderLabel} = this.props; + const { label, items, renderLabel } = this.props; return (
-
- {label}: -
+
{label}:
- + onChange={this.handleToggleAllCheck} + > {renderLabel} - {items.map(item => - ( + + onChange={this.handleItemCheck} + > {renderLabel} - )} + ))}
); @@ -64,20 +67,22 @@ export default class CheckboxList extends PureComponent { handleToggleAllCheck = () => { const checkedItems = this.isAllChecked() ? [] : this.props.items; - this.setState({checkedItems}); + this.setState({ checkedItems }); this.informAboutChange(checkedItems); }; - handleItemCheck = item => { + handleItemCheck = (item) => { let checkedItems; if (this.isItemChecked(item)) { - checkedItems = this.state.checkedItems.filter(checkedItem => checkedItem !== item); + checkedItems = this.state.checkedItems.filter( + (checkedItem) => checkedItem !== item, + ); } else { checkedItems = [...this.state.checkedItems, item]; } - this.setState({checkedItems}); + this.setState({ checkedItems }); this.informAboutChange(checkedItems); }; @@ -86,11 +91,10 @@ export default class CheckboxList extends PureComponent { } isAllChecked() { - return (this.props.items.length === this.state.checkedItems.length); + return this.props.items.length === this.state.checkedItems.length; } informAboutChange(checkedItems) { setTimeout(() => this.props.onChange(checkedItems)); } - } diff --git a/client/components/CheckboxListItem.jsx b/client/components/CheckboxListItem.jsx index fd26c058..0c4cf31d 100644 --- a/client/components/CheckboxListItem.jsx +++ b/client/components/CheckboxListItem.jsx @@ -1,16 +1,14 @@ -import {Component} from 'preact'; +import { Component } from "preact"; -import Checkbox from './Checkbox'; -import CheckboxList from './CheckboxList'; -import s from './CheckboxList.css'; +import Checkbox from "./Checkbox"; +import CheckboxList from "./CheckboxList"; +import s from "./CheckboxList.css"; export default class CheckboxListItem extends Component { - render() { return (
- + {this.renderLabel()}
@@ -18,17 +16,16 @@ export default class CheckboxListItem extends Component { } renderLabel() { - const {children, item} = this.props; + const { children, item } = this.props; if (children) { return children(item); } - return (item === CheckboxList.ALL_ITEM) ? 'All' : item.label; + return item === CheckboxList.ALL_ITEM ? "All" : item.label; } handleChange = () => { this.props.onChange(this.props.item); - } - + }; } diff --git a/client/components/ContextMenu.css b/client/components/ContextMenu.css index b8441dfe..d786c892 100644 --- a/client/components/ContextMenu.css +++ b/client/components/ContextMenu.css @@ -9,7 +9,9 @@ opacity: 1; white-space: nowrap; visibility: visible; - transition: opacity .2s ease, visibility .2s ease; + transition: + opacity 0.2s ease, + visibility 0.2s ease; } .hidden { diff --git a/client/components/ContextMenu.jsx b/client/components/ContextMenu.jsx index 7706d369..982e9524 100644 --- a/client/components/ContextMenu.jsx +++ b/client/components/ContextMenu.jsx @@ -1,10 +1,10 @@ -import cls from 'classnames'; -import ContextMenuItem from './ContextMenuItem'; -import PureComponent from '../lib/PureComponent'; -import {store} from '../store'; -import {elementIsOutside} from '../utils'; +import cls from "classnames"; +import ContextMenuItem from "./ContextMenuItem"; +import PureComponent from "../lib/PureComponent"; +import { store } from "../store"; +import { elementIsOutside } from "../utils"; -import s from './ContextMenu.css'; +import s from "./ContextMenu.css"; export default class ContextMenu extends PureComponent { componentDidMount() { @@ -13,32 +13,50 @@ export default class ContextMenu extends PureComponent { componentDidUpdate(prevProps) { if (this.props.visible && !prevProps.visible) { - document.addEventListener('mousedown', this.handleDocumentMousedown, true); + document.addEventListener( + "mousedown", + this.handleDocumentMousedown, + true, + ); } else if (prevProps.visible && !this.props.visible) { - document.removeEventListener('mousedown', this.handleDocumentMousedown, true); + document.removeEventListener( + "mousedown", + this.handleDocumentMousedown, + true, + ); } } render() { - const {visible} = this.props; + const { visible } = this.props; const containerClassName = cls({ [s.container]: true, - [s.hidden]: !visible + [s.hidden]: !visible, }); const multipleChunksSelected = store.selectedChunks.length > 1; return ( -