Guide
xMap
is a TypeScript library for working with source maps, stack trace parsing, and code formatting. It provides powerful tools for debugging, error reporting, and code visualization in CLI environments.
Features
- Source Map Processing: Parse, manipulate, and query source maps
- Stack Trace Parsing: Parse error stacks from V8, SpiderMonkey, and JavaScriptCore engines
- Code Formatting: Display code with line numbers and custom highlighting
- Syntax Highlighting: Semantic TypeScript code highlighting with customizable themes
- Error Visualization: Format code snippets with error indicators
Installation
To install the package, use npm or yarn:
npm install @remotex-labs/xmap
or
yarn add @remotex-labs/xmap
Optimizing Bundle Size
xMap supports subpath imports, allowing you to import only the specific components you need:
// Import only what you need
import { parseErrorStack } from '@remotex-labs/xmap/parser.component';
import { formatCode } from '@remotex-labs/xmap/formatter.component';
import { highlightCode } from '@remotex-labs/xmap/highlighter.component';
import { SourceService } from '@remotex-labs/xmap';
Key Components
SourceService
Process and query source maps to get original positions, concatenate maps, and retrieve code snippets.
import { SourceService, Bias } from '@remotex-labs/xmap';
// Create from JSON string
const sourceService = new SourceService(sourceMapJSON, 'bundle.js');
// Get original position for generated code
const originalPosition = sourceService.getPositionByGenerated(12, 34);
// Get position with code context
const positionWithCode = sourceService.getPositionWithCode(12, 34, Bias.LOWER_BOUND, {
linesBefore: 2,
linesAfter: 2
});
Understanding Bias
When querying source maps, the parameter controls how positions are matched: Bias
Bias.BOUND
- No directional preference; returns the first match foundBias.LOWER_BOUND
- Prefers segments with column values ≤ the targetBias.UPPER_BOUND
- Prefers segments with column values ≥ the targetBias.UPPER_BOUND
Example:
// Using different bias values for position lookup
const exactPosition = sourceService.getPosition(10, 15, Bias.BOUND);
const beforePosition = sourceService.getPosition(10, 15, Bias.LOWER_BOUND);
const afterPosition = sourceService.getPosition(10, 15, Bias.UPPER_BOUND);
Stack Trace Parser
Parse error stack traces from different JavaScript engines into a structured format.
import { parseErrorStack } from '@remotex-labs/xmap/parser.component';
try {
throw new Error('Example error');
} catch (error) {
const parsedStack = parseErrorStack(error);
console.log(parsedStack.name); // "Error"
console.log(parsedStack.message); // "Example error"
// Access the first stack frame
const frame = parsedStack.stack[0];
console.log(frame.fileName); // File where error occurred
console.log(frame.line); // Line number
console.log(frame.functionName); // Function name
}
Code Highlighter
Apply semantic syntax highlighting to TypeScript code.
import { highlightCode } from '@remotex-labs/xmap/highlighter.component';
const code = `
function sum(a: number, b: number): number {
return a + b;
}
`;
// Use default color scheme
const highlightedCode = highlightCode(code);
// Or customize with your own color scheme
const customScheme = {
keywordColor: (text: string): string => `\x1b[36m${ text }\x1b[0m`, // Blue for keywords
stringColor: (text: string): string => `\x1b[32m${ text }\x1b[0m`, // Blue for keywords
numberColor: (text: string): string => `\x1b[31m${ text }\x1b[0m` // Blue for keywords
};
const customHighlightedCode = highlightCode(code, customScheme);
console.log(customHighlightedCode)
formatCode
The formatCode
function formats a given code snippet, adding line numbers with customizable padding and enabling specific actions for particular lines. This utility is useful for displaying code snippets in a user-friendly manner, particularly in documentation or debugging scenarios.
import { formatCode } from '@remotex-labs/xmap/formatter.component';
import { highlightCode } from '@remotex-labs/xmap/highlighter.component';
const code = `
function greet(name: string) {
console.log('Hello, ' + name);
}
greet('World');
`;
// Format with custom options
const formattedCode = formatCode(highlightCode(code), {
padding: 8, // Padding for line numbers
startLine: 1, // Starting line number
action: {
triggerLine: 3, // Line to apply custom formatting
callback: (lineString, padding, lineNumber) => {
return `*** Line ${ lineNumber } ***\n${ lineString }`;
}
}
});
console.log(formattedCode);
import { formatErrorCode } from '@remotex-labs/xmap/formatter.component';
const sourcePosition = {
code: 'function divide(a, b) {\n return a / b;\n}',
line: 2,
column: 13,
startLine: 1,
endLine: 0,
name: null,
source: '',
sourceRoot: null,
sourceIndex: 0,
generatedLine: 0,
generatedColumn: 0
};
// Format with error indicator
const formattedError = formatErrorCode(sourcePosition, {
color: (text) => `\x1b[31m${ text }\x1b[0m` // Red color for error indicator
});
console.log(formattedError)
Practical Examples
Working with Source Maps and Errors
import { SourceService, Bias } from '@remotex-labs/xmap';
import { parseErrorStack } from '@remotex-labs/xmap/parser.component';
import { formatErrorCode } from '@remotex-labs/xmap/formatter.component';
import { highlightCode } from '@remotex-labs/xmap/highlighter.component';
try {
// Code that throws an error
throw new Error('Something went wrong');
} catch (error) {
// Parse the error stack
const parsedStack = parseErrorStack(error);
// Get the top frame
const frame = parsedStack.stack[0];
if (frame.fileName && frame.line && frame.column) {
// Initialize source service with your source map
const sourceService = new SourceService(sourceMapJSON);
// Get original position with code
const position = sourceService.getPositionWithCode(
frame.line,
frame.column,
Bias.LOWER_BOUND,
{ linesBefore: 2, linesAfter: 2 }
);
if (position) {
// Apply syntax highlighting
position.code = highlightCode(position.code);
// Format with error indicator
const formattedError = formatErrorCode(position, {
color: (text) => `\x1b[31m${ text }\x1b[0m`
});
console.log('Error occurred:');
console.log(formattedError);
}
}
}
import { SourceService } from '@remotex-labs/xmap';
const sourceMapJSON = `
{
"version": 3,
"sources": ["../src/core/core.component.ts", "../src/index.ts"],
"sourceRoot": "https://github.com/remotex-lab/xmap/tree/test/",
"sourcesContent": ["export class CoreModule {\\r\\n private name: string;\\r\\n\\r\\n constructor(name: string) {\\r\\n this.name = name;\\r\\n }\\r\\n\\r\\n public greet(): string {\\r\\n return \`Hello from \${ this.name }!\`;\\r\\n }\\r\\n}", "import { CoreModule } from '@core/core.component';\\r\\n\\r\\nconst coreInstance = new CoreModule('Core Module');\\r\\n\\r\\nconsole.log(coreInstance.greet());"],
"mappings": "aAAO,IAAMA,EAAN,KAAiB,CACZ,KAER,YAAYC,EAAc,CACtB,KAAK,KAAOA,CAChB,CAEO,OAAgB,CACnB,MAAO,cAAc,KAAK,IAAI,GAClC,CACJ,ECRA,IAAMC,EAAe,IAAIC,EAAW,aAAa,EAEjD,QAAQ,IAAID,EAAa,MAAM,CAAC",
"names": ["CoreModule", "name", "coreInstance", "CoreModule"]
}
`;
const sourceService = new SourceService(sourceMapJSON, 'bundle.js');
console.log(sourceService);
const position = sourceService.getPositionByOriginal(3, 7, 'index.ts');
console.log(position);
const positionWithCode = sourceService.getPositionWithCode(1, 104, 1, { linesBefore: 2, linesAfter: 2 });
console.log(positionWithCode);