Analyze and debug space usage through source maps
—
Structured data format for bundle analysis results, including file size breakdowns, error handling, and formatted output.
The primary result structure returned by the explore function.
interface ExploreResult {
/** Array of successfully analyzed bundles */
bundles: ExploreBundleResult[];
/** Result as a string - either JSON, TSV or HTML */
output?: string;
/** Array of errors and warnings from analysis */
errors: ExploreErrorResult[];
}Usage Examples:
import { explore } from "source-map-explorer";
const result = await explore("dist/bundle.js");
// Access bundle data
console.log(result.bundles[0].totalBytes);
console.log(result.bundles[0].files);
// Handle errors
if (result.errors.length > 0) {
result.errors.forEach(error => {
console.error(`${error.bundleName}: ${error.message}`);
});
}
// Use formatted output if requested
if (result.output) {
console.log(result.output); // JSON, TSV, or HTML string
}Detailed results for each successfully analyzed bundle.
interface ExploreBundleResult {
/** Display name for the bundle */
bundleName: string;
/** Map of source files to their size data */
files: FileDataMap;
/** Total bytes that were successfully mapped to source files */
mappedBytes: number;
/** Bytes that could not be mapped to source files */
unmappedBytes?: number;
/** Bytes consumed by end-of-line characters */
eolBytes: number;
/** Bytes consumed by sourceMappingURL comment */
sourceMapCommentBytes: number;
/** Total size of the bundle file */
totalBytes: number;
}
type FileDataMap = Record<string, FileData>;
interface FileData {
/** Size in bytes contributed by this file */
size: number;
/** Size in bytes covered by code coverage (if coverage data provided) */
coveredSize?: number;
}Usage Examples:
const result = await explore("dist/bundle.js");
const bundle = result.bundles[0];
// Access size information
console.log(`Total bundle size: ${bundle.totalBytes} bytes`);
console.log(`Mapped to sources: ${bundle.mappedBytes} bytes`);
console.log(`Unmapped: ${bundle.unmappedBytes || 0} bytes`);
// Iterate through source files
Object.entries(bundle.files).forEach(([filename, data]) => {
console.log(`${filename}: ${data.size} bytes`);
if (data.coveredSize !== undefined) {
console.log(` Coverage: ${data.coveredSize}/${data.size} bytes`);
}
});
// Find largest contributors
const sortedFiles = Object.entries(bundle.files)
.sort(([,a], [,b]) => b.size - a.size)
.slice(0, 10);
console.log("Top 10 largest files:");
sortedFiles.forEach(([filename, data]) => {
console.log(` ${filename}: ${data.size} bytes`);
});Error information for bundles that could not be analyzed successfully.
interface ExploreErrorResult {
/** Name of the bundle that failed */
bundleName: string;
/** Error code identifier */
code: string;
/** Human-readable error message */
message: string;
/** Original error object if available */
error?: NodeJS.ErrnoException;
/** True if this is a warning rather than a fatal error */
isWarning?: boolean;
}Usage Examples:
const result = await explore(["good-bundle.js", "bad-bundle.js"]);
// Separate errors from warnings
const errors = result.errors.filter(e => !e.isWarning);
const warnings = result.errors.filter(e => e.isWarning);
// Handle fatal errors
errors.forEach(error => {
console.error(`ERROR [${error.code}] ${error.bundleName}: ${error.message}`);
if (error.error) {
console.error(" Caused by:", error.error.message);
}
});
// Handle warnings
warnings.forEach(warning => {
console.warn(`WARNING [${warning.code}] ${warning.bundleName}: ${warning.message}`);
});
// Check if any bundles succeeded
if (result.bundles.length === 0) {
console.error("No bundles were successfully analyzed");
process.exit(1);
}Analysis results include special keys for different types of content:
// Special filename constants used in results
const UNMAPPED_KEY = "[unmapped]"; // Bytes not mapped to source files
const SOURCE_MAP_COMMENT_KEY = "[sourceMappingURL]"; // Source map comment bytes
const NO_SOURCE_KEY = "[no source]"; // Mapped bytes without source info
const EOL_KEY = "[EOLs]"; // End-of-line character bytesUsage Examples:
import { explore, UNMAPPED_KEY, SOURCE_MAP_COMMENT_KEY } from "source-map-explorer";
const result = await explore("bundle.js");
const files = result.bundles[0].files;
// Check for unmapped bytes
if (files[UNMAPPED_KEY]) {
console.log(`Unmapped bytes: ${files[UNMAPPED_KEY].size}`);
}
// Check source map comment size
if (files[SOURCE_MAP_COMMENT_KEY]) {
console.log(`Source map comment: ${files[SOURCE_MAP_COMMENT_KEY].size} bytes`);
}
// Filter out special keys to get only source files
const sourceFiles = Object.entries(files).filter(([filename]) =>
!filename.startsWith("[") || !filename.endsWith("]")
);
console.log(`Found ${sourceFiles.length} source files`);Structured data format for programmatic processing.
const result = await explore("bundle.js", {
output: { format: "json" }
});
// result.output contains JSON string
const data = JSON.parse(result.output);
console.log(data.results[0].files);JSON Structure:
{
"results": [
{
"bundleName": "bundle.js",
"totalBytes": 12345,
"mappedBytes": 11000,
"unmappedBytes": 1000,
"eolBytes": 200,
"sourceMapCommentBytes": 145,
"files": {
"src/main.js": { "size": 2500 },
"src/utils.js": { "size": 1200 },
"node_modules/lodash/index.js": { "size": 7300 },
"[unmapped]": { "size": 1000 }
}
}
]
}Tab-separated values format for spreadsheet import and analysis.
const result = await explore("bundle.js", {
output: { format: "tsv" }
});
// result.output contains TSV string
console.log(result.output);TSV Format:
Source Size
src/main.js 2500
node_modules/lodash/index.js 7300
src/utils.js 1200
[unmapped] 1000
[sourceMappingURL] 145Interactive treemap visualization for visual analysis.
const result = await explore("bundle.js", {
output: { format: "html" }
});
// result.output contains complete HTML document
fs.writeFileSync("analysis.html", result.output);The HTML output includes:
When Chrome DevTools coverage data is provided, results include coverage information:
const result = await explore("bundle.js", {
coverage: "coverage.json"
});
// FileData includes coveredSize when coverage is available
const files = result.bundles[0].files;
Object.entries(files).forEach(([filename, data]) => {
if (data.coveredSize !== undefined) {
const coverage = (data.coveredSize / data.size * 100).toFixed(1);
console.log(`${filename}: ${coverage}% covered`);
}
});Coverage data affects:
coveredSize propertyInstall with Tessl CLI
npx tessl i tessl/npm-source-map-explorer