Launch Week Day 5: Introducing Reachability for PHP.Learn More
Socket
Book a DemoSign in
Socket

rollup-plugin-visualizer

Package Overview
Dependencies
Maintainers
1
Versions
117
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rollup-plugin-visualizer - npm Package Compare versions

Comparing version
4.2.2
to
5.0.2
+93
dist/package.json
{
"name": "rollup-plugin-visualizer",
"version": "5.0.1",
"main": "./dist/plugin/index.js",
"author": "Denis Bardadym <bardadymchik@gmail.com>",
"license": "MIT",
"bin": "./dist/bin/cli.js",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "git@github.com:btd/rollup-plugin-visualizer.git"
},
"homepage": "https://github.com/btd/rollup-plugin-visualizer",
"bugs": {
"url": "https://github.com/btd/rollup-plugin-visualizer/issues"
},
"scripts": {
"lint": "eslint 'plugin/**/*.ts' 'src/**/*.{ts,tsx}'",
"build": "run-p build:*",
"build:plugin": "tsc",
"build:frontend": "node build.js",
"clean": "del-cli dist",
"test": "run-s test:*",
"test:e2e": "node build.js --all --e2e",
"test:test": "node build.js --all --test",
"test:e2e-json": "node build.js --all --e2e --json",
"test:test-json": "node build.js --all --test --json",
"test:e2e-json-sourcemap": "node build.js --all --e2e --json --sourcemap",
"test:test-json-sourcemap": "node build.js --all --test --json --sourcemap",
"test:cli": "node dist/bin/cli.js stats.e2e.json"
},
"dependencies": {
"nanoid": "^3.1.22",
"open": "^7.4.2",
"source-map": "^0.7.3",
"yargs": "^16.2.0"
},
"peerDependencies": {
"rollup": "^2.0.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-typescript": "^8.2.1",
"@types/bytes": "^3.1.0",
"@types/d3-array": "^2.9.0",
"@types/d3-color": "^2.0.1",
"@types/d3-hierarchy": "^2.0.0",
"@types/d3-scale": "^3.2.2",
"@types/d3-shape": "^2.0.0",
"@types/nanoid": "^2.1.0",
"@types/yargs": "^16.0.1",
"@typescript-eslint/eslint-plugin": "^4.20.0",
"@typescript-eslint/parser": "^4.20.0",
"bytes": "^3.1.0",
"d3-array": "^2.12.1",
"d3-color": "^2.0.0",
"d3-hierarchy": "^2.0.0",
"d3-scale": "^3.2.4",
"d3-shape": "^2.1.0",
"del-cli": "^3.0.1",
"eslint": "^7.23.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.23.1",
"eslint-plugin-react-hooks": "^4.2.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.2.8",
"postcss-url": "^10.1.3",
"preact": "^10.5.13",
"prettier": "^2.2.1",
"rollup": "^2.44.0",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.32.8",
"typescript": "^4.2.3",
"webcola": "^3.4.0"
},
"engines": {
"node": ">=10.16"
},
"keywords": [
"rollup-plugin",
"visualizer",
"network",
"treemap",
"sunburst",
"diagram"
]
}
+8
-0
# Changelog
## 5.0.0
* **Breaking change** Remove support node < 10.16
* **Breaking change** Remove support rollup 1.x (actually some of 1.x will still work, but i officially stop checking)
* **Breaking change** Remove support for `gzipLength` and `brotliLength` for rollup < 2.44. Since 2.44 rollup report renderered module code, which is used to get correct sizes.
* **Breaking change** Change default export
* Migrate plugin to TS, types now included
## 4.2.2

@@ -4,0 +12,0 @@

+52
-21
{
"name": "rollup-plugin-visualizer",
"version": "4.2.2",
"main": "plugin/index.js",
"version": "5.0.2",
"main": "./dist/plugin/index.js",
"author": "Denis Bardadym <bardadymchik@gmail.com>",
"license": "MIT",
"bin": "./bin/cli.js",
"bin": "./dist/bin/cli.js",
"files": [
"bin/*",
"lib/*",
"plugin/*"
"dist"
],

@@ -22,7 +20,15 @@ "repository": {

"scripts": {
"lint": "eslint .",
"build": "node build.js",
"clean": "del-cli lib",
"prepare": "npm run build",
"test": "node build.js --all --e2e && node build.js --all --test"
"lint": "eslint 'plugin/**/*.ts' 'src/**/*.{ts,tsx}'",
"build": "run-p build:*",
"build:plugin": "tsc",
"build:frontend": "node build.js",
"clean": "del-cli dist",
"test": "run-s test:*",
"test:e2e": "node build.js --all --e2e",
"test:test": "node build.js --all --test",
"test:e2e-json": "node build.js --all --e2e --json",
"test:test-json": "node build.js --all --test --json",
"test:e2e-json-sourcemap": "node build.js --all --e2e --json --sourcemap",
"test:test-json-sourcemap": "node build.js --all --test --json --sourcemap",
"test:cli": "node dist/bin/cli.js stats.e2e.json"
},

@@ -36,16 +42,32 @@ "dependencies": {

"peerDependencies": {
"rollup": ">=1.20.0"
"rollup": "^2.0.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^17.1.0",
"@rollup/plugin-node-resolve": "^11.2.0",
"@rollup/plugin-commonjs": "^18.0.0",
"@rollup/plugin-node-resolve": "^11.2.1",
"@rollup/plugin-typescript": "^8.2.1",
"@types/bytes": "^3.1.0",
"@types/d3-array": "^2.9.0",
"@types/d3-color": "^2.0.1",
"@types/d3-hierarchy": "^2.0.0",
"@types/d3-scale": "^3.2.2",
"@types/d3-shape": "^2.0.0",
"@types/nanoid": "^2.1.0",
"@types/yargs": "^16.0.1",
"@typescript-eslint/eslint-plugin": "^4.20.0",
"@typescript-eslint/parser": "^4.20.0",
"bytes": "^3.1.0",
"d3-array": "^2.12.0",
"d3-array": "^2.12.1",
"d3-color": "^2.0.0",
"d3-hierarchy": "^2.0.0",
"d3-scale": "^3.2.3",
"d3-scale": "^3.2.4",
"d3-shape": "^2.1.0",
"del-cli": "^3.0.1",
"eslint": "^7.22.0",
"htm": "^3.0.4",
"eslint": "^7.23.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.23.1",
"eslint-plugin-react-hooks": "^4.2.0",
"npm-run-all": "^4.1.5",
"postcss": "^8.2.8",

@@ -55,11 +77,20 @@ "postcss-url": "^10.1.3",

"prettier": "^2.2.1",
"rollup": "^2.42.3",
"rollup": "^2.44.0",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
"sass": "^1.32.8",
"typescript": "^4.2.3",
"webcola": "^3.4.0"
},
"engines": {
"node": ">=10"
}
"node": ">=10.16"
},
"keywords": [
"rollup-plugin",
"visualizer",
"network",
"treemap",
"sunburst",
"diagram"
]
}
# Rollup Plugin Visualizer
[![NPM Version](https://img.shields.io/npm/v/rollup-plugin-visualizer.svg)](https://npmjs.org/package/rollup-plugin-visualizer) [![Travis CI build status](https://img.shields.io/travis/com/btd/rollup-plugin-visualizer.svg)](https://travis-ci.com/btd/rollup-plugin-visualizer)
[![NPM Version](https://img.shields.io/npm/v/rollup-plugin-visualizer.svg)](https://npmjs.org/package/rollup-plugin-visualizer) [![Node.js CI](https://github.com/btd/rollup-plugin-visualizer/actions/workflows/node.js.yml/badge.svg)](https://github.com/btd/rollup-plugin-visualizer/actions/workflows/node.js.yml)

@@ -23,4 +23,16 @@ Visualize and analyze your Rollup bundle to see which modules are taking up space.

## V5 Upgrade
Version V5 contains seveal minor breaking changes, depending your current installation takes such steps for upgrade:
* If you are using rollup v1.x, then you'd better to stay on v4 of plugin. I officially stopped support v1 of rolloup myself. Some versions of 1.x will work without issues, but i stop testing myself or add any changes in this direction. If somebody wants to contibute in this direction - welcome.
* If you use rollup v2.x and use `gzipLength` or `brotliLength`upgrade to rollup 2.44 at least. In V5 i use provided by rollup api to get rendered module code for size estimations, instead of original sources as it was before.
* In all other case just update the plugin.
To upgrade plugin change import/require statement like it is shown in installation section.
## Usage
Es imports:
```javascript

@@ -36,2 +48,14 @@ import visualizer from 'rollup-plugin-visualizer';

Cjs require:
```javascript
const { visulizer } = require('rollup-plugin-visualizer');
//...
plugins: [
visualizer()
],
//...
```
## Options

@@ -47,9 +71,9 @@

`template` (string, default `treemap`) - Which digram type to use: `sunburst`, `treemap`, `network` (very early stage, feedback welcomed)
`template` (string, default `treemap`) - Which diagram type to use: `sunburst`, `treemap`, `network`.
`json` (boolean, default `false`) - Product portable json file that can be used with plugin CLI util to generate graph from several json files. Every UI property ignored in this case.
`json` (boolean, default `false`) - Produce portable json file that can be used with plugin CLI util to generate graph from several json files. Every UI property ignored in this case.
`gzipSize` (boolean, default `false`) - Collect gzip size from source code and display it at chart
`gzipSize` (boolean, default `false`) - Collect gzip size from source code and display it at chart.
`brotliSize` (boolean, default `false`) - Collect brolti size from source code and display it at chart. Only if current node version supports it
`brotliSize` (boolean, default `false`) - Collect brotli size from source code and display it at chart.

@@ -71,3 +95,3 @@ ## CLI

```js
yarn run build
npm run build
```

@@ -83,3 +107,3 @@

- size of files included in source map
- file's path
- file's paths
- files hierarchy (fs tree for your files)

@@ -86,0 +110,0 @@

#!/usr/bin/env node
"use strict";
const fs = require("fs").promises;
const path = require("path");
const buildStats = require("../plugin/build-stats");
const TEMPLATE = require("../plugin/template-types");
const warn = require("../plugin/warn");
const JSON_VERSION = require("../plugin/version");
const argv = require("yargs")
.strict()
.option("filename", {
describe: "Output file name",
string: true,
default: "./stats.html",
})
.option("title", {
describe: "Output file title",
string: true,
default: "RollUp Visualizer",
})
.option("template", {
describe: "Template type",
string: true,
choices: TEMPLATE,
default: "treemap",
})
.option("sourcemap", {
describe: "Provided files is sourcemaps",
boolean: true,
})
.help().argv;
const listOfFiles = argv._;
const runForPluginJson = async ({ title, template, filename }, files) => {
if (files.length === 0) {
throw new Error("Empty file list");
}
const fileContents = await Promise.all(
files.map(async (file) => {
const textContent = await fs.readFile(file, { encoding: "utf-8" });
const jsonContent = JSON.parse(textContent);
return [file, jsonContent];
})
);
const tree = {
name: "root",
children: [],
};
const nodes = Object.create(null);
let links = [];
for (const [file, fileContent] of fileContents) {
if (fileContent.version !== JSON_VERSION) {
warn(
`Version in ${file} is not supported (${fileContent.version}). Current version ${JSON_VERSION}. Skipping...`
);
continue;
}
if (fileContent.tree.name === "root") {
tree.children = tree.children.concat(fileContent.tree.children);
} else {
tree.children.push(fileContent.tree);
}
Object.assign(nodes, fileContent.nodes);
links = links.concat(fileContent.links);
}
const data = {
version: JSON_VERSION,
tree,
links,
nodes,
env: fileContents[0].env,
options: fileContents[0].options,
};
const fileContent = await buildStats({
title,
data,
template,
});
await fs.mkdir(path.dirname(filename), { recursive: true });
await fs.writeFile(filename, fileContent);
};
runForPluginJson(argv, listOfFiles).catch((err) => {
warn(err.message);
process.exit(1);
});
:root {
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--background-color: #f7eedf;
--text-color: #333;
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #2b2d42;
--text-color: #edf2f4;
}
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
html {
background-color: var(--background-color);
color: var(--text-color);
font-family: var(--font-family);
}
body {
padding: 0;
margin: 0;
}
html,
body {
height: 100%;
width: 100%;
overflow: hidden;
}
body {
display: flex;
flex-direction: column;
}
svg {
vertical-align: middle;
width: 100%;
height: 100%;
max-height: 100vh;
}
main {
flex-grow: 1;
height: 100vh;
padding: 20px;
}
.tooltip {
position: absolute;
z-index: 1070;
border: 2px solid;
border-radius: 5px;
padding: 5px;
white-space: nowrap;
font-size: 0.875rem;
background-color: var(--background-color);
color: var(--text-color);
}
.tooltip-hidden {
visibility: hidden;
opacity: 0;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
flex-direction: row;
font-size: 0.7rem;
align-items: center;
margin: 0 50px;
height: 20px;
}
.size-selectors {
display: flex;
flex-direction: row;
align-items: center;
}
.size-selector {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 1rem;
}
.size-selector input {
margin: 0 0.3rem 0 0;
}
.filters {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
}
.filter {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
}
.filter input {
flex: 1;
height: 1rem;
padding: 0.1rem;
font-size: 0.7rem;
margin-left: 0.3rem;
}
.filter + .filter {
margin-left: 0.5rem;
}

Sorry, the diff of this file is too big to display

:root {
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--background-color: #f7eedf;
--text-color: #333;
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #2b2d42;
--text-color: #edf2f4;
}
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
html {
background-color: var(--background-color);
color: var(--text-color);
font-family: var(--font-family);
}
body {
padding: 0;
margin: 0;
}
html,
body {
height: 100%;
width: 100%;
overflow: hidden;
}
body {
display: flex;
flex-direction: column;
}
svg {
vertical-align: middle;
width: 100%;
height: 100%;
max-height: 100vh;
}
main {
flex-grow: 1;
height: 100vh;
padding: 20px;
}
.tooltip {
position: absolute;
z-index: 1070;
border: 2px solid;
border-radius: 5px;
padding: 5px;
white-space: nowrap;
font-size: 0.875rem;
background-color: var(--background-color);
color: var(--text-color);
}
.tooltip-hidden {
visibility: hidden;
opacity: 0;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
flex-direction: row;
font-size: 0.7rem;
align-items: center;
margin: 0 50px;
height: 20px;
}
.size-selectors {
display: flex;
flex-direction: row;
align-items: center;
}
.size-selector {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 1rem;
}
.size-selector input {
margin: 0 0.3rem 0 0;
}
.filters {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
}
.filter {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
}
.filter input {
flex: 1;
height: 1rem;
padding: 0.1rem;
font-size: 0.7rem;
margin-left: 0.3rem;
}
.filter + .filter {
margin-left: 0.5rem;
}
.details {
position: absolute;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
top: calc(50% - 85px);
left: calc(50% - 85px);
width: 170px;
height: 170px;
font-size: 14px;
text-align: center;
color: var(--font-color);
z-index: 100;
overflow: hidden;
text-overflow: ellipsis;
}
.details-size {
font-size: 0.8em;
}
.details-name {
font-weight: bold;
}
.details-percentage {
margin: 0.4em 0em;
font-size: 2.4em;
line-height: 1em;
}

Sorry, the diff of this file is too big to display

:root {
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--background-color: #f7eedf;
--text-color: #333;
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #2b2d42;
--text-color: #edf2f4;
}
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
html {
background-color: var(--background-color);
color: var(--text-color);
font-family: var(--font-family);
}
body {
padding: 0;
margin: 0;
}
html,
body {
height: 100%;
width: 100%;
overflow: hidden;
}
body {
display: flex;
flex-direction: column;
}
svg {
vertical-align: middle;
width: 100%;
height: 100%;
max-height: 100vh;
}
main {
flex-grow: 1;
height: 100vh;
padding: 20px;
}
.tooltip {
position: absolute;
z-index: 1070;
border: 2px solid;
border-radius: 5px;
padding: 5px;
white-space: nowrap;
font-size: 0.875rem;
background-color: var(--background-color);
color: var(--text-color);
}
.tooltip-hidden {
visibility: hidden;
opacity: 0;
}
.sidebar {
position: fixed;
top: 0;
left: 0;
right: 0;
display: flex;
flex-direction: row;
font-size: 0.7rem;
align-items: center;
margin: 0 50px;
height: 20px;
}
.size-selectors {
display: flex;
flex-direction: row;
align-items: center;
}
.size-selector {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 1rem;
}
.size-selector input {
margin: 0 0.3rem 0 0;
}
.filters {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
}
.filter {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
}
.filter input {
flex: 1;
height: 1rem;
padding: 0.1rem;
font-size: 0.7rem;
margin-left: 0.3rem;
}
.filter + .filter {
margin-left: 0.5rem;
}

Sorry, the diff of this file is too big to display

"use strict";
const fs = require("fs").promises;
const path = require("path");
const htmlEscape = (string) =>
string
.replace(/&/g, "&amp;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
const buildHtmlTemplate = ({ title, script, nodesData, style }) =>
`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>${htmlEscape(title)}</title>
<style>
${style}
</style>
</head>
<body>
<main></main>
<script>
/*<!--*/
${script}
/*-->*/
</script>
<script>
/*<!--*/
const data = ${nodesData};
const run = () => {
const width = window.innerWidth;
const height = window.innerHeight;
const chartNode = document.querySelector("main");
drawChart(chartNode, data, width, height);
};
window.addEventListener('resize', run);
document.addEventListener('DOMContentLoaded', run);
/*-->*/
</script>
</body>
</html>
`;
module.exports = async function buildHtml({ title, data, template }) {
const [script, style] = await Promise.all([
fs.readFile(path.join(__dirname, "..", "lib", `${template}.js`), "utf8"),
fs.readFile(path.join(__dirname, "..", "lib", `${template}.css`), "utf8"),
]);
return buildHtmlTemplate({
title,
style,
script,
nodesData: JSON.stringify(data),
});
};
"use strict";
const zlib = require("zlib");
const { promisify } = require("util");
const gzip = promisify(zlib.gzip);
const brotliCompress = promisify(zlib.brotliCompress || (() => {}));
const gzipOptions = (options) => ({ level: 9, ...options });
const brotliOptions = zlib.brotliCompress
? (options, buffer) => ({
params: {
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT,
[zlib.constants.BROTLI_PARAM_QUALITY]:
zlib.constants.BROTLI_MAX_QUALITY,
[zlib.constants.BROTLI_PARAM_SIZE_HINT]: buffer.length,
},
...options,
})
: () => ({});
const createGzipCompressor = (options) => (buffer) =>
gzip(buffer, gzipOptions(options || {}));
const createGzipSizeGetter = (options) => {
const compress = createGzipCompressor(options);
return async (code) => {
const data = await compress(Buffer.from(code, "utf-8"));
return data.length;
};
};
const createBrotliCompressor = (options) => (buffer) =>
brotliCompress(buffer, brotliOptions(options || {}, buffer));
const createBrotliSizeGetter = zlib.brotliCompress
? (options) => {
const compress = createBrotliCompressor(options);
return async (code) => {
const data = await compress(Buffer.from(code, "utf-8"));
return data.length;
};
}
: () => () => Promise.resolve(undefined);
module.exports = { createGzipSizeGetter, createBrotliSizeGetter };
"use strict";
const path = require("path");
const PLUGIN_PREFIX = "\u0000";
const buildTree = (modules, mapper) => {
let tree = {
name: "root",
children: [],
};
for (const [id, { renderedLength }] of modules) {
const mod = { renderedLength };
const name = id;
const uid = mapper.setValueByModuleId(id, mod);
const nodeData = { uid };
if (name.startsWith(PLUGIN_PREFIX)) {
addToPath(tree, [name], nodeData);
} else {
addToPath(tree, name.split(path.sep), nodeData);
}
}
tree = flattenTree(tree);
return tree;
};
// if root children have only on child we can flatten this
const flattenTree = (root) => {
let newRoot = root;
const pluginChildren = [];
const otherChildren = [];
for (const child of root.children || []) {
if (child.name.startsWith(PLUGIN_PREFIX)) {
pluginChildren.push(child);
} else {
otherChildren.push(child);
}
}
if (otherChildren.length === 1 && otherChildren[0].children) {
newRoot = otherChildren[0];
}
while (
newRoot.children &&
newRoot.children.length === 1 &&
newRoot.children[0].children
) {
newRoot = newRoot.children[0];
}
newRoot.children = newRoot.children.concat(pluginChildren);
return newRoot;
};
// ugly but works for now
function addToPath(tree, p, value) {
if (p[0] === "") {
p.shift();
}
let child = tree.children.find((c) => c.name === p[0]);
if (child == null) {
child = {
name: p[0],
children: [],
};
tree.children.push(child);
}
if (p.length === 1) {
Object.assign(child, value);
delete child.children;
return;
}
p.shift();
addToPath(child, p, value);
}
const mergeTrees = (trees) => {
if (trees.length === 1) {
return trees[0];
}
const newTree = {
name: "root",
children: trees,
};
return newTree;
};
const addLinks = (startModuleId, getModuleInfo, links, mapper) => {
const processedNodes = {};
const moduleIds = [startModuleId];
while (moduleIds.length > 0) {
const moduleId = moduleIds.shift();
const moduleUid = mapper.getUid(moduleId);
if (processedNodes[moduleUid]) {
continue;
} else {
processedNodes[moduleUid] = true;
}
const mod = mapper.getValue(moduleUid, { renderedLength: 0 });
const info = getModuleInfo(moduleId);
const {
importedIds,
isEntry,
isExternal,
dynamicallyImportedIds = [],
} = info;
if (isEntry) {
mod.isEntry = true;
}
if (isExternal) {
mod.isExternal = true;
}
for (const importedId of importedIds) {
const importedUid = mapper.getUid(importedId);
links.push({ source: moduleUid, target: importedUid });
moduleIds.push(importedId);
}
for (const importedId of dynamicallyImportedIds) {
const importedUid = mapper.getUid(importedId);
links.push({ source: moduleUid, target: importedUid, dynamic: true });
moduleIds.push(importedId);
}
}
};
const skipModule = (id, node) =>
id.startsWith(PLUGIN_PREFIX) || node.isExternal || !path.isAbsolute(id);
const removeCommonPrefix = (nodes, nodeIds) => {
let commonPrefix = null;
for (const [id, uid] of Object.entries(nodeIds)) {
const node = nodes[uid];
if (!skipModule(id, node)) {
if (commonPrefix == null) {
commonPrefix = id;
}
for (let i = 0; i < commonPrefix.length && i < id.length; i++) {
if (commonPrefix[i] !== id[i]) {
commonPrefix = commonPrefix.slice(0, i);
break;
}
}
}
}
const commonPrefixLength = commonPrefix.length;
for (const [id, uid] of Object.entries(nodeIds)) {
const node = nodes[uid];
if (!skipModule(id, node)) {
const newId = id.slice(commonPrefixLength);
const value = nodeIds[id];
delete nodeIds[id];
nodeIds[newId] = value;
}
}
};
module.exports = { buildTree, mergeTrees, addLinks, removeCommonPrefix };
"use strict";
const fs = require("fs").promises;
const path = require("path");
const opn = require("open");
const TEMPLATE = require("./template-types");
const ModuleMapper = require("./module-mapper");
const buildStats = require("./build-stats");
const JSON_VERSION = require("./version");
const {
buildTree,
mergeTrees,
addLinks,
removeCommonPrefix,
} = require("./data");
const getSourcemapModules = require("./sourcemap");
const { createGzipSizeGetter, createBrotliSizeGetter } = require("./compress");
const pkg = require("../package.json");
const WARN_SOURCEMAP_DISABLED =
"rollup output configuration missing sourcemap = true. You should add output.sourcemap = true or disable sourcemap in this plugin";
const WARN_SOURCEMAP_MISSING = (id) => `${id} missing source map`;
const isAsset = (bundle) =>
"type" in bundle ? bundle.type === "asset" : bundle.isAsset;
module.exports = function (opts) {
opts = opts || {};
const json = !!opts.json;
const filename = opts.filename || (json ? "stats.json" : "stats.html");
const title = opts.title || "RollUp Visualizer";
const open = !!opts.open;
const openOptions = opts.openOptions || {};
const template = opts.template || "treemap";
if (!TEMPLATE.includes(template)) {
throw new Error(`Unknown template type ${template}. Known: ${TEMPLATE}`);
}
const gzipSize = !!opts.gzipSize;
const brotliSize = !!opts.brotliSize;
const additionalFilesInfo = new Map();
const gzipSizeGetter = gzipSize
? createGzipSizeGetter(
typeof opts.gzipSize === "object" ? opts.gzipSize : {}
)
: null;
const brotliSizeGetter = brotliSize
? createBrotliSizeGetter(
typeof opts.brotliSize === "object" ? opts.brotliSize : {}
)
: null;
const getAdditionalFilesInfo = async (id, code) => {
const info = {};
if (gzipSize) {
info.gzipLength = await gzipSizeGetter(code);
}
if (brotliSize) {
info.brotliLength = await brotliSizeGetter(code);
}
return info;
};
return {
name: "visualizer",
async transform(code, id) {
additionalFilesInfo.set(id, await getAdditionalFilesInfo(id, code));
return null;
},
async generateBundle(outputOptions, outputBundle) {
if (opts.sourcemap && !outputOptions.sourcemap) {
this.warn(WARN_SOURCEMAP_DISABLED);
}
const roots = [];
const mapper = new ModuleMapper();
const links = [];
// collect trees
for (const [id, bundle] of Object.entries(outputBundle)) {
if (isAsset(bundle)) continue; //only chunks
let tree;
if (opts.sourcemap) {
if (!bundle.map) {
this.warn(WARN_SOURCEMAP_MISSING(id));
}
const modules = await getSourcemapModules(
id,
bundle,
outputOptions.dir || path.dirname(outputOptions.file)
);
tree = buildTree(Object.entries(modules), mapper);
} else {
const modules = Object.entries(bundle.modules);
tree = buildTree(modules, mapper);
}
const bundleInfo = await getAdditionalFilesInfo(id, bundle.code);
Object.assign(
tree,
bundleInfo,
{
renderedLength: bundle.code.length,
isRoot: true,
name: id,
},
getAdditionalFilesInfo(id, bundle.code)
);
roots.push(tree);
}
// after trees we process links (this is mostly for uids)
for (const bundle of Object.values(outputBundle)) {
if (isAsset(bundle) || bundle.facadeModuleId == null) continue; //only chunks
addLinks(
bundle.facadeModuleId,
this.getModuleInfo.bind(this),
links,
mapper
);
}
const { nodes, nodeIds } = mapper;
for (const [id, uid] of Object.entries(nodeIds)) {
if (nodes[uid]) {
const newInfo = additionalFilesInfo.get(id) || {};
if (nodes[uid].renderedLength === 0) {
if (gzipSize) {
newInfo.gzipLength = 0;
}
if (brotliSize) {
newInfo.brotliLength = 0;
}
}
nodes[uid] = {
...nodes[uid],
...newInfo,
};
} else {
this.warn(`Could not find mapping for node ${id} ${uid}`);
}
}
removeCommonPrefix(nodes, nodeIds);
for (const [id, uid] of Object.entries(nodeIds)) {
if (nodes[uid]) {
nodes[uid].id = id;
} else {
this.warn(`Could not find mapping for node ${id} ${uid}`);
}
}
const tree = mergeTrees(roots);
const data = {
version: JSON_VERSION,
tree,
nodes,
links,
env: {
rollup: this.meta.rollupVersion,
[pkg.name]: pkg.version,
},
options: {
gzip: gzipSize,
brotli: brotliSize,
sourcemap: opts.sourcemap,
},
};
const fileContent = json
? JSON.stringify(data, null, 2)
: await buildStats({
title,
data,
template,
});
await fs.mkdir(path.dirname(filename), { recursive: true });
await fs.writeFile(filename, fileContent);
if (open) {
return opn(filename, openOptions);
}
},
};
};
"use strict";
const { customAlphabet } = require("nanoid/non-secure");
const warn = require("./warn");
const nanoid = customAlphabet("1234567890abcdef", 4);
class ModuleMapper {
constructor() {
this.id = 0;
this.prefix = nanoid();
this.nodes = Object.create(null);
this.nodeIds = Object.create(null);
}
getUid(moduleId) {
if (!(moduleId in this.nodeIds)) {
const id = this.id;
this.nodeIds[moduleId] = `${this.prefix}-${id}`;
this.id += 1;
}
return this.nodeIds[moduleId];
}
setValueByModuleId(moduleId, value) {
const uid = this.getUid(moduleId);
if (uid in this.nodes) {
warn(
"Override (probably this is a bug)",
moduleId,
uid,
value,
this.nodes[uid]
);
}
this.nodes[uid] = value;
return uid;
}
getValue(uid, defaultValue) {
if (!(uid in this.nodes)) {
this.nodes[uid] = defaultValue;
}
return this.nodes[uid];
}
}
module.exports = ModuleMapper;
"use strict";
const path = require("path");
const { SourceMapConsumer } = require("source-map");
const getBytesPerFileUsingSourceMap = (bundleId, code, map, dir) => {
const modules = Object.create(null);
let line = 1;
let column = 0;
for (let i = 0; i < code.length; i++, column++) {
const { source } = map.originalPositionFor({
line,
column,
});
if (source != null) {
const id = path.resolve(path.dirname(path.join(dir, bundleId)), source);
modules[id] = modules[id] || { renderedLength: 0 };
modules[id].renderedLength += 1;
}
if (code[i] === "\n") {
line += 1;
column = -1;
}
}
return modules;
};
const getSourcemapModules = (id, { map, code }, dir) => {
return SourceMapConsumer.with(map, null, (map) => {
return getBytesPerFileUsingSourceMap(id, code, map, dir);
});
};
module.exports = getSourcemapModules;
"use strict";
module.exports = ["sunburst", "treemap", "network"];
"use strict";
module.exports = 1;
"use strict";
const warn = (...args) => console.warn("[rollup-plugin-visualizer]", ...args);
module.exports = warn;