webpack-bundle-analyzer
Version:
Webpack plugin and CLI utility that represents bundle content as convenient interactive zoomable treemap
164 lines (151 loc) • 4.85 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _Node = _interopRequireDefault(require("./Node.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/** @typedef {import("./Folder").default} Folder */
/** @typedef {import("./Module").default} Module */
/** @typedef {import("./Module").ModuleChartData} ModuleChartData */
/** @typedef {import("./ConcatenatedModule").default} ConcatenatedModule */
/** @typedef {import("./ContentModule").default} ContentModule */
/** @typedef {import("./ContentFolder").default} ContentFolder */
/** @typedef {import("./ContentFolder").ContentFolderChartData} ContentFolderChartData */
/** @typedef {import("./Folder").FolderChartData} FolderChartData */
/**
* @typedef {object} BaseFolderChartData
* @property {string} label label
* @property {string} path path
* @property {number} statSize stat size
* @property {(FolderChartData | ModuleChartData | ContentFolderChartData)[]} groups groups
*/
/** @typedef {Module | ContentModule | ConcatenatedModule | ContentFolder | Folder} Children */
class BaseFolder extends _Node.default {
/**
* @param {string} name name
* @param {Node=} parent parent
*/
constructor(name, parent) {
super(name, parent);
/** @type {Record<string, Children>} */
this.children = Object.create(null);
}
/**
* @returns {string} src
*/
get src() {
if (!Object.hasOwn(this, "_src")) {
this._src = this.walk((node, src) => src += node.src || "", /** @type {string} */"", false);
}
return /** @type {string} */this._src;
}
/**
* @returns {number} size
*/
get size() {
if (!Object.hasOwn(this, "_size")) {
this._size = this.walk((node, size) => size + node.size, /** @type {number} */0, false);
}
return /** @type {number} */this._size;
}
/**
* @param {string} name name
* @returns {Children} child
*/
getChild(name) {
return this.children[name];
}
/**
* @param {Module | ContentModule | ConcatenatedModule} module module
*/
addChildModule(module) {
const {
name
} = module;
const currentChild = this.children[name];
// For some reason we already have this node in children and it's a folder.
if (currentChild && currentChild instanceof BaseFolder) return;
if (currentChild) {
// We already have this node in children and it's a module.
// Merging it's data.
currentChild.mergeData(module.data);
} else {
// Pushing new module
module.parent = this;
this.children[name] = module;
}
delete this._size;
delete this._src;
}
/**
* @param {ContentFolder | Folder} folder folder
* @returns {ContentFolder | Folder} folder
*/
addChildFolder(folder) {
folder.parent = this;
this.children[folder.name] = folder;
delete this._size;
delete this._src;
return folder;
}
/**
* @template T
* @param {(node: Children, state: T, stop: (state: T) => void) => T} walker walker function
* @param {T} state state state
* @param {boolean | ((state: T) => T)=} deep true when need to deep walk, otherwise false
* @returns {T} state
*/
walk(walker, state = (/** @type T */{}), deep = true) {
let stopped = false;
/**
* @param {T} finalState final state
* @returns {T} final state
*/
function stop(finalState) {
stopped = true;
return finalState;
}
for (const child of Object.values(this.children)) {
state = deep && /** @type {BaseFolder} */child.walk ? /** @type {BaseFolder} */child.walk(walker, state, stop) : walker(child, state, stop);
if (stopped) return /** @type {T} */false;
}
return state;
}
mergeNestedFolders() {
if (!this.isRoot) {
let childNames;
while ((childNames = Object.keys(this.children)).length === 1) {
const [childName] = childNames;
const onlyChild = this.children[childName];
if (onlyChild instanceof this.constructor) {
this.name += `/${onlyChild.name}`;
this.children = /** @type {BaseFolder} */onlyChild.children;
} else {
break;
}
}
}
this.walk((child, state) => {
child.parent = this;
if (/** @type {Folder | ContentFolder | ConcatenatedModule} */
child.mergeNestedFolders) {
/** @type {Folder | ContentFolder | ConcatenatedModule} */
child.mergeNestedFolders();
}
return state;
}, null, false);
}
/**
* @returns {BaseFolderChartData} base folder chart data
*/
toChartData() {
return {
label: this.name,
path: this.path,
statSize: this.size,
groups: Object.values(this.children).map(child => child.toChartData())
};
}
}
exports.default = BaseFolder;