Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,2 @@
#!/usr/bin/env node
export {};

View file

@ -0,0 +1,102 @@
#!/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 open_1 = __importDefault(require("open"));
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,
})
.option("open", {
describe: "Open generated tempate in default user agent",
type: "boolean",
default: false,
})
.help()
.parseSync();
const listOfFiles = argv._;
const runForPluginJson = async ({ title, template, filename, open }, 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: JSON.stringify(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);
if (open) {
await (0, open_1.default)(filename);
}
};
runForPluginJson(argv, listOfFiles).catch((err) => {
(0, warn_1.warn)(err.message);
process.exit(1);
});

View file

@ -0,0 +1,133 @@
: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;
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;
}
.node {
cursor: pointer;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,133 @@
: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;
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;
}
.node {
cursor: pointer;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,165 @@
: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;
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;
}
.node {
cursor: pointer;
}
.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

View file

@ -0,0 +1,133 @@
: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;
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;
}
.node {
cursor: pointer;
}

File diff suppressed because one or more lines are too long

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;

View file

@ -0,0 +1,70 @@
"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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__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;

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;

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;

View file

@ -0,0 +1,84 @@
import { Plugin, OutputOptions } from "rollup";
import { Options as OpenOptions } 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?: OpenOptions;
/**
* 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.
* 'flamegraph' will be familar to tools that you know already.
*
* @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;

View file

@ -0,0 +1,164 @@
"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 getModuleLengths = async ({ id, renderedLength, code, }, useRenderedLength = false) => {
const isCodeEmpty = code == null || code == "";
const result = {
id,
gzipLength: isCodeEmpty ? 0 : await gzipSizeGetter(code),
brotliLength: isCodeEmpty ? 0 : await brotliSizeGetter(code),
renderedLength: useRenderedLength
? renderedLength
: isCodeEmpty
? 0
: 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, code }) => {
return getModuleLengths({ id, renderedLength, code: code.join("") }, true);
}));
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 }]) => getModuleLengths({ id, renderedLength, code }), false));
tree = (0, data_1.buildTree)(bundleId, modules, mapper);
}
if (tree.children.length === 0) {
const bundleSizes = await getModuleLengths({
id: bundleId,
renderedLength: bundle.code.length,
code: bundle.code,
}, false);
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 stringData = (0, module_mapper_1.replaceHashPlaceholders)(data);
const fileContent = await (0, render_template_1.renderTemplate)(template, {
title,
data: stringData,
});
if (opts.emitFile) {
// Regex checks for filenames starting with `./`, `../`, `.\` or `..\`
// to account for windows-style path separators
if (path_1.default.isAbsolute(filename) || /^\.{1,2}[/\\]/.test(filename)) {
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;

View file

@ -0,0 +1,24 @@
import { ModuleMeta, ModuleLengths, ModuleUID, VisualizerData } from "../shared/types";
export declare const getDataHash: (json: string) => string;
export declare const replaceHashPlaceholders: (data: VisualizerData) => string;
export declare class ModuleMapper {
private projectRoot;
private nodeParts;
private nodeMetas;
private counter;
constructor(projectRoot: string | RegExp);
trimProjectRootId(moduleId: string): string;
uniqueId(): ModuleUID;
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;
}

View file

@ -0,0 +1,164 @@
"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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ModuleMapper = exports.replaceHashPlaceholders = exports.getDataHash = void 0;
const crypto = __importStar(require("crypto"));
const HASH_PLACEHOLDER = "!{ROLLUP_VISUALIZER_HASH_PLACEHOLDER}";
const HASH_PLACEHOLDER_REGEXP = new RegExp(`"${HASH_PLACEHOLDER}-(\\d+)"`, "g");
const getDataHash = (json) => {
const hash = crypto.createHash("sha1").update(json).digest("hex");
const hashSub = hash.substring(0, 8);
return hashSub;
};
exports.getDataHash = getDataHash;
const replaceHashPlaceholders = (data) => {
const json = JSON.stringify(data);
const hash = (0, exports.getDataHash)(json);
const jsonWithHash = json.replace(HASH_PLACEHOLDER_REGEXP, (_, num) => `"${hash}-${num}"`);
return jsonWithHash;
};
exports.replaceHashPlaceholders = replaceHashPlaceholders;
class ModuleMapper {
constructor(projectRoot) {
this.projectRoot = projectRoot;
this.nodeParts = {};
this.nodeMetas = {};
this.counter = 0;
}
trimProjectRootId(moduleId) {
if (typeof this.projectRoot === "string" && moduleId.startsWith(this.projectRoot)) {
return moduleId.slice(this.projectRoot.length);
}
return moduleId.replace(this.projectRoot, "");
}
uniqueId() {
return `${HASH_PLACEHOLDER}-${this.counter++}`;
}
getModuleUid(moduleId) {
if (!(moduleId in this.nodeMetas)) {
this.nodeMetas[moduleId] = {
uid: this.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: this.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] = this.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;

View file

@ -0,0 +1,6 @@
import { TemplateType } from "./template-types";
export type RenderTemplateOptions = {
data: string;
title: string;
};
export declare const renderTemplate: (templateType: TemplateType, options: RenderTemplateOptions) => Promise<string>;

View file

@ -0,0 +1,109 @@
"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, data, style);
};
const outputRawData = (strData) => {
const data = JSON.parse(strData);
return JSON.stringify(data, null, 2);
};
const outputPlainTextList = (strData) => {
var _a;
const data = JSON.parse(strData);
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),
flamegraph: buildHtml("flamegraph"),
};
const renderTemplate = (templateType, options) => {
return TEMPLATE_TYPE_RENDERED[templateType](options);
};
exports.renderTemplate = renderTemplate;

View file

@ -0,0 +1,8 @@
import { OutputChunk } from "rollup";
interface SourceMapModuleRenderInfo {
id: string;
renderedLength: number;
code: string[];
}
export declare const getSourcemapModules: (id: string, outputChunk: OutputChunk, dir: string) => Promise<Record<string, SourceMapModuleRenderInfo>>;
export {};

View file

@ -0,0 +1,41 @@
"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;
const codeChars = [...code];
for (let i = 0; i < codeChars.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);
const char = codeChars[i];
modules[id] = modules[id] || { id, renderedLength: 0, code: [] };
modules[id].renderedLength += Buffer.byteLength(char);
modules[id].code.push(char);
}
if (code[i] === "\n") {
line += 1;
column = -1;
}
}
return modules;
};
const getSourcemapModules = (id, outputChunk, dir) => {
if (outputChunk.map == null) {
return Promise.resolve({});
}
return source_map_1.SourceMapConsumer.with(outputChunk.map, null, (map) => {
return getBytesPerFileUsingSourceMap(id, outputChunk.code, map, dir);
});
};
exports.getSourcemapModules = getSourcemapModules;

View file

@ -0,0 +1,3 @@
export type TemplateType = "sunburst" | "treemap" | "network" | "raw-data" | "list" | "flamegraph";
declare const templates: ReadonlyArray<TemplateType>;
export default templates;

View file

@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const templates = [
"sunburst",
"treemap",
"network",
"list",
"raw-data",
"flamegraph",
];
exports.default = templates;

View file

@ -0,0 +1 @@
export declare const version = 2;

View file

@ -0,0 +1,4 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = void 0;
exports.version = 2;

View file

@ -0,0 +1 @@
export declare const warn: (...args: any[]) => void;

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;

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;

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;

View file

@ -0,0 +1 @@
export {};

View file

@ -0,0 +1,106 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const create_filter_1 = require("./create-filter");
(0, vitest_1.describe)("createFilter", () => {
(0, vitest_1.it)("should return true when input and output is empty", () => {
const isIncluded = (0, create_filter_1.createFilter)([], []);
(0, vitest_1.expect)(isIncluded("bundle", "file")).toBe(true);
});
(0, vitest_1.describe)("Bundle", () => {
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
});
(0, vitest_1.describe)("File", () => {
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
});
(0, vitest_1.describe)("File in Bundle", () => {
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
vitest_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, vitest_1.expect)(isIncluded(bundle, file)).toBe(result);
});
});
});

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;
};
}

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;