initial commit

This commit is contained in:
Zoe
2023-01-03 09:29:04 -06:00
commit 7851137d88
12889 changed files with 2557443 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Denis Bardadym
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+184
View File
@@ -0,0 +1,184 @@
# 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)
Visualize and analyze your Rollup bundle to see which modules are taking up space.
## Screenshots
![pic](https://github.com/btd/rollup-plugin-visualizer/blob/master/pics/collage.png?raw=true)
## Installation
```sh
npm install --save-dev rollup-plugin-visualizer
```
or via yarn:
```sh
yarn add --dev rollup-plugin-visualizer
```
## Usage
Import
```javascript
// es
import { visualizer } from "rollup-plugin-visualizer";
// or
// cjs
const { visualizer } = require("rollup-plugin-visualizer");
```
Usage with rollup (rollup.config.js)
```js
module.exports = {
plugins: [
// put it the last one
visualizer(),
],
};
```
Usage with vite (vite.config.js)
```js
module.exports = {
plugins: [visualizer()],
};
```
Usage with vite TypeScript (vite.config.ts)
```ts
import { defineConfig, type PluginOption } from 'vite'
export default defineConfig({
plugins: [visualizer() as PluginOption],
})
```
Usage with SvelteKit (vite.config.js)
```js
const config = {
plugins: [
visualizer({
emitFile: true,
filename: "stats.html",
}),
],
};
export default config;
```
You will find 2/3 files in .svelte-kit/output dir named stats.html (see vite logs for file locations) . You can use this snippet as a starting point and change props/path.
You can also generate 3 json files and combine them to one with cli util.
## How to use generated files
Blue color used to to mark project-own files. This could be: written by you files or generated by build tool files.
Green color used to mark your dependencies.
Internally it just checks if file id prefix is `node_modules`.
All charts refresh layout on window resize.
### Sunburst
This circular hierarchical diagram can help you find huge pieces of code (aka that one huge thing). If you click on some arc it will increase its and all nested arcs size for better inspection.
### Treemap
This rectangular hierarchical diagram can help you find huge pieces. Just look on biggest reclangle. But also it can help you find modules included several times, they will have the same topology and relative size. If you click on reclangle it will increase in size for further inspection.
### Network
This digram should help you answer for the question 'why it is included?'. After force layout stabilize all nodes, you can move it back and forth by dragging with your mouse. Gray circles are treeshakened out files.
In real life scenarious, sometimes you will see terribly connected diagrams. There is no 100% working solution for everyone, it is expected you topology will look 'terrible' and not hold on screen. To make it still visually ispectable, first remove all highly connected nodes that you see (typical examples: commonjsHelpers, tslib, react etc, basically if tooltip for the node is not hold on the screen - exclude this node), after layout stabilization you will see, your layout is not that terrible anymore and most of dependencies cluster together. Move layout to find pieces you looked for.
When you click on node it will hightlight nodes that are listed in tooltip (the files that imports current node).
### Raw-data
This template produce JSON output with raw data. Normally it should be used with CLI from this plugin.
### List
Output yml file with all the data, could be good idea to commit this file to track file changes.
## Options
`filename` (string, default `stats.{ext depending template}`) - name of the file with diagram to generate
`title` (string, default `Rollup Visualizer`) - title tag value
`open` (boolean, default `false`) - Open generated file in default user agent
`template` (string, default `treemap`) - Which diagram type to use: `sunburst`, `treemap`, `network`, `raw-data`, `list`.
`gzipSize` (boolean, default `false`) - Collect gzip size from source code and display it at chart.
`brotliSize` (boolean, default `false`) - Collect brotli size from source code and display it at chart.
### Advanced options (touch only if you really need it):
`emitFile` (boolean, default `false`) - Use rollup's `emitFile` to generate file. Useful if you want to control all output in one place (via rollup output options). This also could be usefull with svelte as it calls vite several times.
`sourcemap` (boolean, default `false`) - Use sourcemaps to calculate sizes (e.g. after UglifyJs or Terser). **Always add plugin as last option.**
`projectRoot` (string | RegExp, default `process.cwd()`) - This is some common root(s) path to your files. This is used to cut absolute files paths out.
`include` (Filter | Filter[], default `undefined`) - Filter for inclusion
`exclude` (Filter | Filter[], default `undefined`) - Filter for exclusion
`Filter` type is `{ bundle?: picomatchPattern, file?: picomatchPattern }`
**Note about `include` and `exclude`** - If options.include is omitted or has zero length, filter will return true by default. Otherwise, an ID must match one or more of the picomatch patterns, and must not match any of the options.exclude patterns. This entries will not be included in stats at all.
## CLI
This plugin provides cli util `rollup-plugin-visualizer`. Add `--help` to check actual options. It can be used like:
```sh
rollup-plugin-visualizer [OPTIONS] stat1.json stat2.json ../stat3.json
```
This can be usefull in case you have different config files in the same project and you want to display all of them in the same chart.
## Build plugin
For development if you need to build plugin, just exec:
```js
npm run build
```
## Disclaimer about generated files
Generated html files do not and never will contain your source code (contents of files). They can contain only js/html/css code required to build chart (plugin code) and statistical information about your source code.
This statistical information can contain:
- size of files included in bundle
- size of files included in source map
- file's paths
- files hierarchy (fs tree for your files)
## Upgrades
See CHANGELOG.md.
## Versioning
* Plugin backend (one appears in configs) are strictly follows SemVer
* Plugin frontend (generated file):
* `network`, `treemap`, `sunburst` can change does not matter of version (colors, texts, visual structure etc)
* `raw-data` format follows own `version` property
* `list` follows semver
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env node
export {};
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env node
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const yargs_1 = __importDefault(require("yargs"));
const helpers_1 = require("yargs/helpers");
const render_template_1 = require("../plugin/render-template");
const template_types_1 = __importDefault(require("../plugin/template-types"));
const warn_1 = require("../plugin/warn");
const version_1 = require("../plugin/version");
const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
.option("filename", {
describe: "Output file name",
type: "string",
default: "./stats.html",
})
.option("title", {
describe: "Output file title",
type: "string",
default: "Rollup Visualizer",
})
.option("template", {
describe: "Template type",
type: "string",
choices: template_types_1.default,
default: "treemap",
})
.option("sourcemap", {
describe: "Provided files is sourcemaps",
type: "boolean",
default: false,
})
.help()
.parseSync();
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_1.promises.readFile(file, { encoding: "utf-8" });
const data = JSON.parse(textContent);
return { file, data };
}));
const tree = {
name: "root",
children: [],
};
const nodeParts = {};
const nodeMetas = {};
for (const { file, data } of fileContents) {
if (data.version !== version_1.version) {
(0, warn_1.warn)(`Version in ${file} is not supported (${data.version}). Current version ${version_1.version}. Skipping...`);
continue;
}
if (data.tree.name === "root") {
tree.children = tree.children.concat(data.tree.children);
}
else {
tree.children.push(data.tree);
}
Object.assign(nodeParts, data.nodeParts);
Object.assign(nodeMetas, data.nodeMetas);
}
const data = {
version: version_1.version,
tree,
nodeParts,
nodeMetas,
env: fileContents[0].data.env,
options: fileContents[0].data.options,
};
const fileContent = await (0, render_template_1.renderTemplate)(template, {
title,
data,
});
await fs_1.promises.mkdir(path_1.default.dirname(filename), { recursive: true });
try {
await fs_1.promises.unlink(filename);
}
catch (err) {
// ignore
}
await fs_1.promises.writeFile(filename, fileContent);
};
runForPluginJson(argv, listOfFiles).catch((err) => {
(0, warn_1.warn)(err.message);
process.exit(1);
});
+129
View File
@@ -0,0 +1,129 @@
: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: #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;
}
.module-filters {
display: flex;
flex-grow: 1;
}
.module-filter {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
}
.module-filter input {
flex: 1;
height: 1rem;
padding: 0.01rem;
font-size: 0.7rem;
margin-left: 0.3rem;
}
.module-filter + .module-filter {
margin-left: 0.5rem;
}
File diff suppressed because one or more lines are too long
+161
View File
@@ -0,0 +1,161 @@
: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: #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;
}
.module-filters {
display: flex;
flex-grow: 1;
}
.module-filter {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
}
.module-filter input {
flex: 1;
height: 1rem;
padding: 0.01rem;
font-size: 0.7rem;
margin-left: 0.3rem;
}
.module-filter + .module-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;
}
File diff suppressed because one or more lines are too long
+129
View File
@@ -0,0 +1,129 @@
: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: #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;
}
.module-filters {
display: flex;
flex-grow: 1;
}
.module-filter {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex: 1;
}
.module-filter input {
flex: 1;
height: 1rem;
padding: 0.01rem;
font-size: 0.7rem;
margin-left: 0.3rem;
}
.module-filter + .module-filter {
margin-left: 0.5rem;
}
File diff suppressed because one or more lines are too long
+4
View File
@@ -0,0 +1,4 @@
import * as zlib from "zlib";
export type SizeGetter = (code: string) => Promise<number>;
export declare const createGzipSizeGetter: (options: zlib.ZlibOptions) => SizeGetter;
export declare const createBrotliSizeGetter: (options: zlib.BrotliOptions) => SizeGetter;
+60
View File
@@ -0,0 +1,60 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBrotliSizeGetter = exports.createGzipSizeGetter = void 0;
const zlib = __importStar(require("zlib"));
const util_1 = require("util");
const gzip = (0, util_1.promisify)(zlib.gzip);
const brotliCompress = (0, util_1.promisify)(zlib.brotliCompress);
const gzipOptions = (options) => ({
level: 9,
...options,
});
const brotliOptions = (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;
};
};
exports.createGzipSizeGetter = createGzipSizeGetter;
const createBrotliCompressor = (options) => (buffer) => brotliCompress(buffer, brotliOptions(options || {}, buffer));
const createBrotliSizeGetter = (options) => {
const compress = createBrotliCompressor(options);
return async (code) => {
const data = await compress(Buffer.from(code, "utf-8"));
return data.length;
};
};
exports.createBrotliSizeGetter = createBrotliSizeGetter;
+8
View File
@@ -0,0 +1,8 @@
import { GetModuleInfo } from "rollup";
import { ModuleLengths, ModuleTree, ModuleTreeLeaf } from "../shared/types";
import { ModuleMapper } from "./module-mapper";
export declare const buildTree: (bundleId: string, modules: Array<ModuleLengths & {
id: string;
}>, mapper: ModuleMapper) => ModuleTree;
export declare const mergeTrees: (trees: Array<ModuleTree | ModuleTreeLeaf>) => ModuleTree;
export declare const addLinks: (startModuleId: string, getModuleInfo: GetModuleInfo, mapper: ModuleMapper) => void;
+121
View File
@@ -0,0 +1,121 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.addLinks = exports.mergeTrees = exports.buildTree = void 0;
const types_1 = require("../shared/types");
const addToPath = (moduleId, tree, modulePath, node) => {
if (modulePath.length === 0) {
throw new Error(`Error adding node to path ${moduleId}`);
}
const [head, ...rest] = modulePath;
if (rest.length === 0) {
tree.children.push({ ...node, name: head });
return;
}
else {
let newTree = tree.children.find((folder) => folder.name === head && (0, types_1.isModuleTree)(folder));
if (!newTree) {
newTree = { name: head, children: [] };
tree.children.push(newTree);
}
addToPath(moduleId, newTree, rest, node);
return;
}
};
// TODO try to make it without recursion, but still typesafe
const mergeSingleChildTrees = (tree) => {
if (tree.children.length === 1) {
const child = tree.children[0];
const name = `${tree.name}/${child.name}`;
if ((0, types_1.isModuleTree)(child)) {
tree.name = name;
tree.children = child.children;
return mergeSingleChildTrees(tree);
}
else {
return {
name,
uid: child.uid,
};
}
}
else {
tree.children = tree.children.map((node) => {
if ((0, types_1.isModuleTree)(node)) {
return mergeSingleChildTrees(node);
}
else {
return node;
}
});
return tree;
}
};
const buildTree = (bundleId, modules, mapper) => {
const tree = {
name: bundleId,
children: [],
};
for (const { id, renderedLength, gzipLength, brotliLength } of modules) {
const bundleModuleUid = mapper.setNodePart(bundleId, id, {
renderedLength,
gzipLength,
brotliLength,
});
const trimmedModuleId = mapper.trimProjectRootId(id);
const pathParts = trimmedModuleId.split(/\\|\//).filter((p) => p !== "");
addToPath(trimmedModuleId, tree, pathParts, { uid: bundleModuleUid });
}
tree.children = tree.children.map((node) => {
if ((0, types_1.isModuleTree)(node)) {
return mergeSingleChildTrees(node);
}
else {
return node;
}
});
return tree;
};
exports.buildTree = buildTree;
const mergeTrees = (trees) => {
const newTree = {
name: "root",
children: trees,
isRoot: true,
};
return newTree;
};
exports.mergeTrees = mergeTrees;
const addLinks = (startModuleId, getModuleInfo, mapper) => {
const processedNodes = {};
const moduleIds = [startModuleId];
while (moduleIds.length > 0) {
const moduleId = moduleIds.shift();
if (processedNodes[moduleId]) {
continue;
}
else {
processedNodes[moduleId] = true;
}
const moduleInfo = getModuleInfo(moduleId);
if (!moduleInfo) {
return;
}
if (moduleInfo.isEntry) {
mapper.setNodeMeta(moduleId, { isEntry: true });
}
if (moduleInfo.isExternal) {
mapper.setNodeMeta(moduleId, { isExternal: true });
}
for (const importedId of moduleInfo.importedIds) {
mapper.addImportedByLink(importedId, moduleId);
mapper.addImportedLink(moduleId, importedId);
moduleIds.push(importedId);
}
for (const importedId of moduleInfo.dynamicallyImportedIds || []) {
mapper.addImportedByLink(importedId, moduleId);
mapper.addImportedLink(moduleId, importedId, true);
moduleIds.push(importedId);
}
}
};
exports.addLinks = addLinks;
+83
View File
@@ -0,0 +1,83 @@
import { Plugin, OutputOptions } from "rollup";
import opn from "open";
import { TemplateType } from "./template-types";
import { Filter } from "../shared/create-filter";
export interface PluginVisualizerOptions {
/**
* The path to the template file to use. Or just a name of a file.
*
* @default "stats.html"
*/
filename?: string;
/**
* If plugin should emit json file with visualizer data. It can be used with plugin CLI
*
* @default false
* @deprecated use template 'raw-data'
*/
json?: boolean;
/**
* HTML <title> value in generated file. Ignored when `json` is true.
*
* @default "Rollup Visualizer"
*/
title?: string;
/**
* If plugin should open browser with generated file. Ignored when `json` or `emitFile` is true.
*
* @default false
*/
open?: boolean;
openOptions?: opn.Options;
/**
* Which diagram to generate. 'sunburst' or 'treemap' can help find big dependencies or if they are repeated.
* 'network' can answer you why something was included
*
* @default 'treemap'
*/
template?: TemplateType;
/**
* If plugin should also calculate sizes of gzipped files.
*
* @default false
*/
gzipSize?: boolean;
/**
* If plugin should also calculate sizes of brotlied files.
*
* @default false
*/
brotliSize?: boolean;
/**
* If plugin should use sourcemap to calculate sizes of modules. By idea it will present more accurate results.
* `gzipSize` and `brotliSize` does not make much sense with this option.
*
* @default false
*/
sourcemap?: boolean;
/**
* Absolute path where project is located. It is used to cut prefix from file's paths.
*
* @default process.cwd()
*/
projectRoot?: string | RegExp;
/**
* Use rollup .emitFile API to generate files. Could be usefull if you want to output to configured by rollup output dir.
* When this set to true, filename options must be filename and not a path.
*
* @default false
*/
emitFile?: boolean;
/**
* A valid picomatch pattern, or array of patterns. If options.include is omitted or has zero length, filter will return true by
* default. Otherwise, an ID must match one or more of the picomatch patterns, and must not match any of the options.exclude patterns.
*/
include?: Filter | Filter[];
/**
* A valid picomatch pattern, or array of patterns. If options.include is omitted or has zero length, filter will return true by
* default. Otherwise, an ID must match one or more of the picomatch patterns, and must not match any of the options.exclude patterns.
*/
exclude?: Filter | Filter[];
}
export declare const visualizer: (opts?: PluginVisualizerOptions | ((outputOptions: OutputOptions) => PluginVisualizerOptions)) => Plugin;
export default visualizer;
+159
View File
@@ -0,0 +1,159 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.visualizer = void 0;
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const open_1 = __importDefault(require("open"));
const version_1 = require("./version");
const compress_1 = require("./compress");
const module_mapper_1 = require("./module-mapper");
const data_1 = require("./data");
const sourcemap_1 = require("./sourcemap");
const render_template_1 = require("./render-template");
const create_filter_1 = require("../shared/create-filter");
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 WARN_JSON_DEPRECATED = 'Option `json` deprecated, please use template: "raw-data"';
const ERR_FILENAME_EMIT = "When using emitFile option, filename must not be path but a filename";
const defaultSizeGetter = () => Promise.resolve(0);
const chooseDefaultFileName = (opts) => {
if (opts.filename)
return opts.filename;
if (opts.json || opts.template === "raw-data")
return "stats.json";
if (opts.template === "list")
return "stats.yml";
return "stats.html";
};
const visualizer = (opts = {}) => {
return {
name: "visualizer",
async generateBundle(outputOptions, outputBundle) {
var _a, _b, _c, _d, _e, _f, _g, _h;
opts = typeof opts === "function" ? opts(outputOptions) : opts;
if ("json" in opts) {
this.warn(WARN_JSON_DEPRECATED);
if (opts.json)
opts.template = "raw-data";
}
const filename = (_a = opts.filename) !== null && _a !== void 0 ? _a : chooseDefaultFileName(opts);
const title = (_b = opts.title) !== null && _b !== void 0 ? _b : "Rollup Visualizer";
const open = !!opts.open;
const openOptions = (_c = opts.openOptions) !== null && _c !== void 0 ? _c : {};
const template = (_d = opts.template) !== null && _d !== void 0 ? _d : "treemap";
const projectRoot = (_e = opts.projectRoot) !== null && _e !== void 0 ? _e : process.cwd();
const filter = (0, create_filter_1.createFilter)(opts.include, opts.exclude);
const gzipSize = !!opts.gzipSize && !opts.sourcemap;
const brotliSize = !!opts.brotliSize && !opts.sourcemap;
const gzipSizeGetter = gzipSize
? (0, compress_1.createGzipSizeGetter)(typeof opts.gzipSize === "object" ? opts.gzipSize : {})
: defaultSizeGetter;
const brotliSizeGetter = brotliSize
? (0, compress_1.createBrotliSizeGetter)(typeof opts.brotliSize === "object" ? opts.brotliSize : {})
: defaultSizeGetter;
const ModuleLengths = async ({ id, renderedLength, code, }) => {
const isCodeEmpty = code == null || code == "";
const result = {
id,
gzipLength: isCodeEmpty ? 0 : await gzipSizeGetter(code),
brotliLength: isCodeEmpty ? 0 : await brotliSizeGetter(code),
renderedLength: isCodeEmpty ? renderedLength : Buffer.byteLength(code, "utf-8"),
};
return result;
};
if (opts.sourcemap && !outputOptions.sourcemap) {
this.warn(WARN_SOURCEMAP_DISABLED);
}
const roots = [];
const mapper = new module_mapper_1.ModuleMapper(projectRoot);
// collect trees
for (const [bundleId, bundle] of Object.entries(outputBundle)) {
if (bundle.type !== "chunk")
continue; //only chunks
let tree;
if (opts.sourcemap) {
if (!bundle.map) {
this.warn(WARN_SOURCEMAP_MISSING(bundleId));
}
const modules = await (0, sourcemap_1.getSourcemapModules)(bundleId, bundle, (_g = (_f = outputOptions.dir) !== null && _f !== void 0 ? _f : (outputOptions.file && path_1.default.dirname(outputOptions.file))) !== null && _g !== void 0 ? _g : process.cwd());
const moduleRenderInfo = await Promise.all(Object.values(modules)
.filter(({ id }) => filter(bundleId, id))
.map(({ id, renderedLength }) => {
var _a;
const code = (_a = bundle.modules[id]) === null || _a === void 0 ? void 0 : _a.code;
return ModuleLengths({ id, renderedLength, code });
}));
tree = (0, data_1.buildTree)(bundleId, moduleRenderInfo, mapper);
}
else {
const modules = await Promise.all(Object.entries(bundle.modules)
.filter(([id]) => filter(bundleId, id))
.map(([id, { renderedLength, code }]) => ModuleLengths({ id, renderedLength, code })));
tree = (0, data_1.buildTree)(bundleId, modules, mapper);
}
if (tree.children.length === 0) {
const bundleSizes = await ModuleLengths({
id: bundleId,
renderedLength: bundle.code.length,
code: bundle.code,
});
const facadeModuleId = (_h = bundle.facadeModuleId) !== null && _h !== void 0 ? _h : `${bundleId}-unknown`;
const bundleUid = mapper.setNodePart(bundleId, facadeModuleId, bundleSizes);
mapper.setNodeMeta(facadeModuleId, { isEntry: true });
const leaf = { name: bundleId, uid: bundleUid };
roots.push(leaf);
}
else {
roots.push(tree);
}
}
// after trees we process links (this is mostly for uids)
for (const [, bundle] of Object.entries(outputBundle)) {
if (bundle.type !== "chunk" || bundle.facadeModuleId == null)
continue; //only chunks
(0, data_1.addLinks)(bundle.facadeModuleId, this.getModuleInfo.bind(this), mapper);
}
const tree = (0, data_1.mergeTrees)(roots);
const data = {
version: version_1.version,
tree,
nodeParts: mapper.getNodeParts(),
nodeMetas: mapper.getNodeMetas(),
env: {
rollup: this.meta.rollupVersion,
},
options: {
gzip: gzipSize,
brotli: brotliSize,
sourcemap: !!opts.sourcemap,
},
};
const fileContent = await (0, render_template_1.renderTemplate)(template, {
title,
data,
});
if (opts.emitFile) {
if (path_1.default.isAbsolute(filename) || filename.startsWith(".")) {
this.error(ERR_FILENAME_EMIT);
}
this.emitFile({
type: "asset",
fileName: filename,
source: fileContent,
});
}
else {
await fs_1.promises.mkdir(path_1.default.dirname(filename), { recursive: true });
await fs_1.promises.writeFile(filename, fileContent);
if (open) {
await (0, open_1.default)(filename, openOptions);
}
}
},
};
};
exports.visualizer = visualizer;
exports.default = exports.visualizer;
+20
View File
@@ -0,0 +1,20 @@
import { ModuleMeta, ModuleLengths, ModuleUID } from "../shared/types";
export declare class ModuleMapper {
private projectRoot;
private nodeParts;
private nodeMetas;
constructor(projectRoot: string | RegExp);
trimProjectRootId(moduleId: string): string;
getModuleUid(moduleId: string): ModuleUID;
getBundleModuleUid(bundleId: string, moduleId: string): ModuleUID;
setNodePart(bundleId: string, moduleId: string, value: ModuleLengths): ModuleUID;
setNodeMeta(moduleId: string, value: {
isEntry?: boolean;
isExternal?: boolean;
}): void;
hasNodePart(bundleId: string, moduleId: string): boolean;
getNodeParts(): ModuleMapper["nodeParts"];
getNodeMetas(): Record<ModuleUID, ModuleMeta>;
addImportedByLink(targetId: string, sourceId: string): void;
addImportedLink(sourceId: string, targetId: string, dynamic?: boolean): void;
}
+116
View File
@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModuleMapper = void 0;
const uid_1 = require("./uid");
const nanoid = (0, uid_1.getUid)("1234567890abcdef", 4);
const UNIQUE_PREFIX = nanoid();
let COUNTER = 0;
const uniqueId = () => `${UNIQUE_PREFIX}-${COUNTER++}`;
class ModuleMapper {
constructor(projectRoot) {
this.projectRoot = projectRoot;
this.nodeParts = {};
this.nodeMetas = {};
}
trimProjectRootId(moduleId) {
if (typeof this.projectRoot === 'string' && moduleId.startsWith(this.projectRoot)) {
return moduleId.slice(this.projectRoot.length);
}
return moduleId.replace(this.projectRoot, "");
}
getModuleUid(moduleId) {
if (!(moduleId in this.nodeMetas)) {
this.nodeMetas[moduleId] = {
uid: uniqueId(),
meta: {
id: this.trimProjectRootId(moduleId),
moduleParts: {},
imported: new Set(),
importedBy: new Set(),
},
};
}
return this.nodeMetas[moduleId].uid;
}
getBundleModuleUid(bundleId, moduleId) {
if (!(moduleId in this.nodeMetas)) {
this.nodeMetas[moduleId] = {
uid: uniqueId(),
meta: {
id: this.trimProjectRootId(moduleId),
moduleParts: {},
imported: new Set(),
importedBy: new Set(),
},
};
}
if (!(bundleId in this.nodeMetas[moduleId].meta.moduleParts)) {
this.nodeMetas[moduleId].meta.moduleParts[bundleId] = uniqueId();
}
return this.nodeMetas[moduleId].meta.moduleParts[bundleId];
}
setNodePart(bundleId, moduleId, value) {
const uid = this.getBundleModuleUid(bundleId, moduleId);
if (uid in this.nodeParts) {
throw new Error(`Override module: bundle id ${bundleId}, module id ${moduleId}, value ${JSON.stringify(value)}, existing value: ${JSON.stringify(this.nodeParts[uid])}`);
}
this.nodeParts[uid] = { ...value, metaUid: this.getModuleUid(moduleId) };
return uid;
}
setNodeMeta(moduleId, value) {
this.getModuleUid(moduleId);
this.nodeMetas[moduleId].meta.isEntry = value.isEntry;
this.nodeMetas[moduleId].meta.isExternal = value.isExternal;
}
hasNodePart(bundleId, moduleId) {
if (!(moduleId in this.nodeMetas)) {
return false;
}
if (!(bundleId in this.nodeMetas[moduleId].meta.moduleParts)) {
return false;
}
if (!(this.nodeMetas[moduleId].meta.moduleParts[bundleId] in this.nodeParts)) {
return false;
}
return true;
}
getNodeParts() {
return this.nodeParts;
}
getNodeMetas() {
const nodeMetas = {};
for (const { uid, meta } of Object.values(this.nodeMetas)) {
nodeMetas[uid] = {
...meta,
imported: [...meta.imported].map((rawImport) => {
const [uid, dynamic] = rawImport.split(",");
const importData = { uid };
if (dynamic === "true") {
importData.dynamic = true;
}
return importData;
}),
importedBy: [...meta.importedBy].map((rawImport) => {
const [uid, dynamic] = rawImport.split(",");
const importData = { uid };
if (dynamic === "true") {
importData.dynamic = true;
}
return importData;
}),
};
}
return nodeMetas;
}
addImportedByLink(targetId, sourceId) {
const sourceUid = this.getModuleUid(sourceId);
this.getModuleUid(targetId);
this.nodeMetas[targetId].meta.importedBy.add(sourceUid);
}
addImportedLink(sourceId, targetId, dynamic = false) {
const targetUid = this.getModuleUid(targetId);
this.getModuleUid(sourceId);
this.nodeMetas[sourceId].meta.imported.add(String([targetUid, dynamic]));
}
}
exports.ModuleMapper = ModuleMapper;
@@ -0,0 +1,7 @@
import { VisualizerData } from "../shared/types";
import { TemplateType } from "./template-types";
export type RenderTemplateOptions = {
data: VisualizerData;
title: string;
};
export declare const renderTemplate: (templateType: TemplateType, options: RenderTemplateOptions) => Promise<string>;
+104
View File
@@ -0,0 +1,104 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderTemplate = void 0;
/* eslint-disable @typescript-eslint/require-await */
const fs_1 = require("fs");
const path_1 = __importDefault(require("path"));
const htmlEscape = (str) => str
.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.default(chartNode, data, width, height);
};
window.addEventListener('resize', run);
document.addEventListener('DOMContentLoaded', run);
/*-->*/
</script>
</body>
</html>
`;
const buildHtml = (template) => async ({ title, data }) => {
const [script, style] = await Promise.all([
fs_1.promises.readFile(path_1.default.join(__dirname, "..", "lib", `${template}.js`), "utf8"),
fs_1.promises.readFile(path_1.default.join(__dirname, "..", "lib", `${template}.css`), "utf8"),
]);
return buildHtmlTemplate(title, script, JSON.stringify(data), style);
};
const outputRawData = (data) => JSON.stringify(data, null, 2);
const outputPlainTextList = (data) => {
var _a;
const bundles = {};
for (const meta of Object.values(data.nodeMetas)) {
for (const [bundleId, uid] of Object.entries(meta.moduleParts)) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { metaUid: mainUid, ...lengths } = data.nodeParts[uid];
bundles[bundleId] = (_a = bundles[bundleId]) !== null && _a !== void 0 ? _a : [];
bundles[bundleId].push([meta.id, lengths]);
}
}
const bundlesEntries = Object.entries(bundles).sort((e1, e2) => e1[0].localeCompare(e2[0]));
let output = "";
const IDENT = " ";
for (const [bundleId, files] of bundlesEntries) {
output += bundleId + ":\n";
files.sort((e1, e2) => e1[0].localeCompare(e2[0]));
for (const [file, lengths] of files) {
output += IDENT + file + ":\n";
output += IDENT + IDENT + `rendered: ${String(lengths.renderedLength)}\n`;
if (data.options.gzip) {
output += IDENT + IDENT + `gzip: ${String(lengths.gzipLength)}\n`;
}
if (data.options.brotli) {
output += IDENT + IDENT + `brotli: ${String(lengths.brotliLength)}\n`;
}
}
}
return output;
};
const TEMPLATE_TYPE_RENDERED = {
network: buildHtml("network"),
sunburst: buildHtml("sunburst"),
treemap: buildHtml("treemap"),
"raw-data": async ({ data }) => outputRawData(data),
list: async ({ data }) => outputPlainTextList(data),
};
const renderTemplate = (templateType, options) => {
return TEMPLATE_TYPE_RENDERED[templateType](options);
};
exports.renderTemplate = renderTemplate;
+7
View File
@@ -0,0 +1,7 @@
import { OutputChunk } from "rollup";
interface SourceMapModuleRenderInfo {
id: string;
renderedLength: number;
}
export declare const getSourcemapModules: (id: string, outputChunk: OutputChunk, dir: string) => Promise<Record<string, SourceMapModuleRenderInfo>>;
export {};
+38
View File
@@ -0,0 +1,38 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSourcemapModules = void 0;
const path_1 = __importDefault(require("path"));
const source_map_1 = require("source-map");
const getBytesPerFileUsingSourceMap = (bundleId, code, map, dir) => {
const modules = {};
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_1.default.resolve(path_1.default.dirname(path_1.default.join(dir, bundleId)), source);
modules[id] = modules[id] || { id, renderedLength: 0 };
modules[id].renderedLength += 1;
}
if (code[i] === "\n") {
line += 1;
column = -1;
}
}
return modules;
};
const getSourcemapModules = (id, outputChunk, dir) => {
if (!outputChunk.map) {
return Promise.resolve({});
}
return source_map_1.SourceMapConsumer.with(outputChunk.map, null, (map) => {
return getBytesPerFileUsingSourceMap(id, outputChunk.code, map, dir);
});
};
exports.getSourcemapModules = getSourcemapModules;
@@ -0,0 +1,3 @@
export type TemplateType = "sunburst" | "treemap" | "network" | "raw-data" | "list";
declare const templates: ReadonlyArray<TemplateType>;
export default templates;
+10
View File
@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const templates = [
"sunburst",
"treemap",
"network",
"list",
"raw-data",
];
exports.default = templates;
+1
View File
@@ -0,0 +1 @@
export declare const getUid: (alphabit: string | string[], size: number) => () => string;
+11
View File
@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUid = void 0;
const getUid = (alphabit, size) => () => {
let result = "";
for (let i = 0; i < size; i++) {
result += alphabit[(Math.random() * alphabit.length) | 0];
}
return result;
};
exports.getUid = getUid;
+1
View File
@@ -0,0 +1 @@
export declare const version = 2;
+4
View File
@@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = void 0;
exports.version = 2;
+1
View File
@@ -0,0 +1 @@
export declare const warn: (...args: any[]) => void;
+6
View File
@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.warn = void 0;
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
const warn = (...args) => console.warn("[rollup-plugin-visualizer]", ...args);
exports.warn = warn;
+5
View File
@@ -0,0 +1,5 @@
export type Filter = {
bundle?: string | null | undefined;
file?: string | null | undefined;
};
export declare const createFilter: (include: Filter | Filter[] | undefined, exclude: Filter | Filter[] | undefined) => (bundleId: string, id: string) => boolean;
+56
View File
@@ -0,0 +1,56 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFilter = void 0;
const picomatch_1 = __importDefault(require("picomatch"));
function isArray(arg) {
return Array.isArray(arg);
}
function ensureArray(thing) {
if (isArray(thing))
return thing;
if (thing == null)
return [];
return [thing];
}
const globToTest = (glob) => {
const pattern = glob;
const fn = (0, picomatch_1.default)(pattern, { dot: true });
return {
test: (what) => {
const result = fn(what);
return result;
},
};
};
const testFalse = {
test: () => false,
};
const testTrue = {
test: () => true,
};
const getMatcher = (filter) => {
const bundleTest = "bundle" in filter && filter.bundle != null ? globToTest(filter.bundle) : testTrue;
const fileTest = "file" in filter && filter.file != null ? globToTest(filter.file) : testTrue;
return { bundleTest, fileTest };
};
const createFilter = (include, exclude) => {
const includeMatchers = ensureArray(include).map(getMatcher);
const excludeMatchers = ensureArray(exclude).map(getMatcher);
return (bundleId, id) => {
for (let i = 0; i < excludeMatchers.length; ++i) {
const { bundleTest, fileTest } = excludeMatchers[i];
if (bundleTest.test(bundleId) && fileTest.test(id))
return false;
}
for (let i = 0; i < includeMatchers.length; ++i) {
const { bundleTest, fileTest } = includeMatchers[i];
if (bundleTest.test(bundleId) && fileTest.test(id))
return true;
}
return !includeMatchers.length;
};
};
exports.createFilter = createFilter;
@@ -0,0 +1 @@
export {};
+106
View File
@@ -0,0 +1,106 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const globals_1 = require("@jest/globals");
const create_filter_1 = require("./create-filter");
(0, globals_1.describe)("createFilter", () => {
(0, globals_1.it)("should return true when input and output is empty", () => {
const isIncluded = (0, create_filter_1.createFilter)([], []);
(0, globals_1.expect)(isIncluded("bundle", "file")).toBe(true);
});
(0, globals_1.describe)("Bundle", () => {
globals_1.it.each([
[[{ bundle: "b1.js" }], "b1.js", "file", false],
[[{ bundle: "b1.js" }], "b2.js", "file", true],
[[{ bundle: "translation-*.js" }], "b2.js", "file", true],
[[{ bundle: "translation-*.js" }], "translation-de.js", "file", false],
])("%# should exclude %j bundle %j file %j - %j", (exclude, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)([], exclude);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
globals_1.it.each([
[[{ bundle: "b1.js" }], "b1.js", "file", true],
[[{ bundle: "b1.js" }], "b2.js", "file", false],
[[{ bundle: "translation-*.js" }], "b2.js", "file", false],
[[{ bundle: "translation-*.js" }], "translation-de.js", "file", true],
])("%# should include %j bundle %j file %j - %j", (include, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)(include, []);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
globals_1.it.each([
[
[{ bundle: "translation-*.js" }],
[{ bundle: "translation-de.js" }],
"translation-de.js",
"file",
false,
],
[
[{ bundle: "translation-*.js" }],
[{ bundle: "translation-de.js" }],
"translation-en.js",
"file",
true,
],
])("%# should exclude included %j %j bundle %j file %j - %j", (include, exclude, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)(include, exclude);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
});
(0, globals_1.describe)("File", () => {
globals_1.it.each([
[[{ file: "b1.js" }], "bundle", "b1.js", false],
[[{ file: "b1.js" }], "bundle", "b2.js", true],
[[{ file: "translation-*.js" }], "bundle", "b2.js", true],
[[{ file: "translation-*.js" }], "bundle", "translation-de.js", false],
])("%# should exclude %j bundle %j file %j - %j", (exclude, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)([], exclude);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
globals_1.it.each([
[[{ file: "b1.js" }], "bundle", "b1.js", true],
[[{ file: "b1.js" }], "bundle", "b2.js", false],
[[{ file: "translation-*.js" }], "bundle", "b2.js", false],
[[{ file: "translation-*.js" }], "bundle", "translation-de.js", true],
])("%# should include %j bundle %j file %j - %j", (include, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)(include, []);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
globals_1.it.each([
[
[{ file: "translations/**" }],
[{ file: "translations/de.js" }],
"bundle",
"translations/de.js",
false,
],
[
[{ file: "translations/**" }],
[{ file: "translations/de.js" }],
"bundle",
"translations/en.js",
true,
],
])("%# should exclude included %j %j bundle %j file %j - %j", (include, exclude, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)(include, exclude);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
});
(0, globals_1.describe)("File in Bundle", () => {
globals_1.it.each([
[[{ bundle: "b1.js", file: "f1.js" }], "b1.js", "f1.js", false],
[[{ bundle: "b1.js", file: "f1.js" }], "b2.js", "f1.js", true],
[[{ bundle: "b1.js", file: "f1.js" }], "b1.js", "f2.js", true],
])("%# should exclude %j bundle %j file %j - %j", (exclude, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)([], exclude);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
globals_1.it.each([
[[{ bundle: "b1.js", file: "f1.js" }], "b1.js", "f1.js", true],
[[{ bundle: "b1.js", file: "f1.js" }], "b2.js", "f1.js", false],
[[{ bundle: "b1.js", file: "f1.js" }], "b1.js", "f2.js", false],
])("%# should include %j bundle %j file %j - %j", (include, bundle, file, result) => {
const isIncluded = (0, create_filter_1.createFilter)(include, []);
(0, globals_1.expect)(isIncluded(bundle, file)).toBe(result);
});
});
});
+46
View File
@@ -0,0 +1,46 @@
export type SizeKey = "renderedLength" | "gzipLength" | "brotliLength";
export declare const isModuleTree: (mod: ModuleTree | ModuleTreeLeaf) => mod is ModuleTree;
export type ModuleUID = string;
export type BundleId = string;
export interface ModuleTreeLeaf {
name: string;
uid: ModuleUID;
}
export interface ModuleTree {
name: string;
children: Array<ModuleTree | ModuleTreeLeaf>;
}
export type ModulePart = {
metaUid: ModuleUID;
} & ModuleLengths;
export type ModuleImport = {
uid: ModuleUID;
dynamic?: boolean;
};
export type ModuleMeta = {
moduleParts: Record<BundleId, ModuleUID>;
importedBy: ModuleImport[];
imported: ModuleImport[];
isEntry?: boolean;
isExternal?: boolean;
id: string;
};
export interface ModuleLengths {
renderedLength: number;
gzipLength: number;
brotliLength: number;
}
export interface VisualizerData {
version: number;
tree: ModuleTree;
nodeParts: Record<ModuleUID, ModulePart>;
nodeMetas: Record<ModuleUID, ModuleMeta>;
env: {
[key: string]: unknown;
};
options: {
gzip: boolean;
brotli: boolean;
sourcemap: boolean;
};
}
+5
View File
@@ -0,0 +1,5 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isModuleTree = void 0;
const isModuleTree = (mod) => "children" in mod;
exports.isModuleTree = isModuleTree;
+96
View File
@@ -0,0 +1,96 @@
{
"name": "rollup-plugin-visualizer",
"version": "5.9.0",
"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": "rollup -c rollup.config.js",
"build-dev": "rollup -c rollup.config-dev.js",
"clean": "del-cli dist",
"prebuild": "npm run clean",
"test": "jest"
},
"dependencies": {
"open": "^8.4.0",
"picomatch": "^2.3.1",
"source-map": "^0.7.4",
"yargs": "^17.5.1"
},
"peerDependencies": {
"rollup": "2.x || 3.x"
},
"peerDependenciesMeta": {
"rollup": {
"optional": true
}
},
"devDependencies": {
"@jest/globals": "^29.3.1",
"@rollup/plugin-alias": "^4.0.2",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-typescript": "^10.0.1",
"@types/bytes": "^3.1.1",
"@types/d3-array": "^3.0.3",
"@types/d3-color": "^3.1.0",
"@types/d3-force": "^3.0.3",
"@types/d3-hierarchy": "^3.1.0",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/node": "^18.8.5",
"@types/picomatch": "^2.3.0",
"@types/yargs": "^17.0.10",
"@typescript-eslint/eslint-plugin": "^5.25.0",
"@typescript-eslint/parser": "^5.25.0",
"bytes": "^3.1.2",
"d3-array": "^3.1.6",
"d3-color": "^3.1.0",
"d3-force": "^3.0.0",
"d3-hierarchy": "^3.1.2",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"del-cli": "^5.0.0",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^29.3.1",
"npm-run-all": "^4.1.5",
"picomatch-browser": "^2.2.6",
"postcss": "^8.4.14",
"postcss-url": "^10.1.3",
"preact": "^10.7.2",
"prettier": "^2.6.2",
"rollup": "^3.1.0",
"rollup-plugin-postcss": "^4.0.2",
"sass": "^1.52.1",
"ts-jest": "^29.0.3",
"typescript": "~4.9.0"
},
"engines": {
"node": ">=14"
},
"keywords": [
"rollup-plugin",
"visualizer",
"network",
"treemap",
"sunburst",
"diagram"
]
}