rollup-plugin-visualizer
Advanced tools
| { | ||
| "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" | ||
| ] | ||
| } |
+31
-7
| # Rollup Plugin Visualizer | ||
| [](https://npmjs.org/package/rollup-plugin-visualizer) [](https://travis-ci.com/btd/rollup-plugin-visualizer) | ||
| [](https://npmjs.org/package/rollup-plugin-visualizer) [](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 @@ |
-100
| #!/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); | ||
| }); |
-131
| :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
-163
| :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
-131
| :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, "&") | ||
| .replace(/"/g, """) | ||
| .replace(/'/g, "'") | ||
| .replace(/</g, "<") | ||
| .replace(/>/g, ">"); | ||
| 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 }; |
-176
| "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 }; |
-204
| "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; |
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
113
26.97%0
-100%15298
-96.76%37
85%5
-75%0
-100%2
Infinity%