├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── CHANGELOG.md
├── README.md
├── assets
└── log.png
├── client
├── assets
│ ├── icon-arrow-right.svg
│ ├── icon-chunk.svg
│ ├── icon-folder.svg
│ ├── icon-invisible.svg
│ ├── icon-module.svg
│ └── icon-pin.svg
├── components
│ ├── Button.css
│ ├── Button.jsx
│ ├── Checkbox.css
│ ├── Checkbox.jsx
│ ├── CheckboxList.css
│ ├── CheckboxList.jsx
│ ├── CheckboxListItem.jsx
│ ├── ContextMenu.css
│ ├── ContextMenu.jsx
│ ├── ContextMenuItem.css
│ ├── ContextMenuItem.jsx
│ ├── Icon.css
│ ├── Icon.jsx
│ ├── ModuleItem.css
│ ├── ModuleItem.jsx
│ ├── ModulesList.css
│ ├── ModulesList.jsx
│ ├── ModulesTreemap.css
│ ├── ModulesTreemap.jsx
│ ├── Search.css
│ ├── Search.jsx
│ ├── Sidebar.css
│ ├── Sidebar.jsx
│ ├── Switcher.css
│ ├── Switcher.jsx
│ ├── SwitcherItem.jsx
│ ├── Tooltip.css
│ ├── Tooltip.jsx
│ └── Treemap.jsx
├── lib
│ └── PureComponent.jsx
├── localStorage.js
├── store.js
├── utils.js
├── viewer.css
└── viewer.jsx
├── dist
├── cjs
│ ├── index.js
│ └── index.js.map
└── es
│ ├── index.js
│ ├── index.js.map
│ └── package.json
├── package.json
├── pnpm-lock.yaml
├── public
└── viewer.js
├── rollup.config.mjs
├── src
├── generateAdvise.ts
├── index.ts
├── logger.ts
├── parseModule.ts
├── pureBundle.ts
├── size.ts
├── template.ts
├── tree
│ ├── Folder.ts
│ ├── Module.ts
│ ├── Node.ts
│ └── utils.ts
├── utils.ts
└── viewer.ts
├── tsconfig.base.json
├── tsconfig.json
├── types
└── index.d.ts
└── webpack.config.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | insert_final_newline = true
14 | trim_trailing_whitespace = false
15 |
16 | [README.md]
17 | max_line_length = 170
18 |
19 | [*.yml]
20 | max_line_length = 500
21 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | .eslintrc.js
2 | **/dist
3 | **/node_modules
4 | **/public
5 | **/test/**/output
6 | packages/commonjs/test/fixtures
7 | packages/typescript/test/fixtures/syntax-error
8 |
9 | # temporary workaround for eslint bug where package.json is a directory
10 | packages/node-resolve/test/fixtures/package-json-in-path
11 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['rollup', 'plugin:import/typescript'],
3 | parserOptions: {
4 | project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'],
5 | tsconfigRootDir: __dirname
6 | },
7 | rules: {
8 | // disabling sort keys for now so we can get the rest of the linting shored up
9 | 'sort-keys': 'off',
10 | 'typescript-sort-keys/interface': 'off',
11 | 'import/extensions': 'off',
12 | 'import/no-unresolved': 'off'
13 | },
14 | overrides: [
15 | {
16 | files: ['**/fixtures/**'],
17 | rules: {
18 | 'no-console': 'off'
19 | }
20 | }
21 | ]
22 | };
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .nyc_output/
2 | coverage/
3 | dist/
4 | node_modules/
5 | output/
6 |
7 | .DS_Store
8 | .eslintcache
9 | coverage.lcov
10 | .pnpm-debug.log
11 | .idea
12 | .rollup.cache
13 |
14 | !packages/*/test/**/node_modules
15 | !packages/node-resolve/test/fixtures/**/node_modules
16 | !packages/commonjs/test/**/node_modules
17 | !packages/typescript/test/fixtures/**/node_modules
18 | !packages/typescript/test/fixtures/**/dist
19 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .github/ISSUE_TEMPLATE
2 | .github/ISSUE_TEMPLATE.md
3 | .github/PULL_REQUEST_TEMPLATE.md
4 | packages/json/test/fixtures/garbage/*
5 | packages/yaml/test/fixtures/**/*
6 | **/dist/
7 | **/fixtures/
8 | **/snapshots/
9 | **/public
10 | pnpm-lock.yaml
11 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | // Note: This file is necessary so that prettier writes which happen in hooks and scripts match the
2 | // same config that we're using from the eslint-config package.
3 | module.exports = require('eslint-config-rollup/prettier');
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jerry2023/rollup-plugin-bundle-analyzer/a6902b601b0c2b50ebc1a09a089a50e52bd9e385/CHANGELOG.md
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
Rollup Bundle Analyzer
7 |
Visualize size of rollup output files with an interactive zoomable treemap.
8 |
9 |
10 | Install
11 |
12 | ```bash
13 | # NPM
14 | npm install --save-dev rollup-plugin-bundle-analyzer
15 | # Yarn
16 | yarn add -D rollup-plugin-bundle-analyzer
17 | ```
18 |
19 | Usage (as a plugin)
20 |
21 | ```js
22 | import bundleAnalyzer from "rollup-plugin-bundle-analyzer";
23 |
24 |
25 | plugins: [
26 | bundleAnalyzer(),
27 | vue(),
28 | ]
29 | ```
30 |
31 | It will create an interactive treemap visualization of the contents of all your bundles.
32 |
33 | 
34 |
35 | This module will help you:
36 |
37 | 1. Realize what's *really* inside your bundle
38 | 2. Find out what modules make up the most of its size
39 | 3. Find modules that got there by mistake
40 | 4. Optimize it!
41 |
42 | And the best thing is it supports minified bundles! It parses them to get real size of bundled modules.
43 | And it also shows their gzipped sizes!
44 |
45 | Options (for plugin)
46 |
47 | ```js
48 | bundleAnalyzer(options?: object)
49 | ```
50 |
51 | |Name|Type|Description|
52 | |:--:|:--:|:----------|
53 | |**`analyzerMode`**|One of: `server`, `static`, `json` | 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.
54 | |**`host`**|`{String}`|Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server.|
55 | |**`port`**|`{Number}` or `auto`|Default: `9800`. Port that will be used in `server` mode to start HTTP server.|
56 | |**`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 |
57 | |**`generateStatsFile`**|`{Boolean}`|Default: `false`. If `true`, rollup stats JSON file will be generated in bundle output directory|
58 | |**`statsFilename`**|`{String}`|Default: `stats.json`. Name of rollup 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 |
59 | |**`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. |
60 |
61 | ### `input`
62 |
63 | This is the "input" size of your files, before any transformations like
64 | minification.
65 |
66 | ### `render`
67 |
68 | This is the "output" size of your files. If you're using a rollup plugin such
69 | as Uglify, then this value will reflect the minified size of your code.
70 |
71 | ### `gzip`
72 |
73 | This is the size of running the parsed bundles/modules through gzip compression.
74 |
75 | Selecting Which Chunks to Display
76 |
77 | When opened, the report displays all of the chunks for your project. It's possible to filter to a more specific list of chunks by using the sidebar or the chunk context menu.
78 |
79 | ### Sidebar
80 |
81 | The Sidebar Menu can be opened by clicking the `>` button at the top left of the report. You can select or deselect chunks to display under the "Show chunks" heading there.
82 |
83 | ### Chunk Context Menu
84 |
85 | 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:
86 |
87 | * **Hide chunk:** Hides the selected chunk
88 | * **Hide all other chunks:** Hides all chunks besides the selected one
89 | * **Show all chunks:** Un-hides any hidden chunks, returning the report to its initial, unfiltered view
90 |
--------------------------------------------------------------------------------
/assets/log.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Jerry2023/rollup-plugin-bundle-analyzer/a6902b601b0c2b50ebc1a09a089a50e52bd9e385/assets/log.png
--------------------------------------------------------------------------------
/client/assets/icon-arrow-right.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/assets/icon-chunk.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/assets/icon-folder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/assets/icon-invisible.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/assets/icon-module.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/assets/icon-pin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/components/Button.css:
--------------------------------------------------------------------------------
1 | .button {
2 | background: #fff;
3 | border: 1px solid #aaa;
4 | border-radius: 4px;
5 | cursor: pointer;
6 | display: inline-block;
7 | font: var(--main-font);
8 | outline: none;
9 | padding: 5px 7px;
10 | transition: background .3s ease;
11 | white-space: nowrap;
12 | }
13 |
14 | .button:focus,
15 | .button:hover {
16 | background: #ffefd7;
17 | }
18 |
19 | .button.active {
20 | background: #ffa500;
21 | color: #000;
22 | }
23 |
24 | .button[disabled] {
25 | cursor: default;
26 | }
27 |
--------------------------------------------------------------------------------
/client/components/Button.jsx:
--------------------------------------------------------------------------------
1 | import cls from 'classnames';
2 | import s from './Button.css';
3 | import PureComponent from '../lib/PureComponent';
4 |
5 | export default class Button extends PureComponent {
6 | render({active, toggle, className, children, ...props}) {
7 | const classes = cls(className, {
8 | [s.button]: true,
9 | [s.active]: active,
10 | [s.toggle]: toggle
11 | });
12 |
13 | return (
14 |
20 | {children}
21 |
22 | );
23 | }
24 |
25 | get disabled() {
26 | const {props} = this;
27 | return (
28 | props.disabled ||
29 | (props.active && !props.toggle)
30 | );
31 | }
32 |
33 | handleClick = (event) => {
34 | this.elem.blur();
35 | this.props.onClick(event);
36 | }
37 |
38 | saveRef = elem => this.elem = elem;
39 | }
40 |
--------------------------------------------------------------------------------
/client/components/Checkbox.css:
--------------------------------------------------------------------------------
1 | .label {
2 | cursor: pointer;
3 | display: inline-block;
4 | }
5 |
6 | .checkbox {
7 | cursor: pointer;
8 | }
9 |
10 | .itemText {
11 | margin-left: 3px;
12 | position: relative;
13 | top: -2px;
14 | vertical-align: middle;
15 | }
16 |
--------------------------------------------------------------------------------
/client/components/Checkbox.jsx:
--------------------------------------------------------------------------------
1 | import {Component} from 'preact';
2 | import cls from 'classnames';
3 |
4 | import s from './Checkbox.css';
5 |
6 | export default class Checkbox extends Component {
7 |
8 | render() {
9 | const { checked, className, children } = this.props;
10 |
11 | return (
12 |
13 |
17 |
18 | {children}
19 |
20 |
21 | );
22 | }
23 |
24 | handleChange = () => {
25 | this.props.onChange(!this.props.checked);
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/client/components/CheckboxList.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font: var(--main-font);
3 | white-space: nowrap;
4 | }
5 |
6 | .label {
7 | font-size: 11px;
8 | font-weight: bold;
9 | margin-bottom: 7px;
10 | }
11 |
12 | .item + .item {
13 | margin-top: 1px;
14 | }
15 |
--------------------------------------------------------------------------------
/client/components/CheckboxList.jsx:
--------------------------------------------------------------------------------
1 | import CheckboxListItem from './CheckboxListItem';
2 | import s from './CheckboxList.css';
3 | import PureComponent from '../lib/PureComponent';
4 |
5 | const ALL_ITEM = Symbol('ALL_ITEM');
6 |
7 | export default class CheckboxList extends PureComponent {
8 |
9 | static ALL_ITEM = ALL_ITEM;
10 |
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | checkedItems: props.checkedItems || props.items
15 | };
16 | }
17 |
18 | componentWillReceiveProps(newProps) {
19 | if (newProps.items !== this.props.items) {
20 | if (this.isAllChecked()) {
21 | // Preserving `all checked` state
22 | this.setState({checkedItems: newProps.items});
23 | this.informAboutChange(newProps.items);
24 | } else if (this.state.checkedItems.length) {
25 | // Checking only items that are in the new `items` array
26 | const checkedItems = newProps.items.filter(item =>
27 | this.state.checkedItems.find(checkedItem => checkedItem.label === item.label)
28 | );
29 |
30 | this.setState({checkedItems});
31 | this.informAboutChange(checkedItems);
32 | }
33 | } else if (newProps.checkedItems !== this.props.checkedItems) {
34 | this.setState({checkedItems: newProps.checkedItems});
35 | }
36 | }
37 |
38 | render() {
39 | const {label, items, renderLabel} = this.props;
40 |
41 | return (
42 |
43 |
44 | {label}:
45 |
46 |
47 |
50 | {renderLabel}
51 |
52 | {items.map(item =>
53 |
57 | {renderLabel}
58 |
59 | )}
60 |
61 |
62 | );
63 | }
64 |
65 | handleToggleAllCheck = () => {
66 | const checkedItems = this.isAllChecked() ? [] : this.props.items;
67 | this.setState({checkedItems});
68 | this.informAboutChange(checkedItems);
69 | };
70 |
71 | handleItemCheck = item => {
72 | let checkedItems;
73 |
74 | if (this.isItemChecked(item)) {
75 | checkedItems = this.state.checkedItems.filter(checkedItem => checkedItem !== item);
76 | } else {
77 | checkedItems = [...this.state.checkedItems, item];
78 | }
79 |
80 | this.setState({checkedItems});
81 | this.informAboutChange(checkedItems);
82 | };
83 |
84 | isItemChecked(item) {
85 | return this.state.checkedItems.includes(item);
86 | }
87 |
88 | isAllChecked() {
89 | return (this.props.items.length === this.state.checkedItems.length);
90 | }
91 |
92 | informAboutChange(checkedItems) {
93 | setTimeout(() => this.props.onChange(checkedItems));
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/client/components/CheckboxListItem.jsx:
--------------------------------------------------------------------------------
1 | import {Component} from 'preact';
2 |
3 | import Checkbox from './Checkbox';
4 | import CheckboxList from './CheckboxList';
5 | import s from './CheckboxList.css';
6 |
7 | export default class CheckboxListItem extends Component {
8 |
9 | render() {
10 | return (
11 |
12 |
14 | {this.renderLabel()}
15 |
16 |
17 | );
18 | }
19 |
20 | renderLabel() {
21 | const {children, item} = this.props;
22 |
23 | if (children) {
24 | return children(item);
25 | }
26 |
27 | return (item === CheckboxList.ALL_ITEM) ? 'All' : item.label;
28 | }
29 |
30 | handleChange = () => {
31 | this.props.onChange(this.props.item);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/client/components/ContextMenu.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font: var(--main-font);
3 | position: absolute;
4 | padding: 0;
5 | border-radius: 4px;
6 | background: #fff;
7 | border: 1px solid #aaa;
8 | list-style: none;
9 | opacity: 1;
10 | white-space: nowrap;
11 | visibility: visible;
12 | transition: opacity .2s ease, visibility .2s ease;
13 | }
14 |
15 | .hidden {
16 | opacity: 0;
17 | visibility: hidden;
18 | }
19 |
--------------------------------------------------------------------------------
/client/components/ContextMenu.jsx:
--------------------------------------------------------------------------------
1 | import cls from 'classnames';
2 | import ContextMenuItem from './ContextMenuItem';
3 | import PureComponent from '../lib/PureComponent';
4 | import { store } from '../store';
5 | import { elementIsOutside } from '../utils';
6 |
7 | import s from './ContextMenu.css';
8 |
9 | export default class ContextMenu extends PureComponent {
10 | componentDidMount() {
11 | this.boundingRect = this.node.getBoundingClientRect();
12 | }
13 |
14 | componentDidUpdate(prevProps) {
15 | if (this.props.visible && !prevProps.visible) {
16 | document.addEventListener('mousedown', this.handleDocumentMousedown, true);
17 | } else if (prevProps.visible && !this.props.visible) {
18 | document.removeEventListener('mousedown', this.handleDocumentMousedown, true);
19 | }
20 | }
21 |
22 | render() {
23 | const { visible } = this.props;
24 | const containerClassName = cls({
25 | [s.container]: true,
26 | [s.hidden]: !visible
27 | });
28 | const multipleChunksSelected = store.selectedChunks.length > 1;
29 | return (
30 |
31 |
32 | Hide chunk
33 |
34 |
35 | Hide all other chunks
36 |
37 |
38 |
39 | Show all chunks
40 |
41 |
42 | );
43 | }
44 |
45 | handleClickHideChunk = () => {
46 | const { chunk: selectedChunk } = this.props;
47 | if (selectedChunk && selectedChunk.label) {
48 | const filteredChunks = store.selectedChunks.filter(
49 | (chunk) => chunk.label !== selectedChunk.label
50 | );
51 | store.selectedChunks = filteredChunks;
52 | }
53 | this.hide();
54 | };
55 |
56 | handleClickFilterToChunk = () => {
57 | const { chunk: selectedChunk } = this.props;
58 | if (selectedChunk && selectedChunk.label) {
59 | const filteredChunks = store.allChunks.filter((chunk) => chunk.label === selectedChunk.label);
60 | store.selectedChunks = filteredChunks;
61 | }
62 | this.hide();
63 | };
64 |
65 | handleClickShowAllChunks = () => {
66 | store.selectedChunks = store.allChunks;
67 | this.hide();
68 | };
69 |
70 | /**
71 | * Handle document-wide `mousedown` events to detect clicks
72 | * outside the context menu.
73 | * @param {MouseEvent} e - DOM mouse event object
74 | * @returns {void}
75 | */
76 | handleDocumentMousedown = (e) => {
77 | const isSecondaryClick = e.ctrlKey || e.button === 2;
78 | if (!isSecondaryClick && elementIsOutside(e.target, this.node)) {
79 | e.preventDefault();
80 | e.stopPropagation();
81 | this.hide();
82 | }
83 | };
84 |
85 | hide() {
86 | if (this.props.onHide) {
87 | this.props.onHide();
88 | }
89 | }
90 |
91 | saveNode = (node) => (this.node = node);
92 |
93 | getStyle() {
94 | const { boundingRect } = this;
95 |
96 | // Upon the first render of this component, we don't yet know
97 | // its dimensions, so can't position it yet
98 | if (!boundingRect) return;
99 |
100 | const { coords } = this.props;
101 |
102 | const pos = {
103 | left: coords.x,
104 | top: coords.y
105 | };
106 |
107 | if (pos.left + boundingRect.width > window.innerWidth) {
108 | // Shifting horizontally
109 | pos.left = window.innerWidth - boundingRect.width;
110 | }
111 |
112 | if (pos.top + boundingRect.height > window.innerHeight) {
113 | // Flipping vertically
114 | pos.top = coords.y - boundingRect.height;
115 | }
116 | return pos;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/client/components/ContextMenuItem.css:
--------------------------------------------------------------------------------
1 | .item {
2 | cursor: pointer;
3 | margin: 0;
4 | padding: 8px 14px;
5 | user-select: none;
6 | }
7 |
8 | .item:hover {
9 | background: #ffefd7;
10 | }
11 |
12 | .disabled {
13 | cursor: default;
14 | color: gray;
15 | }
16 |
17 | .item.disabled:hover {
18 | background: transparent;
19 | }
20 |
--------------------------------------------------------------------------------
/client/components/ContextMenuItem.jsx:
--------------------------------------------------------------------------------
1 | import cls from 'classnames';
2 | import s from './ContextMenuItem.css';
3 |
4 | function noop() {
5 | return false;
6 | }
7 |
8 | export default function ContextMenuItem({children, disabled, onClick}) {
9 | const className = cls({
10 | [s.item]: true,
11 | [s.disabled]: disabled
12 | });
13 | const handler = disabled ? noop : onClick;
14 | return ({children} );
15 | }
16 |
--------------------------------------------------------------------------------
/client/components/Icon.css:
--------------------------------------------------------------------------------
1 | .icon {
2 | background: no-repeat center/contain;
3 | display: inline-block;
4 | }
5 |
--------------------------------------------------------------------------------
/client/components/Icon.jsx:
--------------------------------------------------------------------------------
1 | import cls from 'classnames';
2 | import s from './Icon.css';
3 | import PureComponent from '../lib/PureComponent';
4 |
5 | import iconArrowRight from '../assets/icon-arrow-right.svg';
6 | import iconPin from '../assets/icon-pin.svg';
7 |
8 | const ICONS = {
9 | 'arrow-right': {
10 | src: iconArrowRight,
11 | size: [7, 13]
12 | },
13 | 'pin': {
14 | src: iconPin,
15 | size: [12, 18]
16 | }
17 | };
18 |
19 | export default class Icon extends PureComponent {
20 | render({className}) {
21 | return (
22 |
24 | );
25 | }
26 |
27 | get style() {
28 | const {name, size, rotate} = this.props;
29 | const icon = ICONS[name];
30 |
31 | if (!icon) throw new TypeError(`Can't find "${name}" icon.`);
32 |
33 | let [width, height] = icon.size;
34 |
35 | if (size) {
36 | const ratio = size / Math.max(width, height);
37 | width = Math.min(Math.ceil(width * ratio), size);
38 | height = Math.min(Math.ceil(height * ratio), size);
39 | }
40 |
41 | return {
42 | backgroundImage: `url(${icon.src})`,
43 | width: `${width}px`,
44 | height: `${height}px`,
45 | transform: rotate ? `rotate(${rotate}deg)` : ''
46 | };
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/client/components/ModuleItem.css:
--------------------------------------------------------------------------------
1 | .container {
2 | background: no-repeat left center;
3 | cursor: pointer;
4 | margin-bottom: 4px;
5 | padding-left: 18px;
6 | position: relative;
7 | white-space: nowrap;
8 | }
9 |
10 | .container.module {
11 | background-image: url('../assets/icon-module.svg');
12 | background-position-x: 1px;
13 | }
14 |
15 | .container.folder {
16 | background-image: url('../assets/icon-folder.svg');
17 | }
18 |
19 | .container.chunk {
20 | background-image: url('../assets/icon-chunk.svg');
21 | }
22 |
23 | .container.invisible:hover::before {
24 | background: url('../assets/icon-invisible.svg') no-repeat left center;
25 | content: "";
26 | height: 100%;
27 | left: 0;
28 | top: 1px;
29 | position: absolute;
30 | width: 13px;
31 | }
32 |
--------------------------------------------------------------------------------
/client/components/ModuleItem.jsx:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import filesize from 'filesize';
3 | import cls from 'classnames';
4 |
5 | import PureComponent from '../lib/PureComponent';
6 | import s from './ModuleItem.css';
7 |
8 | export default class ModuleItem extends PureComponent {
9 | state = {
10 | visible: true
11 | };
12 |
13 | render({ module, showSize }) {
14 | const invisible = !this.state.visible;
15 | const classes = cls(s.container, s[this.itemType], {
16 | [s.invisible]: invisible
17 | });
18 |
19 | return (
20 |
27 |
28 | {showSize && [' (', {filesize(module[showSize])} , ')']}
29 |
30 | );
31 | }
32 |
33 | get itemType() {
34 | const { module } = this.props;
35 | if (!module.path) return 'chunk';
36 | return module.groups ? 'folder' : 'module';
37 | }
38 |
39 | get titleHtml() {
40 | let html;
41 | const { module } = this.props;
42 | const title = module.path || module.label;
43 | const term = this.props.highlightedText;
44 |
45 | if (term) {
46 | const regexp =
47 | term instanceof RegExp
48 | ? new RegExp(term.source, 'igu')
49 | : new RegExp(`(?:${_.escapeRegExp(term)})+`, 'iu');
50 | let match;
51 | let lastMatch;
52 |
53 | do {
54 | lastMatch = match;
55 | match = regexp.exec(title);
56 | } while (match);
57 |
58 | if (lastMatch) {
59 | html =
60 | _.escape(title.slice(0, lastMatch.index)) +
61 | `${_.escape(lastMatch[0])} ` +
62 | _.escape(title.slice(lastMatch.index + lastMatch[0].length));
63 | }
64 | }
65 |
66 | if (!html) {
67 | html = _.escape(title);
68 | }
69 |
70 | return html;
71 | }
72 |
73 | get invisibleHint() {
74 | return `${_.upperFirst(this.itemType)} is not rendered in the treemap because it's too small.`;
75 | }
76 |
77 | get isVisible() {
78 | const { isVisible } = this.props;
79 | return isVisible ? isVisible(this.props.module) : true;
80 | }
81 |
82 | handleClick = () => this.props.onClick(this.props.module);
83 |
84 | handleMouseEnter = () => {
85 | if (this.props.isVisible) {
86 | this.setState({ visible: this.isVisible });
87 | }
88 | };
89 | }
90 |
--------------------------------------------------------------------------------
/client/components/ModulesList.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font: var(--main-font);
3 | }
4 |
--------------------------------------------------------------------------------
/client/components/ModulesList.jsx:
--------------------------------------------------------------------------------
1 | import cls from 'classnames';
2 | import s from './ModulesList.css';
3 | import ModuleItem from './ModuleItem';
4 | import PureComponent from '../lib/PureComponent';
5 |
6 | export default class ModulesList extends PureComponent {
7 | render({ modules, showSize, highlightedText, isModuleVisible, className }) {
8 | return (
9 |
10 | {modules.map((module) => (
11 |
19 | ))}
20 |
21 | );
22 | }
23 |
24 | handleModuleClick = (module) => this.props.onModuleClick(module);
25 | }
26 |
--------------------------------------------------------------------------------
/client/components/ModulesTreemap.css:
--------------------------------------------------------------------------------
1 | .container {
2 | align-items: stretch;
3 | display: flex;
4 | height: 100%;
5 | position: relative;
6 | width: 100%;
7 | }
8 |
9 | .map {
10 | flex: 1;
11 | }
12 |
13 | .sidebarGroup {
14 | font: var(--main-font);
15 | margin-bottom: 20px;
16 | }
17 |
18 | .showOption {
19 | margin-top: 5px;
20 | }
21 |
22 | .activeSize {
23 | font-weight: bold;
24 | }
25 |
26 | .foundModulesInfo {
27 | display: flex;
28 | font: var(--main-font);
29 | margin: 8px 0 0;
30 | }
31 |
32 | .foundModulesInfoItem + .foundModulesInfoItem {
33 | margin-left: 15px;
34 | }
35 |
36 | .foundModulesContainer {
37 | margin-top: 15px;
38 | max-height: 600px;
39 | overflow: auto;
40 | }
41 |
42 | .foundModulesChunk + .foundModulesChunk {
43 | margin-top: 15px;
44 | }
45 |
46 | .foundModulesChunkName {
47 | cursor: pointer;
48 | font: var(--main-font);
49 | font-weight: bold;
50 | margin-bottom: 7px;
51 | }
52 |
53 | .foundModulesList {
54 | margin-left: 7px;
55 | }
56 |
--------------------------------------------------------------------------------
/client/components/ModulesTreemap.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'preact';
2 | import filesize from 'filesize';
3 | import { computed } from 'mobx';
4 | import { observer } from 'mobx-react';
5 |
6 | import { isChunkParsed } from '../utils';
7 | import localStorage from '../localStorage';
8 | import Treemap from './Treemap';
9 | import Tooltip from './Tooltip';
10 | import Switcher from './Switcher';
11 | import Sidebar from './Sidebar';
12 | import Checkbox from './Checkbox';
13 | import CheckboxList from './CheckboxList';
14 | import ContextMenu from './ContextMenu';
15 |
16 | import s from './ModulesTreemap.css';
17 | import Search from './Search';
18 | import { store } from '../store';
19 | import ModulesList from './ModulesList';
20 |
21 | const SIZE_SWITCH_ITEMS = [
22 | { label: 'entry', prop: 'statSize' },
23 | { label: 'render', prop: 'parsedSize' },
24 | { label: 'gzipped', prop: 'gzipSize' }
25 | ];
26 |
27 | const viewType = [
28 | { label: 'chunk', prop: 'default' },
29 | { label: 'library', prop: 'lib' }
30 | ];
31 |
32 | @observer
33 | export default class ModulesTreemap extends Component {
34 | mouseCoords = {
35 | x: 0,
36 | y: 0
37 | };
38 |
39 | state = {
40 | selectedChunk: null,
41 | selectedMouseCoords: { x: 0, y: 0 },
42 | sidebarPinned: false,
43 | showChunkContextMenu: false,
44 | showTooltip: false,
45 | tooltipContent: null
46 | };
47 |
48 | componentDidMount() {
49 | document.addEventListener('mousemove', this.handleMouseMove, true);
50 | }
51 |
52 | componentWillUnmount() {
53 | document.removeEventListener('mousemove', this.handleMouseMove, true);
54 | }
55 |
56 | render() {
57 | const {
58 | selectedChunk,
59 | selectedMouseCoords,
60 | sidebarPinned,
61 | showChunkContextMenu,
62 | showTooltip,
63 | tooltipContent
64 | } = this.state;
65 |
66 | return (
67 |
68 |
74 |
75 |
81 |
82 |
83 |
89 | {store.hasConcatenatedModules && (
90 |
91 |
92 | {`Show content of concatenated modules${
93 | store.activeSize === 'statSize' ? '' : ' (inaccurate)'
94 | }`}
95 |
96 |
97 | )}
98 |
99 |
100 |
101 |
107 |
{this.foundModulesInfo}
108 | {store.isSearching && store.hasFoundModules && (
109 |
110 | {store.foundModulesByChunk.map(({ chunk, modules }) => (
111 |
112 |
this.treemap.zoomToGroup(chunk)}
115 | >
116 | {chunk.label}
117 |
118 |
126 |
127 | ))}
128 |
129 | )}
130 |
131 | {this.chunkItems.length > 1 && (
132 |
133 |
140 |
141 | )}
142 |
143 |
154 |
{tooltipContent}
155 |
161 |
162 | );
163 | }
164 |
165 | renderModuleSize(module, sizeType) {
166 | const sizeProp = `${sizeType}Size`;
167 | const size = module[sizeProp];
168 | const sizeLabel = SIZE_SWITCH_ITEMS.find((item) => item.prop === sizeProp).label;
169 | const isActive = store.activeSize === sizeProp;
170 |
171 | return typeof size === 'number' ? (
172 |
173 | {sizeLabel} size: {filesize(size)}
174 |
175 | ) : null;
176 | }
177 |
178 | renderChunkItemLabel = (item) => {
179 | const isAllItem = item === CheckboxList.ALL_ITEM;
180 | const label = isAllItem ? 'All' : item.label;
181 | const size = isAllItem ? store.totalChunksSize : item[store.activeSize];
182 | if (typeof size !== 'number') {
183 | console.error('size计算错误');
184 | }
185 | return [`${label} (`, {filesize(size)} , ')'];
186 | };
187 |
188 | @computed get sizeSwitchItems() {
189 | return store.hasParsedSizes ? SIZE_SWITCH_ITEMS : SIZE_SWITCH_ITEMS.slice(0, 1);
190 | }
191 |
192 | @computed get activeSizeItem() {
193 | return this.sizeSwitchItems.find((item) => item.prop === store.activeSize);
194 | }
195 |
196 | @computed get activeViewType() {
197 | return viewType.find((item) => item.prop === store.viewType);
198 | }
199 |
200 | @computed get chunkItems() {
201 | const { allChunks, activeSize } = store;
202 | let chunkItems = [...allChunks];
203 |
204 | if (activeSize !== 'statSize') {
205 | chunkItems = chunkItems.filter(isChunkParsed);
206 | }
207 |
208 | chunkItems.sort((chunk1, chunk2) => chunk2[activeSize] - chunk1[activeSize]);
209 |
210 | return chunkItems;
211 | }
212 |
213 | @computed get highlightedModules() {
214 | return new Set(store.foundModules);
215 | }
216 |
217 | @computed get foundModulesInfo() {
218 | if (!store.isSearching) {
219 | // ` ` to reserve space
220 | return '\u00A0';
221 | }
222 |
223 | if (store.hasFoundModules) {
224 | return [
225 |
226 | Count: {store.foundModules.length}
227 |
,
228 |
229 | Total size: {filesize(store.foundModulesSize)}
230 |
231 | ];
232 | } else {
233 | return 'Nothing found' + (store.allChunksSelected ? '' : ' in selected chunks');
234 | }
235 | }
236 |
237 | handleConcatenatedModulesContentToggle = (flag) => {
238 | store.showConcatenatedModulesContent = flag;
239 | if (flag) {
240 | localStorage.setItem('showConcatenatedModulesContent', true);
241 | } else {
242 | localStorage.removeItem('showConcatenatedModulesContent');
243 | }
244 | };
245 |
246 | handleChunkContextMenuHide = () => {
247 | this.setState({
248 | showChunkContextMenu: false
249 | });
250 | };
251 |
252 | handleResize = () => {
253 | // Close any open context menu when the report is resized,
254 | // so it doesn't show in an incorrect position
255 | if (this.state.showChunkContextMenu) {
256 | this.setState({
257 | showChunkContextMenu: false
258 | });
259 | }
260 | };
261 |
262 | handleSidebarToggle = () => {
263 | if (this.state.sidebarPinned) {
264 | setTimeout(() => this.treemap.resize());
265 | }
266 | };
267 |
268 | handleSidebarPinStateChange = (pinned) => {
269 | this.setState({ sidebarPinned: pinned });
270 | setTimeout(() => this.treemap.resize());
271 | };
272 |
273 | handleSidebarResize = () => {
274 | this.treemap.resize();
275 | };
276 |
277 | handleViewTypeSwitch(type) {
278 | store.viewType = type.prop;
279 | }
280 |
281 | handleSizeSwitch = (sizeSwitchItem) => {
282 | store.selectedSize = sizeSwitchItem.prop;
283 | };
284 |
285 | handleQueryChange = (query) => {
286 | store.searchQuery = query;
287 | };
288 |
289 | handleSelectedChunksChange = (selectedChunks) => {
290 | store.selectedChunks = selectedChunks;
291 | };
292 |
293 | handleMouseLeaveTreemap = () => {
294 | this.setState({ showTooltip: false });
295 | };
296 |
297 | handleTreemapGroupSecondaryClick = (event) => {
298 | const { group } = event;
299 |
300 | if (group && group.isAsset) {
301 | this.setState({
302 | selectedChunk: group,
303 | selectedMouseCoords: { ...this.mouseCoords },
304 | showChunkContextMenu: true
305 | });
306 | } else {
307 | this.setState({
308 | selectedChunk: null,
309 | showChunkContextMenu: false
310 | });
311 | }
312 | };
313 |
314 | handleTreemapGroupHover = (event) => {
315 | const { group } = event;
316 |
317 | if (group) {
318 | this.setState({
319 | showTooltip: true,
320 | tooltipContent: this.getTooltipContent(group)
321 | });
322 | } else {
323 | this.setState({ showTooltip: false });
324 | }
325 | };
326 |
327 | handleFoundModuleClick = (module) => this.treemap.zoomToGroup(module);
328 |
329 | handleMouseMove = (event) => {
330 | Object.assign(this.mouseCoords, {
331 | x: event.pageX,
332 | y: event.pageY
333 | });
334 | };
335 |
336 | isModuleVisible = (module) => this.treemap.isGroupRendered(module);
337 |
338 | saveTreemapRef = (treemap) => (this.treemap = treemap);
339 |
340 | getTooltipContent(module) {
341 | if (!module) return null;
342 |
343 | return (
344 |
345 |
346 | {module.label}
347 |
348 |
349 | {this.renderModuleSize(module, 'stat')}
350 | {!module.inaccurateSizes && this.renderModuleSize(module, 'parsed')}
351 | {!module.inaccurateSizes && this.renderModuleSize(module, 'gzip')}
352 | {module.path && (
353 |
354 | Path: {module.path}
355 |
356 | )}
357 | {module.isAsset && (
358 |
359 |
360 |
361 | Right-click to view options related to this chunk
362 |
363 |
364 | )}
365 |
366 | );
367 | }
368 | }
369 |
--------------------------------------------------------------------------------
/client/components/Search.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font: var(--main-font);
3 | white-space: nowrap;
4 | }
5 |
6 | .label {
7 | font-weight: bold;
8 | margin-bottom: 7px;
9 | }
10 |
11 | .row {
12 | display: flex;
13 | }
14 |
15 | .input {
16 | border: 1px solid #aaa;
17 | border-radius: 4px;
18 | display: block;
19 | flex: 1;
20 | padding: 5px;
21 | }
22 |
23 | .clear {
24 | flex: 0 0 auto;
25 | line-height: 1;
26 | margin-left: 3px;
27 | padding: 5px 8px 7px;
28 | }
29 |
--------------------------------------------------------------------------------
/client/components/Search.jsx:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | import s from './Search.css';
4 | import Button from './Button';
5 | import PureComponent from '../lib/PureComponent';
6 |
7 | export default class Search extends PureComponent {
8 |
9 | componentDidMount() {
10 | if (this.props.autofocus) {
11 | this.focus();
12 | }
13 | }
14 |
15 | componentWillUnmount() {
16 | this.handleValueChange.cancel();
17 | }
18 |
19 | render() {
20 | const {label, query} = this.props;
21 |
22 | return (
23 |
24 |
25 | {label}:
26 |
27 |
28 |
36 | x
37 |
38 |
39 | );
40 | }
41 |
42 | handleValueChange = _.debounce((event) => {
43 | this.informChange(event.target.value);
44 | }, 400)
45 |
46 | handleInputBlur = () => {
47 | this.handleValueChange.flush();
48 | }
49 |
50 | handleClearClick = () => {
51 | this.clear();
52 | this.focus();
53 | }
54 |
55 | handleKeyDown = event => {
56 | let handled = true;
57 |
58 | switch (event.key) {
59 | case 'Escape':
60 | this.clear();
61 | break;
62 | case 'Enter':
63 | this.handleValueChange.flush();
64 | break;
65 | default:
66 | handled = false;
67 | }
68 |
69 | if (handled) {
70 | event.stopPropagation();
71 | }
72 | }
73 |
74 | focus() {
75 | if (this.input) {
76 | this.input.focus();
77 | }
78 | }
79 |
80 | clear() {
81 | this.handleValueChange.cancel();
82 | this.informChange('');
83 | this.input.value = '';
84 | }
85 |
86 | informChange(value) {
87 | this.props.onQueryChange(value);
88 | }
89 |
90 | saveInputNode = node => this.input = node;
91 | }
92 |
--------------------------------------------------------------------------------
/client/components/Sidebar.css:
--------------------------------------------------------------------------------
1 | @value toggleTime: 200ms;
2 |
3 | .container {
4 | background: #fff;
5 | border: none;
6 | border-right: 1px solid #aaa;
7 | box-sizing: border-box;
8 | max-width: calc(50% - 10px);
9 | opacity: 0.95;
10 | z-index: 1;
11 | }
12 |
13 | .container:not(.hidden) {
14 | min-width: 200px;
15 | }
16 |
17 | .container:not(.pinned) {
18 | bottom: 0;
19 | position: absolute;
20 | top: 0;
21 | transition: transform toggleTime ease;
22 | }
23 |
24 | .container.pinned {
25 | position: relative;
26 | }
27 |
28 | .container.left {
29 | left: 0;
30 | }
31 |
32 | .container.left.hidden {
33 | transform: translateX(calc(-100% + 7px));
34 | }
35 |
36 | .content {
37 | box-sizing: border-box;
38 | height: 100%;
39 | overflow-y: auto;
40 | padding: 25px 20px 20px;
41 | width: 100%;
42 | }
43 |
44 | .empty.pinned .content {
45 | padding: 0;
46 | }
47 |
48 | .pinButton,
49 | .toggleButton {
50 | cursor: pointer;
51 | height: 26px;
52 | line-height: 0;
53 | position: absolute;
54 | top: 10px;
55 | width: 27px;
56 | }
57 |
58 | .pinButton {
59 | right: 47px;
60 | }
61 |
62 | .toggleButton {
63 | padding-left: 6px;
64 | right: 15px;
65 | }
66 |
67 | .hidden .toggleButton {
68 | right: -35px;
69 | transition: transform .2s ease;
70 | }
71 |
72 | .hidden .toggleButton:hover {
73 | transform: translateX(4px);
74 | }
75 |
76 | .resizer {
77 | bottom: 0;
78 | cursor: col-resize;
79 | position: absolute;
80 | right: 0;
81 | top: 0;
82 | width: 7px;
83 | }
84 |
--------------------------------------------------------------------------------
/client/components/Sidebar.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'preact';
2 | import cls from 'classnames';
3 |
4 | import s from './Sidebar.css';
5 | import Button from './Button';
6 | import Icon from './Icon';
7 |
8 | const toggleTime = parseInt(s.toggleTime);
9 |
10 | export default class Sidebar extends Component {
11 | static defaultProps = {
12 | pinned: false,
13 | position: 'left'
14 | };
15 |
16 | allowHide = true;
17 | toggling = false;
18 | hideContentTimeout = null;
19 | width = null;
20 | state = {
21 | visible: true,
22 | renderContent: true
23 | };
24 |
25 | componentDidMount() {
26 | this.hideTimeoutId = setTimeout(() => this.toggleVisibility(false), 3000);
27 | }
28 |
29 | componentWillUnmount() {
30 | clearTimeout(this.hideTimeoutId);
31 | clearTimeout(this.hideContentTimeout);
32 | }
33 |
34 | render() {
35 | const { position, pinned, children } = this.props;
36 | const { visible, renderContent } = this.state;
37 |
38 | const className = cls({
39 | [s.container]: true,
40 | [s.pinned]: pinned,
41 | [s.left]: position === 'left',
42 | [s.hidden]: !visible,
43 | [s.empty]: !renderContent
44 | });
45 |
46 | return (
47 |
53 | {/* {visible && (*/}
54 | {/*
*/}
62 | {/* */}
63 | {/* */}
64 | {/* )}*/}
65 |
71 |
72 |
73 | {pinned && visible &&
}
74 |
79 | {renderContent ? children : null}
80 |
81 |
82 | );
83 | }
84 |
85 | handleClick = () => {
86 | this.allowHide = false;
87 | };
88 |
89 | handleMouseEnter = () => {
90 | if (!this.toggling && !this.props.pinned) {
91 | clearTimeout(this.hideTimeoutId);
92 | this.toggleVisibility(true);
93 | }
94 | };
95 |
96 | handleMouseMove = () => {
97 | this.allowHide = true;
98 | };
99 |
100 | handleMouseLeave = () => {
101 | if (this.allowHide && !this.toggling && !this.props.pinned) {
102 | this.toggleVisibility(false);
103 | }
104 | };
105 |
106 | handleToggleButtonClick = () => {
107 | this.toggleVisibility();
108 | };
109 |
110 | handlePinButtonClick = () => {
111 | const pinned = !this.props.pinned;
112 | this.width = pinned ? this.node.getBoundingClientRect().width : null;
113 | this.updateNodeWidth();
114 | this.props.onPinStateChange(pinned);
115 | };
116 |
117 | handleResizeStart = (event) => {
118 | this.resizeInfo = {
119 | startPageX: event.pageX,
120 | initialWidth: this.width
121 | };
122 | document.body.classList.add('resizing', 'col');
123 | document.addEventListener('mousemove', this.handleResize, true);
124 | document.addEventListener('mouseup', this.handleResizeEnd, true);
125 | };
126 |
127 | handleResize = (event) => {
128 | this.width = this.resizeInfo.initialWidth + (event.pageX - this.resizeInfo.startPageX);
129 | this.updateNodeWidth();
130 | };
131 |
132 | handleResizeEnd = () => {
133 | document.body.classList.remove('resizing', 'col');
134 | document.removeEventListener('mousemove', this.handleResize, true);
135 | document.removeEventListener('mouseup', this.handleResizeEnd, true);
136 | this.props.onResize();
137 | };
138 |
139 | toggleVisibility(flag) {
140 | clearTimeout(this.hideContentTimeout);
141 |
142 | const { visible } = this.state;
143 | const { onToggle, pinned } = this.props;
144 |
145 | if (flag === undefined) {
146 | flag = !visible;
147 | } else if (flag === visible) {
148 | return;
149 | }
150 |
151 | this.setState({ visible: flag });
152 | this.toggling = true;
153 | setTimeout(() => {
154 | this.toggling = false;
155 | }, toggleTime);
156 |
157 | if (pinned) {
158 | this.updateNodeWidth(flag ? this.width : null);
159 | }
160 |
161 | if (flag || pinned) {
162 | this.setState({ renderContent: flag });
163 | onToggle(flag);
164 | } else if (!flag) {
165 | // Waiting for the CSS animation to finish and hiding content
166 | this.hideContentTimeout = setTimeout(() => {
167 | this.hideContentTimeout = null;
168 | this.setState({ renderContent: false });
169 | onToggle(false);
170 | }, toggleTime);
171 | }
172 | }
173 |
174 | saveNode = (node) => (this.node = node);
175 |
176 | updateNodeWidth(width = this.width) {
177 | this.node.style.width = width ? `${width}px` : '';
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/client/components/Switcher.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font: var(--main-font);
3 | white-space: nowrap;
4 | }
5 |
6 | .label {
7 | font-weight: bold;
8 | font-size: 11px;
9 | margin-bottom: 7px;
10 | }
11 |
12 | .item + .item {
13 | margin-left: 5px;
14 | }
15 |
--------------------------------------------------------------------------------
/client/components/Switcher.jsx:
--------------------------------------------------------------------------------
1 | import SwitcherItem from './SwitcherItem';
2 | import s from './Switcher.css';
3 | import PureComponent from '../lib/PureComponent';
4 |
5 | export default class Switcher extends PureComponent {
6 |
7 | render() {
8 | const {label, items, activeItem, onSwitch} = this.props;
9 |
10 | return (
11 |
12 |
13 | {label}:
14 |
15 |
16 | {items.map(item =>
17 |
22 | )}
23 |
24 |
25 | );
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/client/components/SwitcherItem.jsx:
--------------------------------------------------------------------------------
1 | import Button from './Button';
2 | import PureComponent from '../lib/PureComponent';
3 |
4 | export default class SwitcherItem extends PureComponent {
5 | render({item, ...props}) {
6 | return (
7 |
8 | {item.label}
9 |
10 | );
11 | }
12 |
13 | handleClick = () => {
14 | this.props.onClick(this.props.item);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/client/components/Tooltip.css:
--------------------------------------------------------------------------------
1 | .container {
2 | font: var(--main-font);
3 | position: absolute;
4 | padding: 5px 10px;
5 | border-radius: 4px;
6 | background: #fff;
7 | border: 1px solid #aaa;
8 | opacity: 0.9;
9 | white-space: nowrap;
10 | visibility: visible;
11 | transition: opacity .2s ease, visibility .2s ease;
12 | }
13 |
14 | .hidden {
15 | opacity: 0;
16 | visibility: hidden;
17 | }
18 |
--------------------------------------------------------------------------------
/client/components/Tooltip.jsx:
--------------------------------------------------------------------------------
1 | import {Component} from 'preact';
2 | import cls from 'classnames';
3 |
4 | import s from './Tooltip.css';
5 |
6 | export default class Tooltip extends Component {
7 |
8 | static marginX = 10;
9 | static marginY = 30;
10 |
11 | mouseCoords = {
12 | x: 0,
13 | y: 0
14 | };
15 |
16 | state = {
17 | left: 0,
18 | top: 0
19 | };
20 |
21 | componentDidMount() {
22 | document.addEventListener('mousemove', this.handleMouseMove, true);
23 | }
24 |
25 | shouldComponentUpdate(nextProps) {
26 | return this.props.visible || nextProps.visible;
27 | }
28 |
29 | componentWillUnmount() {
30 | document.removeEventListener('mousemove', this.handleMouseMove, true);
31 | }
32 |
33 | render() {
34 | const {children, visible} = this.props;
35 | const className = cls({
36 | [s.container]: true,
37 | [s.hidden]: !visible
38 | });
39 |
40 | return (
41 |
44 | {children}
45 |
46 | );
47 | }
48 |
49 | handleMouseMove = event => {
50 | Object.assign(this.mouseCoords, {
51 | x: event.pageX,
52 | y: event.pageY
53 | });
54 |
55 | if (this.props.visible) {
56 | this.updatePosition();
57 | }
58 | };
59 |
60 | saveNode = node => (this.node = node);
61 |
62 | getStyle() {
63 | return {
64 | left: this.state.left,
65 | top: this.state.top
66 | };
67 | }
68 |
69 | updatePosition() {
70 | if (!this.props.visible) return;
71 |
72 | const pos = {
73 | left: this.mouseCoords.x + Tooltip.marginX,
74 | top: this.mouseCoords.y + Tooltip.marginY
75 | };
76 |
77 | const boundingRect = this.node.getBoundingClientRect();
78 |
79 | if (pos.left + boundingRect.width > window.innerWidth) {
80 | // Shifting horizontally
81 | pos.left = window.innerWidth - boundingRect.width;
82 | }
83 |
84 | if (pos.top + boundingRect.height > window.innerHeight) {
85 | // Flipping vertically
86 | pos.top = this.mouseCoords.y - Tooltip.marginY - boundingRect.height;
87 | }
88 |
89 | this.setState(pos);
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/client/components/Treemap.jsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'preact';
2 | import FoamTree from '@carrotsearch/foamtree';
3 |
4 | export default class Treemap extends Component {
5 | constructor(props) {
6 | super(props);
7 | this.treemap = null;
8 | this.zoomOutDisabled = false;
9 | this.findChunkNamePartIndex();
10 | }
11 |
12 | componentDidMount() {
13 | this.treemap = this.createTreemap();
14 | window.addEventListener('resize', this.resize);
15 | }
16 |
17 | componentWillReceiveProps(nextProps) {
18 | if (nextProps.data !== this.props.data) {
19 | this.findChunkNamePartIndex();
20 | this.treemap.set({
21 | dataObject: this.getTreemapDataObject(nextProps.data)
22 | });
23 | } else if (nextProps.highlightGroups !== this.props.highlightGroups) {
24 | const groupsToRedraw = [...nextProps.highlightGroups, ...this.props.highlightGroups];
25 | setTimeout(() => this.treemap.redraw(false, groupsToRedraw));
26 | }
27 | }
28 |
29 | shouldComponentUpdate() {
30 | return false;
31 | }
32 |
33 | componentWillUnmount() {
34 | window.removeEventListener('resize', this.resize);
35 | this.treemap.dispose();
36 | }
37 |
38 | render() {
39 | return
;
40 | }
41 |
42 | saveNodeRef = (node) => (this.node = node);
43 |
44 | getTreemapDataObject(data = this.props.data) {
45 | return { groups: data };
46 | }
47 |
48 | createTreemap() {
49 | const component = this;
50 | const { props } = this;
51 |
52 | return new FoamTree({
53 | element: this.node,
54 | layout: 'squarified',
55 | stacking: 'flattened',
56 | pixelRatio: window.devicePixelRatio || 1,
57 | maxGroups: Infinity,
58 | maxGroupLevelsDrawn: Infinity,
59 | maxGroupLabelLevelsDrawn: Infinity,
60 | maxGroupLevelsAttached: Infinity,
61 | wireframeLabelDrawing: 'always',
62 | groupMinDiameter: 0,
63 | groupLabelVerticalPadding: 0.2,
64 | rolloutDuration: 0,
65 | pullbackDuration: 0,
66 | fadeDuration: 0,
67 | groupExposureZoomMargin: 0.2,
68 | zoomMouseWheelDuration: 300,
69 | openCloseDuration: 200,
70 | dataObject: this.getTreemapDataObject(),
71 | titleBarDecorator(opts, props, vars) {
72 | vars.titleBarShown = false;
73 | },
74 | groupColorDecorator(options, properties, variables) {
75 | const root = component.getGroupRoot(properties.group);
76 | const chunkName = component.getChunkNamePart(root.label);
77 | const hash = /[^0-9]/u.test(chunkName)
78 | ? hashCode(chunkName)
79 | : (parseInt(chunkName) / 1000) * 360;
80 | variables.groupColor = {
81 | model: 'hsla',
82 | h: Math.round(Math.abs(hash) % 360),
83 | s: 60,
84 | l: 50,
85 | a: 0.9
86 | };
87 |
88 | const { highlightGroups } = component.props;
89 | const module = properties.group;
90 |
91 | if (highlightGroups && highlightGroups.has(module)) {
92 | variables.groupColor = {
93 | model: 'rgba',
94 | r: 255,
95 | g: 0,
96 | b: 0,
97 | a: 0.8
98 | };
99 | }
100 | },
101 | /**
102 | * Handle Foamtree's "group clicked" event
103 | * @param {FoamtreeEvent} event - Foamtree event object
104 | * (see https://get.carrotsearch.com/foamtree/demo/api/index.html#event-details)
105 | * @returns {void}
106 | */
107 | onGroupClick(event) {
108 | preventDefault(event);
109 | if ((event.ctrlKey || event.secondary) && props.onGroupSecondaryClick) {
110 | props.onGroupSecondaryClick.call(component, event);
111 | return;
112 | }
113 | component.zoomOutDisabled = false;
114 | this.zoom(event.group);
115 | },
116 | onGroupDoubleClick: preventDefault,
117 | onGroupHover(event) {
118 | // Ignoring hovering on `FoamTree` branding group and the root group
119 | if (event.group && (event.group.attribution || event.group === this.get('dataObject'))) {
120 | event.preventDefault();
121 | if (props.onMouseLeave) {
122 | props.onMouseLeave.call(component, event);
123 | }
124 | return;
125 | }
126 |
127 | if (props.onGroupHover) {
128 | props.onGroupHover.call(component, event);
129 | }
130 | },
131 | onGroupMouseWheel(event) {
132 | const { scale } = this.get('viewport');
133 | const isZoomOut = event.delta < 0;
134 |
135 | if (isZoomOut) {
136 | if (component.zoomOutDisabled) return preventDefault(event);
137 | if (scale < 1) {
138 | component.zoomOutDisabled = true;
139 | preventDefault(event);
140 | }
141 | } else {
142 | component.zoomOutDisabled = false;
143 | }
144 | }
145 | });
146 | }
147 |
148 | getGroupRoot(group) {
149 | let nextParent;
150 | while (!group.isAsset && (nextParent = this.treemap.get('hierarchy', group).parent)) {
151 | group = nextParent;
152 | }
153 | return group;
154 | }
155 |
156 | zoomToGroup(group) {
157 | this.zoomOutDisabled = false;
158 |
159 | while (group && !this.treemap.get('state', group).revealed) {
160 | group = this.treemap.get('hierarchy', group).parent;
161 | }
162 |
163 | if (group) {
164 | this.treemap.zoom(group);
165 | }
166 | }
167 |
168 | isGroupRendered(group) {
169 | const groupState = this.treemap.get('state', group);
170 | return !!groupState && groupState.revealed;
171 | }
172 |
173 | update() {
174 | this.treemap.update();
175 | }
176 |
177 | resize = () => {
178 | const { props } = this;
179 | this.treemap.resize();
180 |
181 | if (props.onResize) {
182 | props.onResize();
183 | }
184 | };
185 |
186 | /**
187 | * Finds patterns across all chunk names to identify the unique "name" part.
188 | */
189 | findChunkNamePartIndex() {
190 | const splitChunkNames = this.props.data.map((chunk) => chunk.label.split(/[^a-z0-9]/iu));
191 | const longestSplitName = Math.max(...splitChunkNames.map((parts) => parts.length));
192 | const namePart = {
193 | index: 0,
194 | votes: 0
195 | };
196 | for (let i = longestSplitName - 1; i >= 0; i--) {
197 | const identifierVotes = {
198 | name: 0,
199 | hash: 0,
200 | ext: 0
201 | };
202 | let lastChunkPart = '';
203 | for (const splitChunkName of splitChunkNames) {
204 | const part = splitChunkName[i];
205 | if (part === undefined || part === '') {
206 | continue;
207 | }
208 | if (part === lastChunkPart) {
209 | identifierVotes.ext++;
210 | } else if (
211 | /[a-z]/u.test(part) &&
212 | /[0-9]/u.test(part) &&
213 | part.length === lastChunkPart.length
214 | ) {
215 | identifierVotes.hash++;
216 | } else if (/^[a-z]+$/iu.test(part) || /^[0-9]+$/u.test(part)) {
217 | identifierVotes.name++;
218 | }
219 | lastChunkPart = part;
220 | }
221 | if (identifierVotes.name >= namePart.votes) {
222 | namePart.index = i;
223 | namePart.votes = identifierVotes.name;
224 | }
225 | }
226 | this.chunkNamePartIndex = namePart.index;
227 | }
228 |
229 | getChunkNamePart(chunkLabel) {
230 | return chunkLabel.split(/[^a-z0-9]/iu)[this.chunkNamePartIndex] || chunkLabel;
231 | }
232 | }
233 |
234 | function preventDefault(event) {
235 | event.preventDefault();
236 | }
237 |
238 | function hashCode(str) {
239 | let hash = 0;
240 | for (let i = 0; i < str.length; i++) {
241 | const code = str.charCodeAt(i);
242 | hash = (hash << 5) - hash + code;
243 | hash = hash & hash;
244 | }
245 | return hash;
246 | }
247 |
--------------------------------------------------------------------------------
/client/lib/PureComponent.jsx:
--------------------------------------------------------------------------------
1 | import {Component} from 'preact';
2 |
3 | export default class PureComponent extends Component {
4 | shouldComponentUpdate(nextProps, nextState) {
5 | return !isEqual(nextProps, this.props) || !isEqual(this.state, nextState);
6 | }
7 | }
8 |
9 | function isEqual(obj1, obj2) {
10 | if (obj1 === obj2) return true;
11 | const keys = Object.keys(obj1);
12 | if (keys.length !== Object.keys(obj2).length) return false;
13 | for (let i = 0; i < keys.length; i++) {
14 | const key = keys[i];
15 | if (obj1[key] !== obj2[key]) return false;
16 | }
17 | return true;
18 | }
19 |
--------------------------------------------------------------------------------
/client/localStorage.js:
--------------------------------------------------------------------------------
1 | const KEY_PREFIX = 'wba';
2 |
3 | export default {
4 |
5 | getItem(key) {
6 | try {
7 | return JSON.parse(window.localStorage.getItem(`${KEY_PREFIX}.${key}`));
8 | } catch (err) {
9 | return null;
10 | }
11 | },
12 |
13 | setItem(key, value) {
14 | try {
15 | window.localStorage.setItem(`${KEY_PREFIX}.${key}`, JSON.stringify(value));
16 | } catch (err) { /* ignored */ }
17 | },
18 |
19 | removeItem(key) {
20 | try {
21 | window.localStorage.removeItem(`${KEY_PREFIX}.${key}`);
22 | } catch (err) { /* ignored */ }
23 | }
24 |
25 | };
26 |
--------------------------------------------------------------------------------
/client/store.js:
--------------------------------------------------------------------------------
1 | import { observable, computed } from 'mobx';
2 | import { isChunkParsed, walkModules } from './utils';
3 | import localStorage from './localStorage';
4 |
5 | export class Store {
6 | cid = 0;
7 | sizes = new Set(['statSize', 'parsedSize', 'gzipSize']);
8 |
9 | // @observable.ref allChunks;
10 | @observable.ref rawChunks;
11 | @observable.shallow selectedChunks;
12 | @observable searchQuery = '';
13 | @observable defaultSize;
14 | @observable selectedSize;
15 | @observable showConcatenatedModulesContent =
16 | localStorage.getItem('showConcatenatedModulesContent') === true;
17 | @observable viewType = 'default';
18 |
19 | setModules(chunks) {
20 | for (const [, chunk] of Object.entries(chunks)) {
21 | walkModules(chunk, (module) => {
22 | // eslint-disable-next-line no-plusplus
23 | module.cid = this.cid++;
24 | });
25 | }
26 |
27 | this.rawChunks = chunks;
28 | this.selectedChunks = this.rawChunks[this.viewType];
29 | }
30 |
31 | @computed get allChunks() {
32 | console.log('target', this.rawChunks[this.viewType]);
33 | return this.rawChunks[this.viewType];
34 | }
35 |
36 | @computed get hasParsedSizes() {
37 | return this.allChunks.some(isChunkParsed);
38 | }
39 |
40 | @computed get activeSize() {
41 | const activeSize = this.selectedSize || this.defaultSize;
42 |
43 | if (!this.hasParsedSizes || !this.sizes.has(activeSize)) {
44 | return 'statSize';
45 | }
46 |
47 | return activeSize;
48 | }
49 |
50 | @computed get visibleChunks() {
51 | const visibleChunks = this.allChunks.filter((chunk) => this.selectedChunks.includes(chunk));
52 |
53 | return this.filterModulesForSize(visibleChunks, this.activeSize);
54 | }
55 |
56 | @computed get allChunksSelected() {
57 | return this.visibleChunks.length === this.allChunks.length;
58 | }
59 |
60 | @computed get totalChunksSize() {
61 | return this.allChunks.reduce(
62 | (totalSize, chunk) => totalSize + (chunk[this.activeSize] || 0),
63 | 0
64 | );
65 | }
66 |
67 | @computed get searchQueryRegexp() {
68 | const query = this.searchQuery.trim();
69 |
70 | if (!query) {
71 | return null;
72 | }
73 |
74 | try {
75 | return new RegExp(query, 'iu');
76 | } catch (err) {
77 | return null;
78 | }
79 | }
80 |
81 | @computed get isSearching() {
82 | return !!this.searchQueryRegexp;
83 | }
84 |
85 | @computed get foundModulesByChunk() {
86 | if (!this.isSearching) {
87 | return [];
88 | }
89 |
90 | const query = this.searchQueryRegexp;
91 |
92 | return this.visibleChunks
93 | .map((chunk) => {
94 | let foundGroups = [];
95 |
96 | walkModules(chunk.groups, (module) => {
97 | let weight = 0;
98 |
99 | /**
100 | * Splitting found modules/directories into groups:
101 | *
102 | * 1) Module with matched label (weight = 4)
103 | * 2) Directory with matched label (weight = 3)
104 | * 3) Module with matched path (weight = 2)
105 | * 4) Directory with matched path (weight = 1)
106 | */
107 | if (query.test(module.label)) {
108 | weight += 3;
109 | } else if (module.path && query.test(module.path)) {
110 | weight++;
111 | }
112 |
113 | if (!weight) return;
114 |
115 | if (!module.groups) {
116 | weight += 1;
117 | }
118 |
119 | const foundModules = (foundGroups[weight - 1] = foundGroups[weight - 1] || []);
120 | foundModules.push(module);
121 | });
122 |
123 | const { activeSize } = this;
124 |
125 | // Filtering out missing groups
126 | foundGroups = foundGroups.filter(Boolean).reverse();
127 | // Sorting each group by active size
128 | foundGroups.forEach((modules) => modules.sort((m1, m2) => m2[activeSize] - m1[activeSize]));
129 |
130 | return {
131 | chunk,
132 | modules: [].concat(...foundGroups)
133 | };
134 | })
135 | .filter((result) => result.modules.length > 0)
136 | .sort((c1, c2) => c1.modules.length - c2.modules.length);
137 | }
138 |
139 | @computed get foundModules() {
140 | return this.foundModulesByChunk.reduce((arr, chunk) => arr.concat(chunk.modules), []);
141 | }
142 |
143 | @computed get hasFoundModules() {
144 | return this.foundModules.length > 0;
145 | }
146 |
147 | @computed get hasConcatenatedModules() {
148 | let result = false;
149 |
150 | walkModules(this.visibleChunks, (module) => {
151 | if (module.concatenated) {
152 | result = true;
153 | return false;
154 | }
155 | });
156 |
157 | return result;
158 | }
159 |
160 | @computed get foundModulesSize() {
161 | return this.foundModules.reduce((summ, module) => summ + module[this.activeSize], 0);
162 | }
163 |
164 | filterModulesForSize(modules, sizeProp) {
165 | return modules.reduce((filteredModules, module) => {
166 | if (module[sizeProp]) {
167 | if (module.groups) {
168 | const showContent = !module.concatenated || this.showConcatenatedModulesContent;
169 |
170 | module = {
171 | ...module,
172 | groups: showContent ? this.filterModulesForSize(module.groups, sizeProp) : null
173 | };
174 | }
175 |
176 | module.weight = module[sizeProp];
177 | filteredModules.push(module);
178 | }
179 |
180 | return filteredModules;
181 | }, []);
182 | }
183 | }
184 |
185 | export const store = new Store();
186 |
--------------------------------------------------------------------------------
/client/utils.js:
--------------------------------------------------------------------------------
1 | export function isChunkParsed(chunk) {
2 | return (typeof chunk.parsedSize === 'number');
3 | }
4 |
5 | export function walkModules(modules, cb) {
6 | for (const module of modules) {
7 | if (cb(module) === false) return false;
8 |
9 | if (module.groups) {
10 | if (walkModules(module.groups, cb) === false) {
11 | return false;
12 | }
13 | }
14 | }
15 | }
16 |
17 | export function elementIsOutside(elem, container) {
18 | return !(elem === container || container.contains(elem));
19 | }
20 |
--------------------------------------------------------------------------------
/client/viewer.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --main-font: normal 11px Verdana, sans-serif;
3 | }
4 |
5 | :global html,
6 | :global body,
7 | :global #app {
8 | height: 100%;
9 | margin: 0;
10 | overflow: hidden;
11 | padding: 0;
12 | width: 100%;
13 | }
14 |
15 | :global body.resizing {
16 | user-select: none !important;
17 | }
18 |
19 | :global body.resizing * {
20 | pointer-events: none;
21 | }
22 |
23 | :global body.resizing.col {
24 | cursor: col-resize !important;
25 | }
26 |
--------------------------------------------------------------------------------
/client/viewer.jsx:
--------------------------------------------------------------------------------
1 | import { render } from 'preact';
2 |
3 | import { store } from './store';
4 | import ModulesTreemap from './components/ModulesTreemap';
5 | /* eslint no-unused-vars: "off" */
6 | import styles from './viewer.css';
7 |
8 | // Initializing WebSocket for live treemap updates
9 | let ws;
10 | try {
11 | if (window.enableWebSocket) {
12 | ws = new WebSocket(`ws://${location.host}`);
13 | }
14 | } catch (err) {
15 | console.warn(
16 | "Couldn't connect to analyzer websocket server so you'll have to reload page manually to see updates in the treemap"
17 | );
18 | }
19 |
20 | window.addEventListener(
21 | 'load',
22 | () => {
23 | store.defaultSize = `${window.defaultSizes}Size`;
24 | store.setModules(window.chartData);
25 |
26 | render( , document.getElementById('app'));
27 |
28 | if (ws) {
29 | ws.addEventListener('message', (event) => {
30 | const msg = JSON.parse(event.data);
31 |
32 | if (msg.event === 'chartDataUpdated') {
33 | store.setModules(msg.data);
34 | }
35 | });
36 | }
37 | },
38 | false
39 | );
40 |
--------------------------------------------------------------------------------
/dist/cjs/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', { value: true });
4 |
5 | var pkg = require('chalk');
6 | var terser = require('terser');
7 | var _ = require('lodash');
8 | var gzipSize = require('gzip-size');
9 | var path = require('path');
10 | var http = require('http');
11 | var url = require('url');
12 | var WebSocket = require('ws');
13 | var sirv = require('sirv');
14 | require('util');
15 | var opener = require('opener');
16 | var fs = require('fs');
17 |
18 | function _interopNamespaceDefault(e) {
19 | var n = Object.create(null);
20 | if (e) {
21 | Object.keys(e).forEach(function (k) {
22 | if (k !== 'default') {
23 | var d = Object.getOwnPropertyDescriptor(e, k);
24 | Object.defineProperty(n, k, d.get ? d : {
25 | enumerable: true,
26 | get: function () { return e[k]; }
27 | });
28 | }
29 | });
30 | }
31 | n.default = e;
32 | return Object.freeze(n);
33 | }
34 |
35 | var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
36 | var http__namespace = /*#__PURE__*/_interopNamespaceDefault(http);
37 | var url__namespace = /*#__PURE__*/_interopNamespaceDefault(url);
38 |
39 | const createBrotliSizeGetter = () => {
40 | return (code) => {
41 | // eslint-disable-next-line no-console
42 | const data = Buffer.from(code || '', 'utf-8');
43 | return data.length;
44 | };
45 | };
46 | const sizeGetter$1 = (code) => {
47 | const data = Buffer.from(code, 'utf-8');
48 | return data.length;
49 | };
50 |
51 | class Node {
52 | constructor(name, parent) {
53 | this.name = name;
54 | this.parent = parent;
55 | }
56 | get path() {
57 | return this.name;
58 | }
59 | get isRoot() {
60 | return !this.parent;
61 | }
62 | }
63 |
64 | const sizeGetter = createBrotliSizeGetter();
65 | class Module extends Node {
66 | constructor(name, data, parent) {
67 | super(name, parent);
68 | this.data = data;
69 | }
70 | get size() {
71 | var _a;
72 | return sizeGetter((_a = this.originCode) !== null && _a !== void 0 ? _a : '');
73 | }
74 | get code() {
75 | return this.data.code;
76 | }
77 | get originCode() {
78 | return this.data.originCode;
79 | }
80 | get renderSize() {
81 | var _a;
82 | return sizeGetter((_a = this.data.code) !== null && _a !== void 0 ? _a : '');
83 | }
84 | get gzipSize() {
85 | if (!_.has(this, '_gzipSize')) {
86 | this._gzipSize = this.code ? gzipSize.sync(this.code) : undefined;
87 | }
88 | return this._gzipSize;
89 | }
90 | toChartData() {
91 | return {
92 | id: this.data.name,
93 | label: this.name,
94 | path: this.path,
95 | statSize: this.size,
96 | parsedSize: this.renderSize,
97 | gzipSize: this.gzipSize
98 | };
99 | }
100 | }
101 |
102 | function getModulePathParts(moduleData) {
103 | const projectRoot = process.cwd();
104 | const parsedPath = moduleData.name
105 | .replace(`${projectRoot}/`, '')
106 | .split('/')
107 | // Replacing `~` with `node_modules`
108 | .map((part) => (part === '~' ? 'node_modules' : part));
109 | return parsedPath.length ? parsedPath : null;
110 | }
111 |
112 | class Folder extends Node {
113 | constructor(name, parent) {
114 | super(name, parent);
115 | this.children = Object.create(null);
116 | }
117 | get gzipSize() {
118 | if (!_.has(this, '_gzipSize')) {
119 | this._gzipSize = this.code ? gzipSize.sync(this.code) : 0;
120 | }
121 | return this._gzipSize;
122 | }
123 | get code() {
124 | if (!_.has(this, '_code')) {
125 | this._code = this.walk((node, code) => { var _a; return (_a = code + node.code) !== null && _a !== void 0 ? _a : ''; }, '', false);
126 | }
127 | return this._code;
128 | }
129 | get originCode() {
130 | if (!_.has(this, '_originCode')) {
131 | this._originCode = this.walk((node, originCode) => { var _a; return (_a = originCode + node.originCode) !== null && _a !== void 0 ? _a : ''; }, '', false);
132 | }
133 | return this._originCode;
134 | }
135 | get size() {
136 | if (!_.has(this, '_size')) {
137 | this._size = this.walk((node, size) => size + node.size, 0, false);
138 | }
139 | return this._size;
140 | }
141 | get renderSize() {
142 | if (!_.has(this, '_renderSize')) {
143 | this._renderSize = this.walk((node, renderSize) => renderSize + node.renderSize, 0, false);
144 | }
145 | return this._renderSize;
146 | }
147 | getChild(name) {
148 | return this.children[name];
149 | }
150 | addChildModule(module) {
151 | const { name } = module;
152 | const currentChild = this.children[name];
153 | if (currentChild && currentChild instanceof Folder)
154 | return;
155 | // eslint-disable-next-line no-param-reassign
156 | module.parent = this;
157 | this.children[name] = module;
158 | delete this._size;
159 | delete this._code;
160 | }
161 | addChildFolder(folder) {
162 | this.children[folder.name] = folder;
163 | delete this._size;
164 | delete this._code;
165 | return folder;
166 | }
167 | walk(walker, state = {}, deep = true) {
168 | let stopped = false;
169 | function stop(finalState) {
170 | stopped = true;
171 | return finalState;
172 | }
173 | // eslint-disable-next-line consistent-return
174 | Object.values(this.children).forEach((child) => {
175 | if (deep && child.walk) {
176 | // eslint-disable-next-line no-param-reassign
177 | state = child.walk(walker, state, stop);
178 | }
179 | else {
180 | // eslint-disable-next-line no-param-reassign
181 | state = walker(child, state, stop);
182 | }
183 | if (stopped)
184 | return false;
185 | });
186 | return state;
187 | }
188 | mergeNestedFolders() {
189 | if (!this.isRoot) {
190 | let childNames;
191 | // 合并只有一个文件的目录
192 | // eslint-disable-next-line no-cond-assign
193 | while ((childNames = Object.keys(this.children)).length === 1) {
194 | // eslint-disable-next-line prefer-destructuring
195 | const childName = childNames[0];
196 | const onlyChild = this.children[childName];
197 | if (onlyChild instanceof this.constructor) {
198 | this.name += `/${onlyChild.name}`;
199 | this.children = onlyChild.children;
200 | }
201 | else {
202 | break;
203 | }
204 | }
205 | }
206 | this.walk((child) => {
207 | // eslint-disable-next-line no-param-reassign
208 | child.parent = this;
209 | if (child.mergeNestedFolders) {
210 | child.mergeNestedFolders();
211 | }
212 | }, null, false);
213 | }
214 | addModule(moduleData) {
215 | const pathParts = getModulePathParts(moduleData);
216 | if (!pathParts) {
217 | return;
218 | }
219 | const [folders, fileName] = [pathParts.slice(0, -1), _.last(pathParts)];
220 | let currentFolder = this;
221 | folders.forEach((folderName) => {
222 | let childNode = currentFolder.getChild(folderName);
223 | if (!childNode || !(childNode instanceof Folder)) {
224 | childNode = currentFolder.addChildFolder(new Folder(folderName, this));
225 | }
226 | currentFolder = childNode;
227 | });
228 | if (fileName) {
229 | const module = new Module(fileName, moduleData, this);
230 | currentFolder.addChildModule(module);
231 | }
232 | }
233 | toChartData() {
234 | return {
235 | label: this.name,
236 | path: this.path,
237 | statSize: this.size,
238 | parsedSize: this.renderSize,
239 | groups: _.invokeMap(this.children, 'toChartData'),
240 | gzipSize: this.gzipSize
241 | };
242 | }
243 | }
244 | function createModulesTree(modules) {
245 | const root = new Folder('.');
246 | modules.forEach((module) => root.addModule(module));
247 | root.mergeNestedFolders();
248 | return root;
249 | }
250 |
251 | const codeMap = {};
252 | async function transformModule(module, id, resolver) {
253 | var _a, _b;
254 | // fix https://github.com/ritz078/rollup-plugin-filesize/issues/57
255 | // @ts-ignore
256 | delete module.isAsset;
257 | // @ts-ignore
258 | const transformedModule = {
259 | ...module,
260 | name: id,
261 | modules: [],
262 | originCode: codeMap[id],
263 | code: (_b = (await terser.minify((_a = module.code) !== null && _a !== void 0 ? _a : '', {
264 | // module: true,
265 | safari10: true
266 | // toplevel: true
267 | })).code) !== null && _b !== void 0 ? _b : ''
268 | };
269 | if (resolver) {
270 | resolver(transformedModule);
271 | }
272 | if (!('modules' in module)) {
273 | transformedModule.modules = [];
274 | return transformedModule;
275 | }
276 | transformedModule.modules = [];
277 | for (const [id, m] of Object.entries(module.modules)) {
278 | // eslint-disable-next-line no-await-in-loop
279 | transformedModule.modules.push((await transformModule(m, id, resolver)));
280 | }
281 | return transformedModule;
282 | }
283 | async function getModuleData(outputBundle) {
284 | const modules = {};
285 | for (const [id, module] of Object.entries(outputBundle)) {
286 | // eslint-disable-next-line
287 | const chunk = await transformModule(module, id);
288 | chunk.tree = createModulesTree(chunk.modules);
289 | chunk.size = sizeGetter$1(chunk.code || '');
290 | modules[id] = chunk;
291 | if (module.type === 'asset') {
292 | const { source } = module;
293 | // 二进制资源文件,比如图片
294 | if (typeof source === 'string') {
295 | chunk.size = sizeGetter$1(source);
296 | }
297 | else {
298 | chunk.size = Buffer.byteLength(source);
299 | }
300 | }
301 | }
302 | return Object.entries(modules).map(([name, asset]) => {
303 | return {
304 | label: name,
305 | isAsset: true,
306 | statSize: asset.tree.size || asset.size,
307 | parsedSize: asset.tree.renderSize,
308 | gzipSize: asset.tree.gzipSize,
309 | groups: _.invokeMap(asset.tree.children, 'toChartData')
310 | };
311 | });
312 | }
313 | async function getSplitLibData(outputBundle) {
314 | const modules = {
315 | library: {},
316 | project: {}
317 | };
318 | const libModules = [];
319 | const projectModules = [];
320 | for (const [id, module] of Object.entries(outputBundle)) {
321 | if (module.type === 'chunk') {
322 | // eslint-disable-next-line
323 | await transformModule(module, id, (m) => {
324 | if (m.name === id) {
325 | return;
326 | }
327 | if (m.name.indexOf('node_modules') > -1 || m.name.indexOf('vite') > -1) {
328 | libModules.push(m);
329 | }
330 | else {
331 | projectModules.push(m);
332 | }
333 | });
334 | }
335 | }
336 | modules.library.tree = createModulesTree(libModules);
337 | modules.project.tree = createModulesTree(projectModules);
338 | return Object.entries(modules).map(([name, asset]) => {
339 | return {
340 | label: name,
341 | isAsset: true,
342 | statSize: asset.tree.size,
343 | parsedSize: asset.tree.renderSize,
344 | gzipSize: asset.tree.gzipSize,
345 | groups: _.invokeMap(asset.tree.children, 'toChartData')
346 | };
347 | });
348 | }
349 | async function getMixCharData(outputBundle) {
350 | return {
351 | lib: await getSplitLibData(outputBundle),
352 | default: await getModuleData(outputBundle)
353 | };
354 | }
355 |
356 | const LEVELS = ['debug', 'info', 'warn', 'error', 'silent'];
357 | const LOG_PREFIX = '\n[rollup-plugin-analyzer]';
358 | const LEVEL_TO_CONSOLE_METHOD = new Map([
359 | ['debug', 'log'],
360 | ['info', 'log'],
361 | ['warn', 'log']
362 | ]);
363 | class Logger {
364 | constructor(level = Logger.defaultLevel) {
365 | this.activeLevels = new Set();
366 | this.setLogLevel(level);
367 | }
368 | static log(level, ...args) {
369 | const operation = LEVEL_TO_CONSOLE_METHOD.get(level) || level;
370 | // eslint-disable-next-line no-console
371 | console[operation](...args);
372 | }
373 | setLogLevel(level) {
374 | const levelIndex = LEVELS.indexOf(level);
375 | if (levelIndex === -1)
376 | throw new Error(`Invalid log level "${level}". Use one of these: ${LEVELS.join(', ')}`);
377 | this.activeLevels.clear();
378 | for (const [i, levelValue] of LEVELS.entries()) {
379 | if (i >= levelIndex)
380 | this.activeLevels.add(levelValue);
381 | }
382 | }
383 | }
384 | Logger.levels = LEVELS;
385 | Logger.defaultLevel = 'info';
386 | LEVELS.forEach((level) => {
387 | if (level === 'silent')
388 | return;
389 | // @ts-ignore
390 | Logger.prototype[level] = function log(...args) {
391 | if (this.activeLevels.has(level))
392 | Logger.log(level, LOG_PREFIX, ...args);
393 | };
394 | });
395 | const logger = new Logger('info');
396 |
397 | const open = function (uri) {
398 | try {
399 | opener(uri);
400 | }
401 | catch (err) {
402 | // @ts-ignore
403 | logger.debug(`Opener failed to open "${uri}":\n${err}`);
404 | }
405 | };
406 |
407 | // @ts-ignore
408 | const fileName = url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.js', document.baseURI).href)));
409 | const __dirname$2 = path.dirname(fileName);
410 | const projectRoot$1 = path.resolve(__dirname$2, '..', '..');
411 | const assetsRoot = path.join(projectRoot$1, 'public');
412 | function escapeJson(json) {
413 | return JSON.stringify(json).replace(/ `${string}${values[index] || ''}`).join('');
424 | }
425 | function getScript(filename, mode) {
426 | if (mode === 'static') {
427 | return ` `;
428 | }
429 | return ``;
430 | }
431 | function renderViewer({ title, enableWebSocket, chartData, defaultSizes, mode } = {}) {
432 | return html `
433 |
434 |
435 |
436 |
437 | ${_.escape(title)}
438 |
441 | ${getScript('viewer.js', mode)}
442 |
443 |
444 |
445 |
446 |
450 |
451 | `;
452 | }
453 |
454 | const { bold } = pkg;
455 | let serverInstance;
456 | // @ts-ignore
457 | // eslint-disable-next-line no-underscore-dangle
458 | const __filename$1 = url__namespace.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.js', document.baseURI).href)));
459 | const __dirname$1 = path__namespace.dirname(__filename$1);
460 | const projectRoot = path__namespace.resolve(__dirname$1, '..', '..');
461 | function analyzerUrl(options) {
462 | const { listenHost, boundAddress } = options;
463 | return `http://${listenHost}:${boundAddress.port}`;
464 | }
465 | async function startServer(opts, moduleData, getModuleDataFun) {
466 | const { port = 8888, host = '127.0.0.1', openBrowser = true } = opts || {};
467 | const sirvMiddleware = sirv(`${projectRoot}/public`, { dev: true });
468 | const server = http__namespace.createServer((req, res) => {
469 | if (req.method === 'GET' && req.url === '/') {
470 | const html = renderViewer({
471 | title: 'rollup-bundle-analyzer',
472 | enableWebSocket: true,
473 | chartData: moduleData,
474 | mode: 'serve',
475 | defaultSizes: 'stat'
476 | });
477 | res.writeHead(200, { 'Content-Type': 'text/html' });
478 | res.end(html);
479 | }
480 | else {
481 | sirvMiddleware(req, res);
482 | }
483 | });
484 | await new Promise((resolve) => {
485 | // @ts-ignore
486 | server.listen(port, host, () => {
487 | resolve();
488 | // eslint-disable-next-line @typescript-eslint/no-shadow
489 | const url = analyzerUrl({
490 | listenPort: port,
491 | listenHost: host,
492 | boundAddress: server.address()
493 | });
494 | // @ts-ignore
495 | logger.info(`${bold('Rollup Bundle Analyzer')} is started at ${bold(url)}\n` +
496 | `Use ${bold('Ctrl+C')} to close it`);
497 | if (openBrowser) {
498 | open(url);
499 | }
500 | });
501 | });
502 | const wss = new WebSocket.Server({ server });
503 | wss.on('connection', (ws) => {
504 | ws.on('error', (err) => {
505 | // Ignore network errors like `ECONNRESET`, `EPIPE`, etc.
506 | // @ts-ignore
507 | if (err.errno)
508 | return;
509 | // @ts-ignore
510 | logger.info(err.message);
511 | });
512 | });
513 | // eslint-disable-next-line consistent-return
514 | return {
515 | ws: wss,
516 | http: server,
517 | updateChartData
518 | };
519 | async function updateChartData() {
520 | const newChartData = await getModuleDataFun();
521 | if (!newChartData)
522 | return;
523 | wss.clients.forEach((client) => {
524 | if (client.readyState === WebSocket.OPEN) {
525 | client.send(JSON.stringify({
526 | event: 'chartDataUpdated',
527 | data: newChartData
528 | }));
529 | }
530 | });
531 | }
532 | }
533 | async function startViewServe(getModuleDataFun, pluginOpt) {
534 | const moduleData = await getModuleDataFun();
535 | if (serverInstance) {
536 | (await serverInstance).updateChartData();
537 | }
538 | else {
539 | serverInstance = await startServer({
540 | openBrowser: pluginOpt.openBrowser,
541 | host: pluginOpt.host,
542 | port: pluginOpt.port,
543 | bundleDir: ''
544 | }, moduleData, getModuleDataFun);
545 | }
546 | }
547 |
548 | const getQuery = (schema) => schema.split('?').slice(1);
549 | const formatName = (name) => { var _a; return (_a = name.split('.')) === null || _a === void 0 ? void 0 : _a[0]; };
550 | async function generateAdvise(outputBundle, context) {
551 | const advise = {
552 | treeSharking: {},
553 | replace: {},
554 | commonjs: {},
555 | polyfill: {},
556 | rewrite: {}
557 | };
558 | const replaceTpl = (moduleName, target) => {
559 | const name = formatName(moduleName);
560 | advise.replace[name] = {
561 | target
562 | };
563 | };
564 | const commonjsTpl = (moduleName) => {
565 | const name = formatName(moduleName);
566 | advise.commonjs[name] = {};
567 | };
568 | const treeSharkingTpl = (moduleName, size) => {
569 | const name = formatName(moduleName);
570 | advise.treeSharking[name] = {
571 | size
572 | };
573 | };
574 | const polyfillTpl = (moduleName, size) => {
575 | const name = formatName(moduleName);
576 | advise.polyfill[name] = {
577 | size
578 | };
579 | };
580 | const rewriteTpl = (moduleName, size, desc) => {
581 | const name = formatName(moduleName);
582 | advise.rewrite[name] = {
583 | size,
584 | desc
585 | };
586 | };
587 | for (const [id, module] of Object.entries(outputBundle)) {
588 | // eslint-disable-next-line no-await-in-loop
589 | await transformModule(module, id, (m) => {
590 | const pathParts = getModulePathParts(m);
591 | const name = _.last(pathParts);
592 | const moduleInfo = context.getModuleInfo(m.name);
593 | if (moduleInfo) {
594 | // 判断是否是项目引进的包,再判断是否可以优化
595 | const isImportByProject = moduleInfo.importers.find((i) => i.indexOf('node_modules') === -1);
596 | if (isImportByProject) {
597 | const size = sizeGetter$1(m.code || '');
598 | if (pathParts && m.name.indexOf('node_modules') > -1 && /\.js| \.ts/.test(m.name)) {
599 | const nodeModulesIndex = pathParts.findIndex((i) => i === 'node_modules');
600 | if (nodeModulesIndex === -1) {
601 | return;
602 | }
603 | let moduleName = pathParts[nodeModulesIndex + 1];
604 | // eg @element-plus/icons-vue
605 | if (moduleName.indexOf('@') > -1) {
606 | moduleName = `${moduleName}/${pathParts[nodeModulesIndex + 2]}`;
607 | }
608 | if (m.removedExports &&
609 | Array.isArray(m.removedExports) &&
610 | m.removedExports.length === 0) {
611 | treeSharkingTpl(moduleName, size);
612 | }
613 | const query = getQuery(m.name);
614 | if (query.find((i) => i === 'commonjs-module')) {
615 | commonjsTpl(moduleName);
616 | }
617 | if (moduleName.indexOf('polyfill') > -1) {
618 | polyfillTpl(moduleName, size);
619 | }
620 | }
621 | if (name) {
622 | if (name === 'moment.js') {
623 | replaceTpl(name, 'dayjs');
624 | }
625 | if (name === 'lodash.js') {
626 | replaceTpl(name, 'lodash-es.js');
627 | }
628 | if (name.indexOf('polyfill') > -1) {
629 | polyfillTpl(name, size);
630 | }
631 | if (name === 'js-file-downloader.js') {
632 | rewriteTpl(name, size, '该模块使用webpack打包,体积较大,建议参照核心功能重写,减少约50%体积');
633 | }
634 | }
635 | }
636 | }
637 | });
638 | }
639 | return advise;
640 | }
641 |
642 | function bundleAnalyzer(userOpts) {
643 | return {
644 | name: 'rollup-bundle-analyzer',
645 | // 暂时先用transform来获取原代码,用resolveId hook会有不执行的情况
646 | transform(code, id) {
647 | codeMap[id] = code;
648 | // babel
649 | // css
650 | // filer return [ast, map, code]
651 | return null;
652 | },
653 | async generateBundle(
654 | // @ts-ignore
655 | outputOptions, outputBundle) {
656 | // @ts-ignore
657 | const defaultOpts = {
658 | analyzerMode: 'server',
659 | host: '127.0.0.1',
660 | port: 9800,
661 | reportFilename: 'bundle-analyzer.html',
662 | openBrowser: true,
663 | statsFilename: 'stats.json',
664 | logLevel: 'info',
665 | bundleDir: './'
666 | };
667 | const options = { ...defaultOpts, ...userOpts };
668 | const getModuleDataFun = () => getMixCharData(outputBundle);
669 | const { customHandle } = options;
670 | const advise = await generateAdvise(outputBundle, this);
671 | if (options.analyzerMode === 'server') {
672 | startViewServe(getModuleDataFun, options);
673 | }
674 | else if (options.analyzerMode === 'json') {
675 | const chartData = await getMixCharData(outputBundle);
676 | this.emitFile({
677 | type: 'asset',
678 | name: options.statsFilename,
679 | fileName: options.statsFilename,
680 | source: JSON.stringify(chartData)
681 | });
682 | }
683 | else if (options.analyzerMode === 'static') {
684 | const chartData = await getMixCharData(outputBundle);
685 | const html = renderViewer({
686 | title: 'rollup-bundle-analyzer',
687 | enableWebSocket: false,
688 | chartData,
689 | mode: 'static',
690 | defaultSizes: 'stat'
691 | });
692 | this.emitFile({
693 | type: 'asset',
694 | name: options.reportFilename,
695 | fileName: options.reportFilename,
696 | source: html
697 | });
698 | }
699 | else if (options.analyzerMode === 'custom') {
700 | const chartData = await getMixCharData(outputBundle);
701 | const html = renderViewer({
702 | title: 'rollup-bundle-analyzer',
703 | enableWebSocket: false,
704 | chartData,
705 | mode: 'static',
706 | defaultSizes: 'stat'
707 | });
708 | if (typeof customHandle === 'function') {
709 | customHandle(chartData, html, advise);
710 | }
711 | }
712 | else {
713 | // @ts-ignore
714 | logger.error(`${pkg.red('analyzerMode只支持server, json, static, custom')} `);
715 | }
716 | }
717 | };
718 | }
719 |
720 | exports.default = bundleAnalyzer;
721 | module.exports = Object.assign(exports.default, exports);
722 | //# sourceMappingURL=index.js.map
723 |
--------------------------------------------------------------------------------
/dist/cjs/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sources":["../../src/size.ts","../../src/tree/Node.ts","../../src/tree/Module.ts","../../src/tree/utils.ts","../../src/tree/Folder.ts","../../src/parseModule.ts","../../src/logger.ts","../../src/utils.ts","../../src/template.ts","../../src/viewer.ts","../../src/generateAdvise.ts","../../src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null],"names":["sizeGetter","minify","__dirname","projectRoot","__filename","url","path","http","chalk"],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/es/index.js:
--------------------------------------------------------------------------------
1 | import pkg from 'chalk';
2 | import { minify } from 'terser';
3 | import _ from 'lodash';
4 | import gzipSize from 'gzip-size';
5 | import * as path from 'path';
6 | import path__default from 'path';
7 | import * as http from 'http';
8 | import * as url from 'url';
9 | import url__default from 'url';
10 | import WebSocket from 'ws';
11 | import sirv from 'sirv';
12 | import 'util';
13 | import opener from 'opener';
14 | import fs from 'fs';
15 |
16 | const createBrotliSizeGetter = () => {
17 | return (code) => {
18 | // eslint-disable-next-line no-console
19 | const data = Buffer.from(code || '', 'utf-8');
20 | return data.length;
21 | };
22 | };
23 | const sizeGetter$1 = (code) => {
24 | const data = Buffer.from(code, 'utf-8');
25 | return data.length;
26 | };
27 |
28 | class Node {
29 | constructor(name, parent) {
30 | this.name = name;
31 | this.parent = parent;
32 | }
33 | get path() {
34 | return this.name;
35 | }
36 | get isRoot() {
37 | return !this.parent;
38 | }
39 | }
40 |
41 | const sizeGetter = createBrotliSizeGetter();
42 | class Module extends Node {
43 | constructor(name, data, parent) {
44 | super(name, parent);
45 | this.data = data;
46 | }
47 | get size() {
48 | var _a;
49 | return sizeGetter((_a = this.originCode) !== null && _a !== void 0 ? _a : '');
50 | }
51 | get code() {
52 | return this.data.code;
53 | }
54 | get originCode() {
55 | return this.data.originCode;
56 | }
57 | get renderSize() {
58 | var _a;
59 | return sizeGetter((_a = this.data.code) !== null && _a !== void 0 ? _a : '');
60 | }
61 | get gzipSize() {
62 | if (!_.has(this, '_gzipSize')) {
63 | this._gzipSize = this.code ? gzipSize.sync(this.code) : undefined;
64 | }
65 | return this._gzipSize;
66 | }
67 | toChartData() {
68 | return {
69 | id: this.data.name,
70 | label: this.name,
71 | path: this.path,
72 | statSize: this.size,
73 | parsedSize: this.renderSize,
74 | gzipSize: this.gzipSize
75 | };
76 | }
77 | }
78 |
79 | function getModulePathParts(moduleData) {
80 | const projectRoot = process.cwd();
81 | const parsedPath = moduleData.name
82 | .replace(`${projectRoot}/`, '')
83 | .split('/')
84 | // Replacing `~` with `node_modules`
85 | .map((part) => (part === '~' ? 'node_modules' : part));
86 | return parsedPath.length ? parsedPath : null;
87 | }
88 |
89 | class Folder extends Node {
90 | constructor(name, parent) {
91 | super(name, parent);
92 | this.children = Object.create(null);
93 | }
94 | get gzipSize() {
95 | if (!_.has(this, '_gzipSize')) {
96 | this._gzipSize = this.code ? gzipSize.sync(this.code) : 0;
97 | }
98 | return this._gzipSize;
99 | }
100 | get code() {
101 | if (!_.has(this, '_code')) {
102 | this._code = this.walk((node, code) => { var _a; return (_a = code + node.code) !== null && _a !== void 0 ? _a : ''; }, '', false);
103 | }
104 | return this._code;
105 | }
106 | get originCode() {
107 | if (!_.has(this, '_originCode')) {
108 | this._originCode = this.walk((node, originCode) => { var _a; return (_a = originCode + node.originCode) !== null && _a !== void 0 ? _a : ''; }, '', false);
109 | }
110 | return this._originCode;
111 | }
112 | get size() {
113 | if (!_.has(this, '_size')) {
114 | this._size = this.walk((node, size) => size + node.size, 0, false);
115 | }
116 | return this._size;
117 | }
118 | get renderSize() {
119 | if (!_.has(this, '_renderSize')) {
120 | this._renderSize = this.walk((node, renderSize) => renderSize + node.renderSize, 0, false);
121 | }
122 | return this._renderSize;
123 | }
124 | getChild(name) {
125 | return this.children[name];
126 | }
127 | addChildModule(module) {
128 | const { name } = module;
129 | const currentChild = this.children[name];
130 | if (currentChild && currentChild instanceof Folder)
131 | return;
132 | // eslint-disable-next-line no-param-reassign
133 | module.parent = this;
134 | this.children[name] = module;
135 | delete this._size;
136 | delete this._code;
137 | }
138 | addChildFolder(folder) {
139 | this.children[folder.name] = folder;
140 | delete this._size;
141 | delete this._code;
142 | return folder;
143 | }
144 | walk(walker, state = {}, deep = true) {
145 | let stopped = false;
146 | function stop(finalState) {
147 | stopped = true;
148 | return finalState;
149 | }
150 | // eslint-disable-next-line consistent-return
151 | Object.values(this.children).forEach((child) => {
152 | if (deep && child.walk) {
153 | // eslint-disable-next-line no-param-reassign
154 | state = child.walk(walker, state, stop);
155 | }
156 | else {
157 | // eslint-disable-next-line no-param-reassign
158 | state = walker(child, state, stop);
159 | }
160 | if (stopped)
161 | return false;
162 | });
163 | return state;
164 | }
165 | mergeNestedFolders() {
166 | if (!this.isRoot) {
167 | let childNames;
168 | // 合并只有一个文件的目录
169 | // eslint-disable-next-line no-cond-assign
170 | while ((childNames = Object.keys(this.children)).length === 1) {
171 | // eslint-disable-next-line prefer-destructuring
172 | const childName = childNames[0];
173 | const onlyChild = this.children[childName];
174 | if (onlyChild instanceof this.constructor) {
175 | this.name += `/${onlyChild.name}`;
176 | this.children = onlyChild.children;
177 | }
178 | else {
179 | break;
180 | }
181 | }
182 | }
183 | this.walk((child) => {
184 | // eslint-disable-next-line no-param-reassign
185 | child.parent = this;
186 | if (child.mergeNestedFolders) {
187 | child.mergeNestedFolders();
188 | }
189 | }, null, false);
190 | }
191 | addModule(moduleData) {
192 | const pathParts = getModulePathParts(moduleData);
193 | if (!pathParts) {
194 | return;
195 | }
196 | const [folders, fileName] = [pathParts.slice(0, -1), _.last(pathParts)];
197 | let currentFolder = this;
198 | folders.forEach((folderName) => {
199 | let childNode = currentFolder.getChild(folderName);
200 | if (!childNode || !(childNode instanceof Folder)) {
201 | childNode = currentFolder.addChildFolder(new Folder(folderName, this));
202 | }
203 | currentFolder = childNode;
204 | });
205 | if (fileName) {
206 | const module = new Module(fileName, moduleData, this);
207 | currentFolder.addChildModule(module);
208 | }
209 | }
210 | toChartData() {
211 | return {
212 | label: this.name,
213 | path: this.path,
214 | statSize: this.size,
215 | parsedSize: this.renderSize,
216 | groups: _.invokeMap(this.children, 'toChartData'),
217 | gzipSize: this.gzipSize
218 | };
219 | }
220 | }
221 | function createModulesTree(modules) {
222 | const root = new Folder('.');
223 | modules.forEach((module) => root.addModule(module));
224 | root.mergeNestedFolders();
225 | return root;
226 | }
227 |
228 | const codeMap = {};
229 | async function transformModule(module, id, resolver) {
230 | var _a, _b;
231 | // fix https://github.com/ritz078/rollup-plugin-filesize/issues/57
232 | // @ts-ignore
233 | delete module.isAsset;
234 | // @ts-ignore
235 | const transformedModule = {
236 | ...module,
237 | name: id,
238 | modules: [],
239 | originCode: codeMap[id],
240 | code: (_b = (await minify((_a = module.code) !== null && _a !== void 0 ? _a : '', {
241 | // module: true,
242 | safari10: true
243 | // toplevel: true
244 | })).code) !== null && _b !== void 0 ? _b : ''
245 | };
246 | if (resolver) {
247 | resolver(transformedModule);
248 | }
249 | if (!('modules' in module)) {
250 | transformedModule.modules = [];
251 | return transformedModule;
252 | }
253 | transformedModule.modules = [];
254 | for (const [id, m] of Object.entries(module.modules)) {
255 | // eslint-disable-next-line no-await-in-loop
256 | transformedModule.modules.push((await transformModule(m, id, resolver)));
257 | }
258 | return transformedModule;
259 | }
260 | async function getModuleData(outputBundle) {
261 | const modules = {};
262 | for (const [id, module] of Object.entries(outputBundle)) {
263 | // eslint-disable-next-line
264 | const chunk = await transformModule(module, id);
265 | chunk.tree = createModulesTree(chunk.modules);
266 | chunk.size = sizeGetter$1(chunk.code || '');
267 | modules[id] = chunk;
268 | if (module.type === 'asset') {
269 | const { source } = module;
270 | // 二进制资源文件,比如图片
271 | if (typeof source === 'string') {
272 | chunk.size = sizeGetter$1(source);
273 | }
274 | else {
275 | chunk.size = Buffer.byteLength(source);
276 | }
277 | }
278 | }
279 | return Object.entries(modules).map(([name, asset]) => {
280 | return {
281 | label: name,
282 | isAsset: true,
283 | statSize: asset.tree.size || asset.size,
284 | parsedSize: asset.tree.renderSize,
285 | gzipSize: asset.tree.gzipSize,
286 | groups: _.invokeMap(asset.tree.children, 'toChartData')
287 | };
288 | });
289 | }
290 | async function getSplitLibData(outputBundle) {
291 | const modules = {
292 | library: {},
293 | project: {}
294 | };
295 | const libModules = [];
296 | const projectModules = [];
297 | for (const [id, module] of Object.entries(outputBundle)) {
298 | if (module.type === 'chunk') {
299 | // eslint-disable-next-line
300 | await transformModule(module, id, (m) => {
301 | if (m.name === id) {
302 | return;
303 | }
304 | if (m.name.indexOf('node_modules') > -1 || m.name.indexOf('vite') > -1) {
305 | libModules.push(m);
306 | }
307 | else {
308 | projectModules.push(m);
309 | }
310 | });
311 | }
312 | }
313 | modules.library.tree = createModulesTree(libModules);
314 | modules.project.tree = createModulesTree(projectModules);
315 | return Object.entries(modules).map(([name, asset]) => {
316 | return {
317 | label: name,
318 | isAsset: true,
319 | statSize: asset.tree.size,
320 | parsedSize: asset.tree.renderSize,
321 | gzipSize: asset.tree.gzipSize,
322 | groups: _.invokeMap(asset.tree.children, 'toChartData')
323 | };
324 | });
325 | }
326 | async function getMixCharData(outputBundle) {
327 | return {
328 | lib: await getSplitLibData(outputBundle),
329 | default: await getModuleData(outputBundle)
330 | };
331 | }
332 |
333 | const LEVELS = ['debug', 'info', 'warn', 'error', 'silent'];
334 | const LOG_PREFIX = '\n[rollup-plugin-analyzer]';
335 | const LEVEL_TO_CONSOLE_METHOD = new Map([
336 | ['debug', 'log'],
337 | ['info', 'log'],
338 | ['warn', 'log']
339 | ]);
340 | class Logger {
341 | constructor(level = Logger.defaultLevel) {
342 | this.activeLevels = new Set();
343 | this.setLogLevel(level);
344 | }
345 | static log(level, ...args) {
346 | const operation = LEVEL_TO_CONSOLE_METHOD.get(level) || level;
347 | // eslint-disable-next-line no-console
348 | console[operation](...args);
349 | }
350 | setLogLevel(level) {
351 | const levelIndex = LEVELS.indexOf(level);
352 | if (levelIndex === -1)
353 | throw new Error(`Invalid log level "${level}". Use one of these: ${LEVELS.join(', ')}`);
354 | this.activeLevels.clear();
355 | for (const [i, levelValue] of LEVELS.entries()) {
356 | if (i >= levelIndex)
357 | this.activeLevels.add(levelValue);
358 | }
359 | }
360 | }
361 | Logger.levels = LEVELS;
362 | Logger.defaultLevel = 'info';
363 | LEVELS.forEach((level) => {
364 | if (level === 'silent')
365 | return;
366 | // @ts-ignore
367 | Logger.prototype[level] = function log(...args) {
368 | if (this.activeLevels.has(level))
369 | Logger.log(level, LOG_PREFIX, ...args);
370 | };
371 | });
372 | const logger = new Logger('info');
373 |
374 | const open = function (uri) {
375 | try {
376 | opener(uri);
377 | }
378 | catch (err) {
379 | // @ts-ignore
380 | logger.debug(`Opener failed to open "${uri}":\n${err}`);
381 | }
382 | };
383 |
384 | // @ts-ignore
385 | const fileName = url__default.fileURLToPath(import.meta.url);
386 | const __dirname$1 = path__default.dirname(fileName);
387 | const projectRoot$1 = path__default.resolve(__dirname$1, '..', '..');
388 | const assetsRoot = path__default.join(projectRoot$1, 'public');
389 | function escapeJson(json) {
390 | return JSON.stringify(json).replace(/ `${string}${values[index] || ''}`).join('');
401 | }
402 | function getScript(filename, mode) {
403 | if (mode === 'static') {
404 | return ` `;
405 | }
406 | return ``;
407 | }
408 | function renderViewer({ title, enableWebSocket, chartData, defaultSizes, mode } = {}) {
409 | return html `
410 |
411 |
412 |
413 |
414 | ${_.escape(title)}
415 |
418 | ${getScript('viewer.js', mode)}
419 |
420 |
421 |
422 |
423 |
427 |
428 | `;
429 | }
430 |
431 | const { bold } = pkg;
432 | let serverInstance;
433 | // @ts-ignore
434 | // eslint-disable-next-line no-underscore-dangle
435 | const __filename = url.fileURLToPath(import.meta.url);
436 | const __dirname = path.dirname(__filename);
437 | const projectRoot = path.resolve(__dirname, '..', '..');
438 | function analyzerUrl(options) {
439 | const { listenHost, boundAddress } = options;
440 | return `http://${listenHost}:${boundAddress.port}`;
441 | }
442 | async function startServer(opts, moduleData, getModuleDataFun) {
443 | const { port = 8888, host = '127.0.0.1', openBrowser = true } = opts || {};
444 | const sirvMiddleware = sirv(`${projectRoot}/public`, { dev: true });
445 | const server = http.createServer((req, res) => {
446 | if (req.method === 'GET' && req.url === '/') {
447 | const html = renderViewer({
448 | title: 'rollup-bundle-analyzer',
449 | enableWebSocket: true,
450 | chartData: moduleData,
451 | mode: 'serve',
452 | defaultSizes: 'stat'
453 | });
454 | res.writeHead(200, { 'Content-Type': 'text/html' });
455 | res.end(html);
456 | }
457 | else {
458 | sirvMiddleware(req, res);
459 | }
460 | });
461 | await new Promise((resolve) => {
462 | // @ts-ignore
463 | server.listen(port, host, () => {
464 | resolve();
465 | // eslint-disable-next-line @typescript-eslint/no-shadow
466 | const url = analyzerUrl({
467 | listenPort: port,
468 | listenHost: host,
469 | boundAddress: server.address()
470 | });
471 | // @ts-ignore
472 | logger.info(`${bold('Rollup Bundle Analyzer')} is started at ${bold(url)}\n` +
473 | `Use ${bold('Ctrl+C')} to close it`);
474 | if (openBrowser) {
475 | open(url);
476 | }
477 | });
478 | });
479 | const wss = new WebSocket.Server({ server });
480 | wss.on('connection', (ws) => {
481 | ws.on('error', (err) => {
482 | // Ignore network errors like `ECONNRESET`, `EPIPE`, etc.
483 | // @ts-ignore
484 | if (err.errno)
485 | return;
486 | // @ts-ignore
487 | logger.info(err.message);
488 | });
489 | });
490 | // eslint-disable-next-line consistent-return
491 | return {
492 | ws: wss,
493 | http: server,
494 | updateChartData
495 | };
496 | async function updateChartData() {
497 | const newChartData = await getModuleDataFun();
498 | if (!newChartData)
499 | return;
500 | wss.clients.forEach((client) => {
501 | if (client.readyState === WebSocket.OPEN) {
502 | client.send(JSON.stringify({
503 | event: 'chartDataUpdated',
504 | data: newChartData
505 | }));
506 | }
507 | });
508 | }
509 | }
510 | async function startViewServe(getModuleDataFun, pluginOpt) {
511 | const moduleData = await getModuleDataFun();
512 | if (serverInstance) {
513 | (await serverInstance).updateChartData();
514 | }
515 | else {
516 | serverInstance = await startServer({
517 | openBrowser: pluginOpt.openBrowser,
518 | host: pluginOpt.host,
519 | port: pluginOpt.port,
520 | bundleDir: ''
521 | }, moduleData, getModuleDataFun);
522 | }
523 | }
524 |
525 | const getQuery = (schema) => schema.split('?').slice(1);
526 | const formatName = (name) => { var _a; return (_a = name.split('.')) === null || _a === void 0 ? void 0 : _a[0]; };
527 | async function generateAdvise(outputBundle, context) {
528 | const advise = {
529 | treeSharking: {},
530 | replace: {},
531 | commonjs: {},
532 | polyfill: {},
533 | rewrite: {}
534 | };
535 | const replaceTpl = (moduleName, target) => {
536 | const name = formatName(moduleName);
537 | advise.replace[name] = {
538 | target
539 | };
540 | };
541 | const commonjsTpl = (moduleName) => {
542 | const name = formatName(moduleName);
543 | advise.commonjs[name] = {};
544 | };
545 | const treeSharkingTpl = (moduleName, size) => {
546 | const name = formatName(moduleName);
547 | advise.treeSharking[name] = {
548 | size
549 | };
550 | };
551 | const polyfillTpl = (moduleName, size) => {
552 | const name = formatName(moduleName);
553 | advise.polyfill[name] = {
554 | size
555 | };
556 | };
557 | const rewriteTpl = (moduleName, size, desc) => {
558 | const name = formatName(moduleName);
559 | advise.rewrite[name] = {
560 | size,
561 | desc
562 | };
563 | };
564 | for (const [id, module] of Object.entries(outputBundle)) {
565 | // eslint-disable-next-line no-await-in-loop
566 | await transformModule(module, id, (m) => {
567 | const pathParts = getModulePathParts(m);
568 | const name = _.last(pathParts);
569 | const moduleInfo = context.getModuleInfo(m.name);
570 | if (moduleInfo) {
571 | // 判断是否是项目引进的包,再判断是否可以优化
572 | const isImportByProject = moduleInfo.importers.find((i) => i.indexOf('node_modules') === -1);
573 | if (isImportByProject) {
574 | const size = sizeGetter$1(m.code || '');
575 | if (pathParts && m.name.indexOf('node_modules') > -1 && /\.js| \.ts/.test(m.name)) {
576 | const nodeModulesIndex = pathParts.findIndex((i) => i === 'node_modules');
577 | if (nodeModulesIndex === -1) {
578 | return;
579 | }
580 | let moduleName = pathParts[nodeModulesIndex + 1];
581 | // eg @element-plus/icons-vue
582 | if (moduleName.indexOf('@') > -1) {
583 | moduleName = `${moduleName}/${pathParts[nodeModulesIndex + 2]}`;
584 | }
585 | if (m.removedExports &&
586 | Array.isArray(m.removedExports) &&
587 | m.removedExports.length === 0) {
588 | treeSharkingTpl(moduleName, size);
589 | }
590 | const query = getQuery(m.name);
591 | if (query.find((i) => i === 'commonjs-module')) {
592 | commonjsTpl(moduleName);
593 | }
594 | if (moduleName.indexOf('polyfill') > -1) {
595 | polyfillTpl(moduleName, size);
596 | }
597 | }
598 | if (name) {
599 | if (name === 'moment.js') {
600 | replaceTpl(name, 'dayjs');
601 | }
602 | if (name === 'lodash.js') {
603 | replaceTpl(name, 'lodash-es.js');
604 | }
605 | if (name.indexOf('polyfill') > -1) {
606 | polyfillTpl(name, size);
607 | }
608 | if (name === 'js-file-downloader.js') {
609 | rewriteTpl(name, size, '该模块使用webpack打包,体积较大,建议参照核心功能重写,减少约50%体积');
610 | }
611 | }
612 | }
613 | }
614 | });
615 | }
616 | return advise;
617 | }
618 |
619 | function bundleAnalyzer(userOpts) {
620 | return {
621 | name: 'rollup-bundle-analyzer',
622 | // 暂时先用transform来获取原代码,用resolveId hook会有不执行的情况
623 | transform(code, id) {
624 | codeMap[id] = code;
625 | // babel
626 | // css
627 | // filer return [ast, map, code]
628 | return null;
629 | },
630 | async generateBundle(
631 | // @ts-ignore
632 | outputOptions, outputBundle) {
633 | // @ts-ignore
634 | const defaultOpts = {
635 | analyzerMode: 'server',
636 | host: '127.0.0.1',
637 | port: 9800,
638 | reportFilename: 'bundle-analyzer.html',
639 | openBrowser: true,
640 | statsFilename: 'stats.json',
641 | logLevel: 'info',
642 | bundleDir: './'
643 | };
644 | const options = { ...defaultOpts, ...userOpts };
645 | const getModuleDataFun = () => getMixCharData(outputBundle);
646 | const { customHandle } = options;
647 | const advise = await generateAdvise(outputBundle, this);
648 | if (options.analyzerMode === 'server') {
649 | startViewServe(getModuleDataFun, options);
650 | }
651 | else if (options.analyzerMode === 'json') {
652 | const chartData = await getMixCharData(outputBundle);
653 | this.emitFile({
654 | type: 'asset',
655 | name: options.statsFilename,
656 | fileName: options.statsFilename,
657 | source: JSON.stringify(chartData)
658 | });
659 | }
660 | else if (options.analyzerMode === 'static') {
661 | const chartData = await getMixCharData(outputBundle);
662 | const html = renderViewer({
663 | title: 'rollup-bundle-analyzer',
664 | enableWebSocket: false,
665 | chartData,
666 | mode: 'static',
667 | defaultSizes: 'stat'
668 | });
669 | this.emitFile({
670 | type: 'asset',
671 | name: options.reportFilename,
672 | fileName: options.reportFilename,
673 | source: html
674 | });
675 | }
676 | else if (options.analyzerMode === 'custom') {
677 | const chartData = await getMixCharData(outputBundle);
678 | const html = renderViewer({
679 | title: 'rollup-bundle-analyzer',
680 | enableWebSocket: false,
681 | chartData,
682 | mode: 'static',
683 | defaultSizes: 'stat'
684 | });
685 | if (typeof customHandle === 'function') {
686 | customHandle(chartData, html, advise);
687 | }
688 | }
689 | else {
690 | // @ts-ignore
691 | logger.error(`${pkg.red('analyzerMode只支持server, json, static, custom')} `);
692 | }
693 | }
694 | };
695 | }
696 |
697 | export { bundleAnalyzer as default };
698 | //# sourceMappingURL=index.js.map
699 |
--------------------------------------------------------------------------------
/dist/es/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sources":["../../src/size.ts","../../src/tree/Node.ts","../../src/tree/Module.ts","../../src/tree/utils.ts","../../src/tree/Folder.ts","../../src/parseModule.ts","../../src/logger.ts","../../src/utils.ts","../../src/template.ts","../../src/viewer.ts","../../src/generateAdvise.ts","../../src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null],"names":["sizeGetter","url","__dirname","path","projectRoot","chalk"],"mappings":"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"}
--------------------------------------------------------------------------------
/dist/es/package.json:
--------------------------------------------------------------------------------
1 | {"type":"module"}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rollup-plugin-bundle-analyzer",
3 | "version": "1.6.6",
4 | "publishConfig": {
5 | "access": "public"
6 | },
7 | "description": "rollup plugin that represents bundle content as convenient interactive zoomable treemap",
8 | "license": "MIT",
9 | "author": "xiaojie",
10 | "homepage": "",
11 | "bugs": "",
12 | "main": "./dist/cjs/index.js",
13 | "module": "./dist/es/index.js",
14 | "exports": {
15 | "import": "./dist/es/index.js",
16 | "types": "./types/index.d.ts",
17 | "default": "./dist/cjs/index.js"
18 | },
19 | "engines": {
20 | "node": ">=14.0.0"
21 | },
22 | "devDependencies": {
23 | "@babel/core": "7.14.3",
24 | "@babel/plugin-proposal-class-properties": "7.13.0",
25 | "@babel/plugin-proposal-decorators": "7.14.2",
26 | "@babel/plugin-transform-runtime": "7.14.3",
27 | "@babel/preset-env": "7.14.2",
28 | "@babel/preset-react": "7.13.13",
29 | "@babel/runtime": "7.14.0",
30 | "@carrotsearch/foamtree": "3.5.0",
31 | "@rollup/plugin-commonjs": "^23.0.0",
32 | "@rollup/plugin-node-resolve": "^14.1.0",
33 | "@rollup/plugin-typescript": "^8.5.0",
34 | "@types/lodash": "^4.14.186",
35 | "@types/opener": "^1.4.0",
36 | "@types/uglify-js": "^3.17.1",
37 | "@types/ws": "^8.5.3",
38 | "autoprefixer": "10.2.5",
39 | "babel-eslint": "10.1.0",
40 | "babel-loader": "8.2.2",
41 | "babel-plugin-lodash": "3.3.4",
42 | "chai": "4.3.4",
43 | "chai-subset": "1.6.0",
44 | "classnames": "2.3.1",
45 | "core-js": "3.12.1",
46 | "css-loader": "5.2.5",
47 | "cssnano": "5.0.4",
48 | "del": "6.0.0",
49 | "del-cli": "^5.0.0",
50 | "eslint-plugin-react": "7.23.2",
51 | "filesize": "^6.3.0",
52 | "globby": "11.0.3",
53 | "jest": "27.2.2",
54 | "mobx": "5.15.7",
55 | "mobx-react": "6.3.1",
56 | "postcss": "8.3.0",
57 | "postcss-icss-values": "2.0.2",
58 | "postcss-loader": "5.3.0",
59 | "preact": "10.5.13",
60 | "rollup": "^3.0.0-7",
61 | "stream-combiner2": "1.1.1",
62 | "style-loader": "2.0.0",
63 | "terser-webpack-plugin": "5.1.2",
64 | "tslib": "^2.4.1",
65 | "typescript": "^4.8.3",
66 | "url-loader": "4.1.1",
67 | "webpack": "5.37.1",
68 | "webpack-cli": "3.3.12",
69 | "webpack-dev-server": "3.11.2"
70 | },
71 | "repository": "https://github.com/Jerry2023/rollup-plugin-bundle-analyzer",
72 | "scripts": {
73 | "build:client": "webpack",
74 | "build-all": "pnpm run build:client & pnpm run build",
75 | "build": "rollup -c",
76 | "ci:coverage": "nyc pnpm test && nyc report --reporter=text-lcov > coverage.lcov",
77 | "ci:lint": "pnpm build && pnpm lint",
78 | "ci:lint:commits": "commitlint --from=${CIRCLE_BRANCH} --to=${CIRCLE_SHA1}",
79 | "ci:test": "pnpm test -- --verbose",
80 | "prebuild": "del-cli dist",
81 | "prepare": "if [ ! -d 'dist' ]; then pnpm build; fi",
82 | "prerelease": "pnpm build",
83 | "pretest": "pnpm build",
84 | "release": "pnpm --workspace-root plugin:release --pkg $npm_package_name",
85 | "test": "ava",
86 | "test:ts": "tsc --noEmit"
87 | },
88 | "files": [
89 | "dist",
90 | "!dist/**/*.map",
91 | "types",
92 | "README.md",
93 | "public",
94 | ""
95 | ],
96 | "keywords": [
97 | "vite",
98 | "rollup",
99 | "plugin",
100 | "resolve",
101 | "bundle",
102 | "size"
103 | ],
104 | "peerDependencies": {
105 | "rollup": "^1.20.0||^2.0.0||^3.0.0"
106 | },
107 | "peerDependenciesMeta": {
108 | "rollup": {
109 | "optional": true
110 | }
111 | },
112 | "dependencies": {
113 | "chalk": "^4.1.0",
114 | "commander": "^7.2.0",
115 | "gzip-size": "^6.0.0",
116 | "lodash": "^4.17.21",
117 | "opener": "^1.5.2",
118 | "sirv": "^1.0.7",
119 | "terser": "^5.15.1",
120 | "ws": "^7.3.1"
121 | },
122 | "types": "./types/index.d.ts",
123 | "ava": {
124 | "files": [
125 | "!**/fixtures/**",
126 | "!**/output/**",
127 | "!**/helpers/**",
128 | "!**/recipes/**",
129 | "!**/types.ts"
130 | ]
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { readFileSync } from 'fs';
2 | import { builtinModules } from 'module';
3 |
4 | // eslint-disable-next-line import/no-extraneous-dependencies
5 | import typescript from '@rollup/plugin-typescript';
6 | import commonjs from '@rollup/plugin-commonjs';
7 | import { nodeResolve } from '@rollup/plugin-node-resolve';
8 |
9 | /**
10 | * Create a base rollup config
11 | * @param {Record} pkg Imported package.json
12 | * @param {string[]} external Imported package.json
13 | * @returns {import('rollup').RollupOptions}
14 | */
15 | export function createConfig({ pkg, external = [] }) {
16 | return {
17 | input: 'src/index.ts', external: Object.keys(pkg.dependencies || {})
18 | .concat(Object.keys(pkg.peerDependencies || {}))
19 | .concat(builtinModules)
20 | .concat(external), onwarn: (warning) => {
21 | throw Object.assign(new Error(), warning);
22 | }, strictDeprecations: true, output: [{
23 | format: 'cjs',
24 | file: pkg.main,
25 | exports: 'named',
26 | footer: 'module.exports = Object.assign(exports.default, exports);',
27 | sourcemap: true
28 | }, {
29 | format: 'es', file: pkg.module, plugins: [emitModulePackageFile()], sourcemap: true
30 | }], plugins: [typescript({ sourceMap: true }), nodeResolve(), commonjs()]
31 | };
32 | }
33 |
34 | export function emitModulePackageFile() {
35 | return {
36 | name: 'emit-module-package-file', generateBundle() {
37 | this.emitFile({
38 | type: 'asset', fileName: 'package.json', source: `{"type":"module"}`
39 | });
40 | }
41 | };
42 | }
43 |
44 |
45 | export default createConfig({
46 | pkg: JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'))
47 | });
48 |
--------------------------------------------------------------------------------
/src/generateAdvise.ts:
--------------------------------------------------------------------------------
1 | import { OutputBundle, PluginContext } from 'rollup';
2 |
3 | import _ from 'lodash';
4 |
5 | import { Advise } from '../types';
6 |
7 | import { transformModule } from './parseModule';
8 | import { getModulePathParts } from './tree/utils';
9 | import { sizeGetter } from './size';
10 |
11 | const getQuery = (schema: string) => schema.split('?').slice(1);
12 |
13 | const formatName = (name: string) => name.split('.')?.[0];
14 |
15 | export default async function generateAdvise(outputBundle: OutputBundle, context: PluginContext) {
16 | const advise: Advise = {
17 | treeSharking: {},
18 | replace: {},
19 | commonjs: {},
20 | polyfill: {},
21 | rewrite: {}
22 | };
23 |
24 | const replaceTpl = (moduleName: string, target: string) => {
25 | const name = formatName(moduleName);
26 | advise.replace[name] = {
27 | target
28 | };
29 | };
30 |
31 | const commonjsTpl = (moduleName: string) => {
32 | const name = formatName(moduleName);
33 | advise.commonjs[name] = {};
34 | };
35 |
36 | const treeSharkingTpl = (moduleName: string, size: number) => {
37 | const name = formatName(moduleName);
38 | advise.treeSharking[name] = {
39 | size
40 | };
41 | };
42 |
43 | const polyfillTpl = (moduleName: string, size: number) => {
44 | const name = formatName(moduleName);
45 | advise.polyfill[name] = {
46 | size
47 | };
48 | };
49 |
50 | const rewriteTpl = (moduleName: string, size: number, desc: string) => {
51 | const name = formatName(moduleName);
52 | advise.rewrite[name] = {
53 | size,
54 | desc
55 | };
56 | };
57 |
58 | for (const [id, module] of Object.entries(outputBundle)) {
59 | // eslint-disable-next-line no-await-in-loop
60 | await transformModule(module, id, (m) => {
61 | const pathParts = getModulePathParts(m);
62 | const name = _.last(pathParts);
63 | const moduleInfo = context.getModuleInfo(m.name);
64 | if (moduleInfo) {
65 | // 判断是否是项目引进的包,再判断是否可以优化
66 | const isImportByProject = moduleInfo.importers.find(
67 | (i) => i.indexOf('node_modules') === -1
68 | );
69 | if (isImportByProject) {
70 | const size = sizeGetter(m.code || '');
71 | if (pathParts && m.name.indexOf('node_modules') > -1 && /\.js| \.ts/.test(m.name)) {
72 | const nodeModulesIndex = pathParts.findIndex((i) => i === 'node_modules');
73 | if (nodeModulesIndex === -1) {
74 | return;
75 | }
76 | let moduleName = pathParts[nodeModulesIndex + 1];
77 | // eg @element-plus/icons-vue
78 | if (moduleName.indexOf('@') > -1) {
79 | moduleName = `${moduleName}/${pathParts[nodeModulesIndex + 2]}`;
80 | }
81 | if (
82 | m.removedExports &&
83 | Array.isArray(m.removedExports) &&
84 | m.removedExports.length === 0
85 | ) {
86 | treeSharkingTpl(moduleName, size);
87 | }
88 | const query = getQuery(m.name);
89 | if (query.find((i) => i === 'commonjs-module')) {
90 | commonjsTpl(moduleName);
91 | }
92 | if (moduleName.indexOf('polyfill') > -1) {
93 | polyfillTpl(moduleName, size);
94 | }
95 | }
96 | if (name) {
97 | if (name === 'moment.js') {
98 | replaceTpl(name, 'dayjs');
99 | }
100 | if (name === 'lodash.js') {
101 | replaceTpl(name, 'lodash-es.js');
102 | }
103 | if (name.indexOf('polyfill') > -1) {
104 | polyfillTpl(name, size);
105 | }
106 | if (name === 'js-file-downloader.js') {
107 | rewriteTpl(
108 | name,
109 | size,
110 | '该模块使用webpack打包,体积较大,建议参照核心功能重写,减少约50%体积'
111 | );
112 | }
113 | }
114 | }
115 | }
116 | });
117 | }
118 | return advise;
119 | }
120 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { NormalizedOutputOptions, OutputBundle, Plugin } from 'rollup';
2 |
3 | import chalk from 'chalk';
4 |
5 | import { PluginOptions } from '../types';
6 |
7 | import getMixCharData, { codeMap } from './parseModule';
8 | import startViewServe from './viewer';
9 | import renderViewer from './template';
10 | import logger from './logger';
11 | import generateAdvise from './generateAdvise';
12 |
13 | export default function bundleAnalyzer(userOpts: PluginOptions): Plugin {
14 | return {
15 | name: 'rollup-bundle-analyzer',
16 | // 暂时先用transform来获取原代码,用resolveId hook会有不执行的情况
17 | transform(code, id) {
18 | codeMap[id] = code;
19 | // babel
20 | // css
21 | // filer return [ast, map, code]
22 | return null;
23 | },
24 | async generateBundle(
25 | // @ts-ignore
26 | outputOptions: NormalizedOutputOptions,
27 | outputBundle: OutputBundle
28 | ): Promise {
29 | // @ts-ignore
30 | const defaultOpts: PluginOptions = {
31 | analyzerMode: 'server',
32 | host: '127.0.0.1',
33 | port: 9800,
34 | reportFilename: 'bundle-analyzer.html',
35 | openBrowser: true,
36 | statsFilename: 'stats.json',
37 | logLevel: 'info',
38 | bundleDir: './'
39 | };
40 |
41 | const options = { ...defaultOpts, ...userOpts };
42 | const getModuleDataFun = () => getMixCharData(outputBundle);
43 | const { customHandle } = options;
44 | const advise = await generateAdvise(outputBundle, this);
45 |
46 | if (options.analyzerMode === 'server') {
47 | startViewServe(getModuleDataFun, options);
48 | } else if (options.analyzerMode === 'json') {
49 | const chartData = await getMixCharData(outputBundle);
50 | this.emitFile({
51 | type: 'asset',
52 | name: options.statsFilename,
53 | fileName: options.statsFilename,
54 | source: JSON.stringify(chartData)
55 | });
56 | } else if (options.analyzerMode === 'static') {
57 | const chartData = await getMixCharData(outputBundle);
58 |
59 | const html = renderViewer({
60 | title: 'rollup-bundle-analyzer',
61 | enableWebSocket: false,
62 | chartData,
63 | mode: 'static',
64 | defaultSizes: 'stat'
65 | });
66 | this.emitFile({
67 | type: 'asset',
68 | name: options.reportFilename,
69 | fileName: options.reportFilename,
70 | source: html
71 | });
72 | } else if (options.analyzerMode === 'custom') {
73 | const chartData = await getMixCharData(outputBundle);
74 |
75 | const html = renderViewer({
76 | title: 'rollup-bundle-analyzer',
77 | enableWebSocket: false,
78 | chartData,
79 | mode: 'static',
80 | defaultSizes: 'stat'
81 | });
82 | if (typeof customHandle === 'function') {
83 | customHandle(chartData, html, advise);
84 | }
85 | } else {
86 | // @ts-ignore
87 | logger.error(`${chalk.red('analyzerMode只支持server, json, static, custom')} `);
88 | }
89 | }
90 | };
91 | }
92 |
--------------------------------------------------------------------------------
/src/logger.ts:
--------------------------------------------------------------------------------
1 | import type { Level } from '../types';
2 |
3 | const LEVELS = ['debug', 'info', 'warn', 'error', 'silent'];
4 | const LOG_PREFIX = '\n[rollup-plugin-analyzer]';
5 |
6 | const LEVEL_TO_CONSOLE_METHOD = new Map([
7 | ['debug', 'log'],
8 | ['info', 'log'],
9 | ['warn', 'log']
10 | ]);
11 |
12 | class Logger {
13 | static levels = LEVELS;
14 | static defaultLevel: Level = 'info';
15 | activeLevels = new Set();
16 |
17 | constructor(level: Level = Logger.defaultLevel) {
18 | this.setLogLevel(level);
19 | }
20 |
21 | static log(level: Level, ...args: any[]) {
22 | const operation = LEVEL_TO_CONSOLE_METHOD.get(level) || level;
23 | // eslint-disable-next-line no-console
24 | console[operation as Extract](...args);
25 | }
26 |
27 | private setLogLevel(level: Level) {
28 | const levelIndex = LEVELS.indexOf(level);
29 |
30 | if (levelIndex === -1)
31 | throw new Error(`Invalid log level "${level}". Use one of these: ${LEVELS.join(', ')}`);
32 |
33 | this.activeLevels.clear();
34 |
35 | for (const [i, levelValue] of LEVELS.entries()) {
36 | if (i >= levelIndex) this.activeLevels.add(levelValue);
37 | }
38 | }
39 | }
40 |
41 | LEVELS.forEach((level) => {
42 | if (level === 'silent') return;
43 |
44 | // @ts-ignore
45 | Logger.prototype[level] = function log(...args) {
46 | if (this.activeLevels.has(level)) Logger.log(level as Level, LOG_PREFIX, ...args);
47 | };
48 | });
49 | const logger = new Logger('info');
50 |
51 | export default logger;
52 |
--------------------------------------------------------------------------------
/src/parseModule.ts:
--------------------------------------------------------------------------------
1 | import { OutputAsset, OutputBundle, OutputChunk } from 'rollup';
2 | import { minify } from 'terser';
3 |
4 | import _ from 'lodash';
5 |
6 | import { CharData, MixCharData, Module, TransformedModule } from '../types';
7 |
8 | import { createModulesTree } from './tree/Folder';
9 | import { sizeGetter } from './size';
10 |
11 | export const codeMap: Record = {};
12 |
13 | export async function transformModule(
14 | module: Module,
15 | id: string,
16 | resolver?: (m: TransformedModule) => void
17 | ) {
18 | // fix https://github.com/ritz078/rollup-plugin-filesize/issues/57
19 | // @ts-ignore
20 | delete module.isAsset;
21 | // @ts-ignore
22 | const transformedModule: TransformedModule = {
23 | ...module,
24 | name: id,
25 | modules: [],
26 | originCode: codeMap[id],
27 | code:
28 | (
29 | await minify((module as OutputChunk).code ?? '', {
30 | // module: true,
31 | safari10: true
32 | // toplevel: true
33 | })
34 | ).code ?? ''
35 | };
36 | if (resolver) {
37 | resolver(transformedModule);
38 | }
39 | if (!('modules' in (module as OutputAsset))) {
40 | transformedModule.modules = [];
41 | return transformedModule;
42 | }
43 | transformedModule.modules = [];
44 | for (const [id, m] of Object.entries((module as OutputChunk).modules)) {
45 | // eslint-disable-next-line no-await-in-loop
46 | transformedModule.modules.push((await transformModule(m, id, resolver))!);
47 | }
48 | return transformedModule;
49 | }
50 |
51 | async function getModuleData(outputBundle: OutputBundle): Promise {
52 | const modules: {
53 | [id: string]: TransformedModule;
54 | } = {};
55 | for (const [id, module] of Object.entries(outputBundle)) {
56 | // eslint-disable-next-line
57 | const chunk = await transformModule(module, id);
58 | chunk.tree = createModulesTree(chunk.modules);
59 | chunk.size = sizeGetter(chunk.code || '');
60 | modules[id] = chunk;
61 | if (module.type === 'asset') {
62 | const { source } = module;
63 | // 二进制资源文件,比如图片
64 | if (typeof source === 'string') {
65 | chunk.size = sizeGetter(source);
66 | } else {
67 | chunk.size = Buffer.byteLength(source);
68 | }
69 | }
70 | }
71 | return Object.entries(modules).map(([name, asset]) => {
72 | return {
73 | label: name,
74 | isAsset: true,
75 | statSize: asset.tree.size || asset.size,
76 | parsedSize: asset.tree.renderSize,
77 | gzipSize: asset.tree.gzipSize,
78 | groups: _.invokeMap(asset.tree.children, 'toChartData') as CharData
79 | };
80 | });
81 | }
82 |
83 | interface Chunk {
84 | tree?: any;
85 | }
86 |
87 | async function getSplitLibData(outputBundle: OutputBundle): Promise {
88 | const modules: {
89 | library: Chunk;
90 | project: Chunk;
91 | } = {
92 | library: {},
93 | project: {}
94 | };
95 | const libModules: TransformedModule[] = [];
96 | const projectModules: TransformedModule[] = [];
97 | for (const [id, module] of Object.entries(outputBundle)) {
98 | if (module.type === 'chunk') {
99 | // eslint-disable-next-line
100 | await transformModule(module, id, (m) => {
101 | if (m.name === id) {
102 | return;
103 | }
104 | if (m.name.indexOf('node_modules') > -1 || m.name.indexOf('vite') > -1) {
105 | libModules.push(m);
106 | } else {
107 | projectModules.push(m);
108 | }
109 | });
110 | }
111 | }
112 | modules.library.tree = createModulesTree(libModules);
113 | modules.project.tree = createModulesTree(projectModules);
114 |
115 | return Object.entries(modules).map(([name, asset]) => {
116 | return {
117 | label: name,
118 | isAsset: true,
119 | statSize: asset.tree.size,
120 | parsedSize: asset.tree.renderSize,
121 | gzipSize: asset.tree.gzipSize,
122 | groups: _.invokeMap(asset.tree.children, 'toChartData') as CharData
123 | };
124 | });
125 | }
126 |
127 | export default async function getMixCharData(outputBundle: OutputBundle): Promise {
128 | return {
129 | lib: await getSplitLibData(outputBundle),
130 | default: await getModuleData(outputBundle)
131 | };
132 | }
133 |
134 | export type GetModuleData = typeof getModuleData;
135 |
--------------------------------------------------------------------------------
/src/pureBundle.ts:
--------------------------------------------------------------------------------
1 | import { writeFileSync } from 'fs';
2 | import path from 'path';
3 | import { OutputBundle, OutputOptions } from 'rollup';
4 |
5 | /**
6 | * 删除多余的key值,例如code, data这类比较长的内容,可以更方便观测生成的包数据结构
7 | * @param target 目标对象
8 | * @param delKey 要删除的key值
9 | */
10 | // eslint-disable-next-line @typescript-eslint/no-unused-vars
11 | // @ts-ignore
12 | function deleteKey(target: any, delKey: Array): void {
13 | if (!target) {
14 | return;
15 | }
16 | for (const [key, value] of Object.entries(target)) {
17 | if ((typeof value === 'string' || Array.isArray(value)) && delKey.includes(key)) {
18 | const limit = Array.isArray(value) ? 5 : 300;
19 | if (value.length > 10) {
20 | target[key] = value.slice(0, limit);
21 | }
22 | }
23 | if (typeof value === 'object' && !Array.isArray(value)) {
24 | deleteKey(value, delKey);
25 | }
26 | }
27 | }
28 |
29 | export default function pureBundle(outputOptions: OutputOptions, outputBundle: OutputBundle) {
30 | const pureBundleInfo = JSON.parse(JSON.stringify(outputBundle));
31 | deleteKey(pureBundleInfo, ['code', 'data', 'source']);
32 | writeFileSync(path.join(process.cwd(), 'outputOpt.json'), JSON.stringify(outputOptions));
33 | writeFileSync(path.join(process.cwd(), 'bundle.json'), JSON.stringify(pureBundleInfo));
34 | }
35 |
--------------------------------------------------------------------------------
/src/size.ts:
--------------------------------------------------------------------------------
1 | export const createBrotliSizeGetter = () => {
2 | return (code: string) => {
3 | // eslint-disable-next-line no-console
4 | const data = Buffer.from(code || '', 'utf-8');
5 | return data.length;
6 | };
7 | };
8 |
9 | export const sizeGetter = (code: string) => {
10 | const data = Buffer.from(code, 'utf-8');
11 | return data.length;
12 | };
13 |
--------------------------------------------------------------------------------
/src/template.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import fs from 'fs';
3 | import url from 'url';
4 |
5 | import _ from 'lodash';
6 |
7 | import { MixCharData } from '../types';
8 | // @ts-ignore
9 | const fileName = url.fileURLToPath(import.meta.url);
10 | const __dirname = path.dirname(fileName);
11 | const projectRoot = path.resolve(__dirname, '..', '..');
12 | const assetsRoot = path.join(projectRoot, 'public');
13 |
14 | function escapeJson(json?: Object) {
15 | return JSON.stringify(json).replace(/ `${string}${values[index] || ''}`).join('');
30 | }
31 |
32 | function getScript(filename: string, mode?: string) {
33 | if (mode === 'static') {
34 | return ` `;
35 | }
36 | return ``;
37 | }
38 |
39 | export default function renderViewer({
40 | title,
41 | enableWebSocket,
42 | chartData,
43 | defaultSizes,
44 | mode
45 | }: {
46 | title?: string;
47 | enableWebSocket?: Object;
48 | chartData?: MixCharData;
49 | defaultSizes?: string;
50 | mode?: string;
51 | } = {}) {
52 | return html`
53 |
54 |
55 |
56 |
57 | ${_.escape(title)}
58 |
61 | ${getScript('viewer.js', mode)}
62 |
63 |
64 |
65 |
66 |
70 |
71 | `;
72 | }
73 |
--------------------------------------------------------------------------------
/src/tree/Folder.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import gzipSize from 'gzip-size';
3 |
4 | import { TransformedModule } from '../../types';
5 |
6 | import Module, { ModuleInstance } from './Module';
7 | import Node, { NodeInstance } from './Node';
8 | import { getModulePathParts } from './utils';
9 |
10 | interface Stop {
11 | (state: T): T;
12 | }
13 |
14 | export default class Folder extends Node {
15 | public children: {
16 | [key: string]: Folder | ModuleInstance;
17 | };
18 | private _code: string | undefined;
19 | private _originCode: string | undefined;
20 | private _size: number | undefined;
21 | private _renderSize: number | undefined;
22 | private _gzipSize: number | undefined;
23 |
24 | constructor(name: string, parent?: NodeInstance) {
25 | super(name, parent);
26 | this.children = Object.create(null);
27 | }
28 |
29 | get gzipSize() {
30 | if (!_.has(this, '_gzipSize')) {
31 | this._gzipSize = this.code ? gzipSize.sync(this.code) : 0;
32 | }
33 |
34 | return this._gzipSize;
35 | }
36 |
37 | get code() {
38 | if (!_.has(this, '_code')) {
39 | this._code = this.walk((node, code) => code + node.code ?? '', '', false);
40 | }
41 |
42 | return this._code;
43 | }
44 |
45 | get originCode() {
46 | if (!_.has(this, '_originCode')) {
47 | this._originCode = this.walk(
48 | (node, originCode) => originCode + node.originCode ?? '',
49 | '',
50 | false
51 | );
52 | }
53 |
54 | return this._originCode;
55 | }
56 |
57 | get size() {
58 | if (!_.has(this, '_size')) {
59 | this._size = this.walk((node, size) => size + node.size, 0, false);
60 | }
61 |
62 | return this._size;
63 | }
64 |
65 | get renderSize() {
66 | if (!_.has(this, '_renderSize')) {
67 | this._renderSize = this.walk((node, renderSize) => renderSize + node.renderSize, 0, false);
68 | }
69 |
70 | return this._renderSize;
71 | }
72 |
73 | getChild(name: string) {
74 | return this.children[name];
75 | }
76 |
77 | addChildModule(module: ModuleInstance) {
78 | const { name } = module;
79 | const currentChild = this.children[name];
80 | if (currentChild && currentChild instanceof Folder) return;
81 | // eslint-disable-next-line no-param-reassign
82 | module.parent = this;
83 | this.children[name] = module;
84 |
85 | delete this._size;
86 | delete this._code;
87 | }
88 |
89 | addChildFolder(folder: Folder) {
90 | this.children[folder.name] = folder;
91 | delete this._size;
92 | delete this._code;
93 | return folder;
94 | }
95 |
96 | walk(
97 | walker: (child: Folder | Module, state: any, stop: Stop) => any,
98 | state: any = {},
99 | deep: boolean | Stop = true
100 | ) {
101 | let stopped = false;
102 |
103 | function stop(finalState: any) {
104 | stopped = true;
105 | return finalState;
106 | }
107 |
108 | // eslint-disable-next-line consistent-return
109 | Object.values(this.children).forEach((child) => {
110 | if (deep && (child as Folder).walk) {
111 | // eslint-disable-next-line no-param-reassign
112 | state = (child as Folder).walk(walker, state, stop);
113 | } else {
114 | // eslint-disable-next-line no-param-reassign
115 | state = walker(child, state, stop);
116 | }
117 |
118 | if (stopped) return false;
119 | });
120 | return state;
121 | }
122 |
123 | mergeNestedFolders() {
124 | if (!this.isRoot) {
125 | let childNames;
126 | // 合并只有一个文件的目录
127 | // eslint-disable-next-line no-cond-assign
128 | while ((childNames = Object.keys(this.children)).length === 1) {
129 | // eslint-disable-next-line prefer-destructuring
130 | const childName = childNames[0];
131 | const onlyChild = this.children[childName];
132 |
133 | if (onlyChild instanceof this.constructor) {
134 | this.name += `/${onlyChild.name}`;
135 | this.children = (onlyChild as Folder).children;
136 | } else {
137 | break;
138 | }
139 | }
140 | }
141 |
142 | this.walk(
143 | (child) => {
144 | // eslint-disable-next-line no-param-reassign
145 | child.parent = this;
146 |
147 | if ((child as Folder).mergeNestedFolders) {
148 | (child as Folder).mergeNestedFolders();
149 | }
150 | },
151 | null,
152 | false
153 | );
154 | }
155 |
156 | addModule(moduleData: TransformedModule) {
157 | const pathParts = getModulePathParts(moduleData);
158 |
159 | if (!pathParts) {
160 | return;
161 | }
162 |
163 | const [folders, fileName] = [pathParts.slice(0, -1), _.last(pathParts)];
164 | let currentFolder: Folder = this;
165 |
166 | folders.forEach((folderName) => {
167 | let childNode = currentFolder.getChild(folderName);
168 |
169 | if (!childNode || !(childNode instanceof Folder)) {
170 | childNode = currentFolder.addChildFolder(new Folder(folderName, this));
171 | }
172 | currentFolder = childNode;
173 | });
174 | if (fileName) {
175 | const module = new Module(fileName, moduleData, this);
176 | currentFolder.addChildModule(module);
177 | }
178 | }
179 |
180 | toChartData() {
181 | return {
182 | label: this.name,
183 | path: this.path,
184 | statSize: this.size,
185 | parsedSize: this.renderSize,
186 | groups: _.invokeMap(this.children, 'toChartData'),
187 | gzipSize: this.gzipSize
188 | };
189 | }
190 | }
191 |
192 | export type FolderInstance = InstanceType;
193 |
194 | export function createModulesTree(modules: TransformedModule['modules']) {
195 | const root = new Folder('.');
196 | modules.forEach((module) => root.addModule(module));
197 | root.mergeNestedFolders();
198 | return root;
199 | }
200 |
--------------------------------------------------------------------------------
/src/tree/Module.ts:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 | import gzipSize from 'gzip-size';
3 |
4 | import type { TransformedModule } from '../../types';
5 | import { createBrotliSizeGetter } from '../size';
6 |
7 | import Node from './Node';
8 | import Folder from './Folder';
9 |
10 | const sizeGetter = createBrotliSizeGetter();
11 |
12 | export default class Module extends Node {
13 | private readonly data: TransformedModule;
14 | private _gzipSize: number | undefined;
15 |
16 | constructor(name: string, data: TransformedModule, parent: Folder) {
17 | super(name, parent);
18 | this.data = data;
19 | }
20 |
21 | get size() {
22 | return sizeGetter(this.originCode ?? '');
23 | }
24 |
25 | get code() {
26 | return this.data.code;
27 | }
28 |
29 | get originCode() {
30 | return this.data.originCode;
31 | }
32 |
33 | get renderSize() {
34 | return sizeGetter(this.data.code ?? '');
35 | }
36 |
37 | get gzipSize() {
38 | if (!_.has(this, '_gzipSize')) {
39 | this._gzipSize = this.code ? gzipSize.sync(this.code) : undefined;
40 | }
41 |
42 | return this._gzipSize;
43 | }
44 |
45 | toChartData() {
46 | return {
47 | id: this.data.name,
48 | label: this.name,
49 | path: this.path,
50 | statSize: this.size,
51 | parsedSize: this.renderSize,
52 | gzipSize: this.gzipSize
53 | };
54 | }
55 | }
56 |
57 | export type ModuleInstance = InstanceType;
58 |
--------------------------------------------------------------------------------
/src/tree/Node.ts:
--------------------------------------------------------------------------------
1 | export default class Node {
2 | name: string;
3 | public parent: Node | undefined;
4 | constructor(name: string, parent?: Node) {
5 | this.name = name;
6 | this.parent = parent;
7 | }
8 |
9 | get path() {
10 | return this.name;
11 | }
12 |
13 | get isRoot() {
14 | return !this.parent;
15 | }
16 | }
17 |
18 | export type NodeInstance = InstanceType;
19 |
--------------------------------------------------------------------------------
/src/tree/utils.ts:
--------------------------------------------------------------------------------
1 | import { TransformedModule } from '../../types';
2 |
3 | export function getModulePathParts(moduleData: TransformedModule) {
4 | const projectRoot = process.cwd();
5 | const parsedPath = moduleData.name
6 | .replace(`${projectRoot}/`, '')
7 | .split('/')
8 | // Replacing `~` with `node_modules`
9 | .map((part) => (part === '~' ? 'node_modules' : part));
10 | return parsedPath.length ? parsedPath : null;
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { inspect, types } from 'util';
2 |
3 | import _ from 'lodash';
4 | import opener from 'opener';
5 | import logger from './logger';
6 |
7 | const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
8 |
9 | export function createAssetsFilter(excludePatterns: any) {
10 | const excludeFunctions = _(excludePatterns)
11 | .castArray()
12 | .compact()
13 | .map((pattern) => {
14 | if (typeof pattern === 'string') {
15 | // eslint-disable-next-line no-param-reassign
16 | pattern = new RegExp(pattern, 'u');
17 | }
18 |
19 | if (types.isRegExp(pattern)) {
20 | return (asset: any) => pattern.test(asset);
21 | }
22 |
23 | if (typeof pattern !== 'function') {
24 | throw new TypeError(
25 | `Pattern should be either string, RegExp or a function, but "${inspect(pattern, {
26 | depth: 0
27 | })}" got.`
28 | );
29 | }
30 |
31 | return pattern;
32 | })
33 | .value();
34 |
35 | if (excludeFunctions.length) {
36 | return (asset: any) => excludeFunctions.every((fn) => fn(asset) !== true);
37 | } else {
38 | return () => true;
39 | }
40 | }
41 |
42 | export function defaultTitle() {
43 | const time = new Date();
44 | const year = time.getFullYear();
45 | const month = MONTHS[time.getMonth()];
46 | const day = time.getDate();
47 | const hour = `0${time.getHours()}`.slice(-2);
48 | const minute = `0${time.getMinutes()}`.slice(-2);
49 |
50 | const currentTime = `${day} ${month} ${year} at ${hour}:${minute}`;
51 |
52 | return `${process.env.npm_package_name || 'Webpack Bundle Analyzer'} [${currentTime}]`;
53 | }
54 |
55 | export function defaultAnalyzerUrl(options: { listenHost: string; boundAddress: any }) {
56 | const { listenHost, boundAddress } = options;
57 | return `http://${listenHost}:${boundAddress.port}`;
58 | }
59 |
60 | export const open = function (uri: string) {
61 | try {
62 | opener(uri);
63 | } catch (err) {
64 | // @ts-ignore
65 | logger.debug(`Opener failed to open "${uri}":\n${err}`);
66 | }
67 | };
68 |
--------------------------------------------------------------------------------
/src/viewer.ts:
--------------------------------------------------------------------------------
1 | import * as path from 'path';
2 | import * as http from 'http';
3 | import * as url from 'url';
4 |
5 | import WebSocket from 'ws';
6 | import sirv from 'sirv';
7 | import pkg from 'chalk';
8 |
9 | import type { MixCharData, PluginOptions } from '../types';
10 |
11 | import { open } from './utils';
12 | import renderViewer from './template';
13 | import logger from './logger';
14 |
15 | type GetModuleDataFun = () => Promise;
16 |
17 | const { bold } = pkg;
18 | let serverInstance: Awaited>;
19 |
20 | // @ts-ignore
21 | // eslint-disable-next-line no-underscore-dangle
22 | const __filename = url.fileURLToPath(import.meta.url);
23 | const __dirname = path.dirname(__filename);
24 | const projectRoot = path.resolve(__dirname, '..', '..');
25 |
26 | function analyzerUrl(options: { listenPort?: any; listenHost: any; boundAddress: any }) {
27 | const { listenHost, boundAddress } = options;
28 | return `http://${listenHost}:${boundAddress.port}`;
29 | }
30 |
31 | async function startServer(
32 | opts: PluginOptions,
33 | moduleData: MixCharData,
34 | getModuleDataFun: GetModuleDataFun
35 | ) {
36 | const { port = 8888, host = '127.0.0.1', openBrowser = true } = opts || {};
37 |
38 | const sirvMiddleware = sirv(`${projectRoot}/public`, { dev: true });
39 |
40 | const server = http.createServer((req, res) => {
41 | if (req.method === 'GET' && req.url === '/') {
42 | const html = renderViewer({
43 | title: 'rollup-bundle-analyzer',
44 | enableWebSocket: true,
45 | chartData: moduleData,
46 | mode: 'serve',
47 | defaultSizes: 'stat'
48 | });
49 | res.writeHead(200, { 'Content-Type': 'text/html' });
50 | res.end(html);
51 | } else {
52 | sirvMiddleware(req, res);
53 | }
54 | });
55 |
56 | await new Promise((resolve) => {
57 | // @ts-ignore
58 | server.listen(port, host, () => {
59 | resolve();
60 | // eslint-disable-next-line @typescript-eslint/no-shadow
61 | const url = analyzerUrl({
62 | listenPort: port,
63 | listenHost: host,
64 | boundAddress: server.address()
65 | });
66 | // @ts-ignore
67 | logger.info(
68 | `${bold('Rollup Bundle Analyzer')} is started at ${bold(url)}\n` +
69 | `Use ${bold('Ctrl+C')} to close it`
70 | );
71 |
72 | if (openBrowser) {
73 | open(url);
74 | }
75 | });
76 | });
77 |
78 | const wss = new WebSocket.Server({ server });
79 |
80 | wss.on('connection', (ws) => {
81 | ws.on('error', (err) => {
82 | // Ignore network errors like `ECONNRESET`, `EPIPE`, etc.
83 | // @ts-ignore
84 | if (err.errno) return;
85 | // @ts-ignore
86 | logger.info(err.message);
87 | });
88 | });
89 |
90 | // eslint-disable-next-line consistent-return
91 | return {
92 | ws: wss,
93 | http: server,
94 | updateChartData
95 | };
96 |
97 | async function updateChartData() {
98 | const newChartData = await getModuleDataFun();
99 |
100 | if (!newChartData) return;
101 |
102 | wss.clients.forEach((client) => {
103 | if (client.readyState === WebSocket.OPEN) {
104 | client.send(
105 | JSON.stringify({
106 | event: 'chartDataUpdated',
107 | data: newChartData
108 | })
109 | );
110 | }
111 | });
112 | }
113 | }
114 |
115 | export default async function startViewServe(
116 | getModuleDataFun: GetModuleDataFun,
117 | pluginOpt: PluginOptions
118 | ) {
119 | const moduleData = await getModuleDataFun();
120 | if (serverInstance) {
121 | (await serverInstance).updateChartData();
122 | } else {
123 | serverInstance = await startServer(
124 | {
125 | openBrowser: pluginOpt.openBrowser,
126 | host: pluginOpt.host!,
127 | port: pluginOpt.port!,
128 | bundleDir: ''
129 | },
130 | moduleData,
131 | getModuleDataFun
132 | );
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowSyntheticDefaultImports": true,
4 | "esModuleInterop": true,
5 | "lib": ["es6"],
6 | "module": "commonjs",
7 | "moduleResolution": "node",
8 | "noEmit": true,
9 | "noEmitOnError": false,
10 | "noUnusedLocals": true,
11 | "noUnusedParameters": true,
12 | "pretty": true,
13 | "sourceMap": true,
14 | "strict": true,
15 | "target": "es2019"
16 | },
17 | "exclude": ["dist", "node_modules", "test/types"]
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.base.json"
3 | }
4 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import { OutputAsset, OutputChunk, RenderedModule, Plugin } from 'rollup';
2 |
3 | import Logger from '../src/logger';
4 |
5 | export type CustomHandle = (chartData: MixCharData, html: string, advise: Advise) => void;
6 |
7 | export interface PluginOptions {
8 | // 需要排除的文件
9 | excludeAssets?: string[];
10 | // 日志函数
11 | logger?: typeof Logger;
12 | // 生成文件所在目录
13 | bundleDir?: string;
14 | // host默认127.0.0.1
15 | host?: string;
16 | // 端口 默认9800
17 | port?: number;
18 | // server模式下是否自动打开浏览器,默认是true
19 | openBrowser?: boolean;
20 | // 三种模式,生成JSON, 打开浏览器,生成静态的html文件
21 | analyzerMode?: 'server' | 'json' | 'static' | 'custom';
22 | // 生成的静态html文件名称
23 | reportFilename?: string;
24 | // 生成的包体积json文件的名字
25 | statsFilename?: string;
26 | logLevel?: 'info';
27 | // 生成包体积之后的回调函数,server模式不提供
28 | customHandle?: CustomHandle;
29 | }
30 |
31 | export type Module = OutputAsset | OutputChunk | RenderedModule;
32 |
33 | export type TransformedModule = Omit & {
34 | modules: Array;
35 | tree?: any;
36 | name: string;
37 | size?: number;
38 | gzipSize?: number;
39 | parsedSize?: number;
40 | _code: string;
41 | originCode?: string;
42 | };
43 |
44 | export type Level = 'debug' | 'info' | 'warn' | 'error' | 'silent';
45 |
46 | export interface ModuleItem {
47 | isAsset: boolean;
48 | label: string;
49 | // path?: string;
50 | statSize: number;
51 | gzipSize?: number;
52 | parsedSize?: number;
53 | groups?: ModuleItem[];
54 | }
55 |
56 | export type CharData = Array;
57 |
58 | export interface MixCharData {
59 | lib: CharData;
60 | default: CharData;
61 | }
62 |
63 | export interface NoTreeSharking {
64 | size: number;
65 | }
66 |
67 | export interface Polyfill {
68 | // 体积
69 | size: number;
70 | // 后面补充网址介绍?或者
71 | url?: string;
72 | }
73 |
74 | export interface Rewrite {
75 | size: number;
76 | desc: string;
77 | }
78 |
79 | export interface ReplaceModule {
80 | target: string;
81 | }
82 |
83 | export interface NormalModule {}
84 |
85 | export interface Advise {
86 | treeSharking: Record;
87 | replace: Record;
88 | commonjs: Record;
89 | polyfill: Record;
90 | rewrite: Record;
91 | }
92 |
93 | export default function bundleAnalyzer(opt: PluginOptions): Plugin;
94 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const compact = require('lodash/compact');
2 | const webpack = require('webpack');
3 | const TerserPlugin = require('terser-webpack-plugin');
4 | // const BundleAnalyzePlugin = require('./lib/BundleAnalyzerPlugin');
5 |
6 | module.exports = (opts) => {
7 | opts = Object.assign(
8 | {
9 | env: 'dev',
10 | analyze: false
11 | },
12 | opts
13 | );
14 |
15 | const isDev = opts.env === 'dev';
16 |
17 | return {
18 | mode: isDev ? 'development' : 'production',
19 | context: __dirname,
20 | entry: './client/viewer',
21 | output: {
22 | path: `${__dirname}/public`,
23 | filename: 'viewer.js',
24 | publicPath: '/'
25 | },
26 |
27 | resolve: {
28 | extensions: ['.js', '.jsx'],
29 | alias: {
30 | react: 'preact/compat',
31 | 'react-dom/test-utils': 'preact/test-utils',
32 | 'react-dom': 'preact/compat',
33 | mobx: require.resolve('mobx/lib/mobx.es6.js')
34 | }
35 | },
36 |
37 | devtool: isDev ? 'eval' : 'source-map',
38 | watch: isDev,
39 |
40 | performance: {
41 | hints: false
42 | },
43 | optimization: {
44 | minimize: !isDev,
45 | minimizer: [
46 | new TerserPlugin({
47 | parallel: true,
48 | terserOptions: {
49 | output: {
50 | comments: /copyright/iu
51 | },
52 | safari10: true
53 | }
54 | })
55 | ]
56 | },
57 |
58 | module: {
59 | rules: [
60 | {
61 | test: /\.jsx?$/u,
62 | exclude: /node_modules/u,
63 | loader: 'babel-loader',
64 | options: {
65 | babelrc: false,
66 | presets: [
67 | [
68 | '@babel/preset-env',
69 | {
70 | // Target browsers are specified in .browserslistrc
71 |
72 | modules: false,
73 | useBuiltIns: 'usage',
74 | corejs: require('./package.json').devDependencies['core-js'],
75 | debug: true
76 | }
77 | ],
78 | [
79 | '@babel/preset-react',
80 | {
81 | runtime: 'automatic',
82 | importSource: 'preact'
83 | }
84 | ]
85 | ],
86 | plugins: [
87 | 'lodash',
88 | ['@babel/plugin-proposal-decorators', { legacy: true }],
89 | ['@babel/plugin-proposal-class-properties', { loose: true }],
90 | ['@babel/plugin-proposal-private-methods', { loose: true }],
91 | [
92 | '@babel/plugin-transform-runtime',
93 | {
94 | useESModules: true
95 | }
96 | ]
97 | ]
98 | }
99 | },
100 | {
101 | test: /\.css$/u,
102 | use: [
103 | 'style-loader',
104 | {
105 | loader: 'css-loader',
106 | options: {
107 | modules: {
108 | localIdentName: '[name]__[local]'
109 | },
110 | importLoaders: 1
111 | }
112 | },
113 | {
114 | loader: 'postcss-loader',
115 | options: {
116 | postcssOptions: {
117 | plugins: compact([
118 | require('postcss-icss-values'),
119 | require('autoprefixer'),
120 | !isDev && require('cssnano')()
121 | ])
122 | }
123 | }
124 | }
125 | ]
126 | },
127 | {
128 | test: /\.(png|jpg|gif|svg|eot|ttf|woff|woff2)$/u,
129 | loader: 'url-loader'
130 | }
131 | ]
132 | },
133 |
134 | plugins: ((plugins) => {
135 | if (!isDev) {
136 | plugins.push(
137 | new webpack.DefinePlugin({
138 | process: JSON.stringify({
139 | env: {
140 | NODE_ENV: 'production'
141 | }
142 | }),
143 | // Fixes "ModuleConcatenation bailout" for some modules (e.g. Preact and MobX)
144 | global: 'undefined'
145 | })
146 | );
147 | }
148 |
149 | return plugins;
150 | })([])
151 | };
152 | };
153 |
--------------------------------------------------------------------------------