@remotex-labs/xmap
Version:
A library with a sourcemap parser and TypeScript code formatter for the CLI
1,204 lines (1,197 loc) • 34.8 kB
JavaScript
// src/components/path.component.ts
import * as p from "path";
import * as h from "path/posix";
var P = /\\/g, k = /\/+/g;
function g(n) {
if (!n) return "";
let e = String(n);
return e = e.replace(P, "/"), e = e.replace(k, "/"), e;
}
function F(n) {
return h.normalize(g(n));
}
function Q(...n) {
return h.join(...n.map(g));
}
function y(...n) {
return g(p.resolve(...n));
}
function b(n) {
return g(p.dirname(n));
}
function L(n, e) {
return h.relative(g(n), g(e)) || ".";
}
function T(n, e) {
return g(p.basename(n, e));
}
// src/components/base64.component.ts
var B = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", $ = /* @__PURE__ */ new Uint8Array([
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
255,
62,
255,
255,
255,
63,
// + /
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
255,
255,
255,
255,
255,
255,
// 0-9
255,
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
// A-O
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
255,
255,
255,
255,
255,
// P-Z
255,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
// a-o
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
255,
255,
255,
255,
255
// p-z
]);
function U(n) {
let e = n < 0 ? -n * 2 | 1 : n * 2, r = "";
do {
let t = e & 31;
e >>>= 5, r += B[e > 0 ? t | 32 : t];
} while (e > 0);
return r;
}
function A(n) {
return n.map(U).join("");
}
function I(n) {
let e = [], r = 0, t = 0;
for (let o = 0; o < n.length; o++) {
let s = n.charCodeAt(o), i = s < 128 ? $[s] : 255;
if (i === 255)
throw new Error(`Invalid Base64 character: '${n[o]}' at index ${o}`);
t += (i & 31) << r, i & 32 ? r += 5 : (e.push(t & 1 ? -(t >> 1) : t >> 1), t = r = 0);
}
if (r > 0)
throw new Error("Unexpected end of VLQ input: incomplete sequence");
return e;
}
// src/components/segment.component.ts
var w = /* @__PURE__ */ ((t) => (t[t.BOUND = 0] = "BOUND", t[t.LOWER_BOUND = 1] = "LOWER_BOUND", t[t.UPPER_BOUND = 2] = "UPPER_BOUND", t))(w || {});
function v(n = 0, e = 0) {
return {
line: 0,
column: 0,
nameIndex: n,
sourceIndex: e,
generatedLine: 0,
generatedColumn: 0
};
}
function O(n, e) {
let r = e[0], t = e[1], o = e[2], s = e[3], i = e[4];
return n.line += o ?? 0, n.column += s ?? 0, n.sourceIndex += t ?? 0, n.generatedColumn += r, i !== void 0 && (n.nameIndex += i), {
// +1 converts 0-based VLQ accumulator → 1-based public API
line: n.line + 1,
column: n.column + 1,
generatedLine: n.generatedLine + 1,
generatedColumn: n.generatedColumn + 1,
sourceIndex: n.sourceIndex,
nameIndex: i !== void 0 ? n.nameIndex : null
};
}
function _(n, e) {
return O(n, I(e));
}
function C(n, e) {
let r = e.line - 1, t = e.column - 1, o = e.generatedColumn - 1, s = [
o - n.generatedColumn,
// Δ generatedColumn
e.sourceIndex - n.sourceIndex,
// Δ sourceIndex
r - n.line,
// Δ sourceLine
t - n.column
// Δ sourceColumn
];
return e.nameIndex !== null && e.nameIndex !== void 0 && (s[4] = e.nameIndex - n.nameIndex, n.nameIndex = e.nameIndex), n.generatedColumn = o, n.sourceIndex = e.sourceIndex, n.line = r, n.column = t, A(s);
}
function M(n) {
let e = ["line", "column", "generatedLine", "generatedColumn"];
for (let r of e) {
let t = n[r];
if (!Number.isFinite(t) || t < 1)
throw new Error(`Invalid segment: '${r}' must be a finite number \u2265 1, received ${t}`);
}
if (!Number.isFinite(n.sourceIndex))
throw new Error(`Invalid segment: 'sourceIndex' must be a finite number, received ${n.sourceIndex}`);
if (n.nameIndex !== null && !Number.isFinite(n.nameIndex))
throw new Error(`Invalid segment: 'nameIndex' must be a finite number or null, received ${n.nameIndex}`);
}
// src/services/source.service.ts
import { cwd as D } from "process";
// src/services/mapping.service.ts
var S = class n {
/**
* The generated file this source map is associated with.
*
* @remarks
* Optional field specifying the filename of the generated code that this
* source map describes. This is typically a relative path.
*
* Can be `null` or omitted if the filename is not relevant or is provided
* through other means (e.g., HTTP headers, file naming conventions).
*
* @example
* ```ts
* file: 'bundle.min.js'
* ```
*
* @since 5.0.0
*/
lines = [];
/**
* List of identifier names referenced in the mappings.
*
* @remarks
* Optional array containing all identifier names (variables, functions, etc.)
* that are referenced by segments with a `nameIndex`. These names correspond
* to their positions in the original source code.
*
* Segments can reference these names by index to indicate which identifier
* in the original code corresponds to a position in the generated code.
*
* Omit this field if no names need to be tracked, or provide an empty array.
*
* @example
* ```ts
* names: ['myFunction', 'result', 'value']
* ```
*
* @since 5.0.0
*/
offsets = {
line: 0,
name: 0,
sources: 0
};
/**
* Gets the current line offset used for coordinate adjustments.
*
* @returns The line offset value
*
* @remarks
* This offset is applied when decoding mappings to shift generated line numbers.
* Useful when merging multiple source maps where line numbers need adjustment.
*
* @since 5.0.0
*/
get lineOffset() {
return this.offsets.line;
}
/**
* Gets the current name index offset used for coordinate adjustments.
*
* @returns The name index offset value
*
* @remarks
* This offset is applied when decoding mappings to shift name indices.
* Necessary when concatenating source maps with different name arrays.
*
* @since 5.0.0
*/
get namesOffset() {
return this.offsets.name;
}
/**
* Gets the current source file index offset used for coordinate adjustments.
*
* @returns The source index offset value
*
* @remarks
* This offset is applied when decoding mappings to shift source file indices.
* Required when merging source maps with different source file arrays.
*
* @since 5.0.0
*/
get sourcesOffset() {
return this.offsets.sources;
}
/**
* Encodes all stored mappings to a Source Map v3 VLQ string.
*
* @returns A Base64 VLQ-encoded mappings string
*
* @remarks
* Converts the internal 1-based segment representation to 0-based VLQ deltas
* and encodes them as a semicolon-separated string where:
* - Semicolons (`;`) separate lines
* - Commas (`,`) separate segments within a line
* - Empty frames are represented as empty strings between semicolons
*
* The encoding process:
* 1. Resets column offset at each line boundary
* 2. Computes deltas relative to a previous segment
* 3. Encodes deltas using Base64 VLQ
*
* @example Encoding mappings
* ```ts
* const service = new MappingService();
* service.decode(mappingArray);
* const vlqString = service.encode();
* // Returns: "AAAA;AACA,AADA;AAGA;"
* ```
*
* @since 5.0.0
*/
encode() {
return this.encodeSourceMapping(this.lines);
}
/**
* Decodes and stores source map data from various input formats.
*
* @param mapping - Source map data (VLQ string, array, or MappingService instance)
* @param namesOffset - Offset to apply to name indices (default: 0)
* @param sourcesOffset - Offset to apply to source file indices (default: 0)
* @param lineOffset - Offset to apply to generated line numbers (default: 0)
*
* @throws Error - If the mapping format is invalid or contains malformed data
*
* @remarks
* Accepts three input formats:
* - **VLQ string**: Standard Source Map v3 encoded mappings
* - **Array**: Structured {@link SourceMappingType} with segments
* - **MappingService**: Another service instance (copies its lines)
*
* Offsets are useful when merging multiple source maps, allowing index
* adjustment to prevent conflicts in name and source arrays.
*
* The method appends to existing mappings, so calling it multiple times
* will accumulate all decoded segments.
*
* @example Decoding from string
* ```ts
* const service = new MappingService();
* service.decode('AAAA;AACA;AADA;');
* ```
*
* @example Decoding with offsets
* ```ts
* const service = new MappingService();
* service.decode(mappingArray, 5, 3, 10);
* // All name indices += 5, source indices += 3, generated lines += 10
* ```
*
* @example Decoding from another service
* ```ts
* const source = new MappingService();
* source.decode('AAAA;');
* const target = new MappingService();
* target.decode(source);
* ```
*
* @since 5.0.0
*/
decode(e, r = 0, t = 0, o = 0) {
this.offsets.line = o, this.offsets.name = r, this.offsets.sources = t;
let s = e instanceof n ? e.lines : e;
Array.isArray(s) ? this.decodeSourceMappingArray(s) : this.decodeSourceMappingString(s);
}
/**
* Finds a segment at the specified generated position using binary search.
*
* @param generatedLine - 1-based generated line number
* @param generatedColumn - 1-based generated column number
* @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND})
*
* @returns The matching segment, or `null` if no suitable segment exists
*
* @remarks
* Uses binary search for O(log n) performance on sorted segment arrays.
*
* Bias behavior:
* - **`BOUND`**: Returns exact matches only
* - **`LOWER_BOUND`**: Returns the largest segment ≤ target column
* - **`UPPER_BOUND`**: Returns the smallest segment ≥ target column
*
* The line index is adjusted by the current {@link lineOffset} to support
* merged source maps with shifted line numbers.
*
* @example Finding exact segment
* ```ts
* const service = new MappingService();
* service.decode(mappings);
* const segment = service.getSegment(1, 5);
* // Returns a segment at line 1, column 5, or null
* ```
*
* @example Finding nearest lower bound
* ```ts
* const segment = service.getSegment(1, 7, Bias.LOWER_BOUND);
* // Returns the closest segment at or before column 7
* ```
*
* @see {@link Bias} for bias options
* @see {@link getOriginalSegment} for reverse lookups
*
* @since 5.0.0
*/
getSegment(e, r, t = 0) {
let o = e - this.offsets.line - 1, s = this.lines[o];
if (!s || s.length === 0) return null;
let i = 0, c = s.length - 1, l = null;
for (; i <= c; ) {
let a = i + c >>> 1, u = s[a];
if (u.generatedColumn < r)
i = a + 1, t === 1 && (l = u);
else if (u.generatedColumn > r)
c = a - 1, t === 2 && (l = u);
else
return u;
}
return l;
}
/**
* Finds a segment at the specified original source position using linear search.
*
* @param sourceLine - 1-based original source line number
* @param sourceColumn - 1-based original source column number
* @param sourceIndex - Source file index
* @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND})
*
* @returns The matching segment, or `null` if no suitable segment exists
*
* @remarks
* Performs exhaustive linear search across all segments to find matches
* by original position. For frequent lookups, consider building an index
* first using {@link buildOriginalPositionIndex}.
*
* Returns immediately on the exact column match. For bias modes, tracks the
* closest segment by computing column distance.
*
* Bias behavior:
* - **`BOUND`**: Returns exact matches only
* - **`LOWER_BOUND`**: Returns the closest segment with column ≤ target
* - **`UPPER_BOUND`**: Returns the closest segment with column ≥ target
*
* @example Finding original segment
* ```ts
* const service = new MappingService();
* service.decode(mappings);
* const segment = service.getOriginalSegment(10, 25, 0);
* // Returns segment mapping to source file 0, line 10, column 25
* ```
*
* @example Finding the nearest original position
* ```ts
* const segment = service.getOriginalSegment(10, 25, 0, Bias.LOWER_BOUND);
* // Returns the closest segment at or before column 25
* ```
*
* @see {@link Bias} for bias options
* @see {@link getSegment} for generated position lookups
* @see {@link buildOriginalPositionIndex} for optimized repeated lookups
*
* @since 5.0.0
*/
getOriginalSegment(e, r, t, o = 0) {
let s = null, i = 1 / 0;
for (let c = 0; c < this.lines.length; c++) {
let l = this.lines[c];
if (l)
for (let a = 0; a < l.length; a++) {
let u = l[a];
if (u.sourceIndex !== t || u.line !== e) continue;
let f = u.column;
if (f === r) return u;
if (o === 1) {
if (f < r) {
let m = r - f;
m < i && (i = m, s = u);
}
} else if (o === 2 && f > r) {
let m = f - r;
m < i && (i = m, s = u);
}
}
}
return s;
}
/**
* Builds an indexed map of segments grouped by original source position.
*
* @returns A map keyed by `"sourceIndex:line"` with sorted segment arrays
*
* @remarks
* Creates a lookup structure for efficient reverse mapping from original
* to generated positions. Each bucket contains all segments that map to
* the same source file and line, sorted by column in ascending order.
*
* The index structure:
* - **Keys**: Formatted as `"sourceIndex:line"` (e.g., `"0:1"`, `"2:15"`)
* - **Values**: Arrays of {@link SegmentInterface} sorted by `column`
*
* This is useful when performing many original position lookups, as it
* avoids the O(n) linear search of {@link getOriginalSegment}.
*
* Null/empty lines are safely skipped during index construction.
*
* @example Building and using index
* ```ts
* const service = new MappingService();
* service.decode(mappings);
* const index = service.buildOriginalPositionIndex();
*
* const segments = index.get('0:10');
* // Returns all segments mapping to source file 0, line 10
* ```
*
* @example Binary search on indexed bucket
* ```ts
* const index = service.buildOriginalPositionIndex();
* const bucket = index.get('0:10') || [];
* // Perform binary search on bucket for specific column
* ```
*
* @see {@link getOriginalSegment} for single lookups
*
* @since 5.0.0
*/
buildOriginalPositionIndex() {
let e = /* @__PURE__ */ new Map();
for (let r = 0; r < this.lines.length; r++) {
let t = this.lines[r];
if (t)
for (let o = 0; o < t.length; o++) {
let s = t[o], i = `${s.sourceIndex}:${s.line}`, c = e.get(i);
c || (c = [], e.set(i, c)), c.push(s);
}
}
for (let r of e.values())
r.sort((t, o) => t.column - o.column);
return e;
}
/**
* Encodes a structured source mapping array to a VLQ string.
*
* @param sourceMapping - Array of segment lines to encode
*
* @returns A Base64 VLQ-encoded mappings string
*
* @remarks
* Converts 1-based segment positions to 0-based VLQ deltas and encodes
* them according to Source Map v3 specification.
*
* The encoding process:
* 1. Pre-allocates arrays for optimal performance
* 2. Resets `generatedColumn` offset to 0 at each line boundary
* 3. Encodes each segment as deltas from the previous segment
* 4. Joins segments with commas (`,`) and lines with semicolons (`;`)
*
* Null lines are encoded as empty strings (represented by consecutive
* semicolons in the output).
*
* This method never reads the `generatedLine` field from segments, as
* line boundaries are encoded structurally via semicolons.
*
* @example Internal encoding
* ```ts
* const encoded = this.encodeSourceMapping([
* [{ generatedColumn: 1, sourceIndex: 0, line: 1, column: 1, nameIndex: null, generatedLine: 1 }],
* null,
* [{ generatedColumn: 5, sourceIndex: 0, line: 2, column: 5, nameIndex: null, generatedLine: 3 }]
* ]);
* // Returns: "AAAA;;IAIA"
* ```
*
* @see {@link encode} for public API
* @see {@link encodeSegment} for single segment encoding
*
* @since 5.0.0
*/
encodeSourceMapping(e) {
let r = v(), t = new Array(e.length);
for (let o = 0; o < e.length; o++) {
let s = e[o];
if (r.generatedColumn = 0, !s) {
t[o] = "";
continue;
}
let i = new Array(s.length);
for (let c = 0; c < s.length; c++)
i[c] = C(r, s[c]);
t[o] = i.join(",");
}
return t.join(";");
}
/**
* Validates that a string contains only valid VLQ characters.
*
* @param raw - String to validate
*
* @returns `true` if the string is valid, `false` otherwise
*
* @remarks
* Checks that the input contains only characters from the Source Map v3
* VLQ alphabet plus structural separators:
* - Base64 VLQ characters: `A-Z`, `a-z`, `0-9`, `+`, `/`
* - Separators: `,` (segments), `;` (lines)
*
* Empty strings are considered invalid.
*
* @example Valid mapping string
* ```ts
* this.validateSourceMappingString('AAAA;AACA,AADA;')
* // Returns: true
* ```
*
* @example Invalid mapping string
* ```ts
* this.validateSourceMappingString('AAAA;A#A;')
* // Returns: false (contains '#')
* ```
*
* @since 5.0.0
*/
validateSourceMappingString(e) {
return /^[a-zA-Z0-9+/,;]+$/.test(e);
}
/**
* Decodes a structured source mapping array and appends segments to internal state.
*
* @param sourceMapping - Array of segment lines or null
*
* @throws Error - If the array structure is invalid or segments fail validation
*
* @remarks
* Processes a pre-parsed {@link SourceMappingType} array, applying configured
* offsets to all indices and positions.
*
* The decoding process:
* 1. Validates that input is an array
* 2. Pre-computes combined line shift for performance
* 3. Validates each segment using {@link validateSegment}
* 4. Applies name, source, and line offsets to all coordinates
* 5. Appends decoded segments to an internal lines array
*
* Null lines are preserved as-is. Each segment is validated before the offset
* application to ensure data integrity.
*
* Error messages include 1-based line numbers for easier debugging.
*
* @example Internal array decoding
* ```ts
* this.decodeSourceMappingArray([
* [{ line: 1, column: 1, nameIndex: null, sourceIndex: 0, generatedLine: 1, generatedColumn: 1 }],
* null,
* [{ line: 2, column: 5, nameIndex: 0, sourceIndex: 0, generatedLine: 3, generatedColumn: 3 }]
* ]);
* ```
*
* @see {@link decode} for public API
* @see {@link validateSegment} for validation rules
*
* @since 5.0.0
*/
decodeSourceMappingArray(e) {
if (!Array.isArray(e))
throw new Error("Invalid source mapping: expected an array of lines.");
let r = this.lines.length, { name: t, sources: o, line: s } = this.offsets, i = r + s;
for (let c = 0; c < e.length; c++) {
let l = e[c];
if (!l) {
this.lines.push(l);
continue;
}
if (!Array.isArray(l))
throw new Error(`Invalid source mapping at line ${c + 1}: expected an array of segments, got ${typeof l}.`);
try {
let a = new Array(l.length);
for (let u = 0; u < l.length; u++) {
let f = l[u];
M(f), a[u] = {
...f,
nameIndex: typeof f.nameIndex == "number" ? f.nameIndex + t : null,
sourceIndex: f.sourceIndex + o,
generatedLine: f.generatedLine + i
};
}
this.lines.push(a);
} catch (a) {
throw new Error(`Error decoding mappings at line ${c + 1}: ${a instanceof Error ? a.message : String(a)}`);
}
}
}
/**
* Decodes a VLQ-encoded mapping string and appends segments to internal state.
*
* @param encodedMapping - Base64 VLQ-encoded mappings string
*
* @throws Error - If the string format is invalid or decoding fails
*
* @remarks
* Parses a Source Map v3 VLQ string into structured segments with 1-based
* positions, applying configured offsets.
*
* The decoding process:
* 1. Validates a character set using {@link validateSourceMappingString}
* 2. Splits string by semicolons (`;`) to separate lines
* 3. Pre-computes offset flags to minimize branching in hot loop
* 4. For each line:
* - Resets `generatedColumn` offset to 0
* - Splits by commas (`,`) to separate segments
* - Decodes VLQ deltas using {@link decodeVLQ}
* - Applies offsets (only allocating new objects when needed)
* 5. Appends decoded segments to an internal lines array
*
* Empty lines (consecutive semicolons) are stored as `null`.
*
* Line offset is only applied when non-zero, avoiding unnecessary object
* allocations for the common case.
*
* Error messages include 1-based line numbers for debugging.
*
* @example Internal string decoding
* ```ts
* this.decodeSourceMappingString('AAAA;AACA,AADA;AAGA;');
* ```
*
* @see {@link decode} for public API
* @see {@link decodeVLQ} for VLQ decoding
* @see {@link decodeSegment} for delta application
*
* @since 5.0.0
*/
decodeSourceMappingString(e) {
if (!this.validateSourceMappingString(e))
throw new Error("Invalid mappings string: contains characters outside the VLQ alphabet.");
let r = e.split(";"), t = this.lines.length, { name: o, sources: s, line: i } = this.offsets, c = v(o, s), l = i !== 0;
for (let a = 0; a < r.length; a++) {
let u = r[a];
if (!u) {
this.lines.push(null);
continue;
}
c.generatedColumn = 0, c.generatedLine = t + a;
let f = u.split(","), m = new Array(f.length);
try {
for (let d = 0; d < f.length; d++) {
let x = O(c, I(f[d]));
m[d] = l ? { ...x, generatedLine: x.generatedLine + i } : x;
}
} catch (d) {
throw new Error(`Error decoding mappings at line ${a + 1}: ${d.message}`);
}
this.lines.push(m);
}
}
};
// src/services/source.service.ts
var E = class n {
/**
* Normalized generated file path associated with this source map.
* @since 5.0.0
*/
file = "";
/**
* Identifier names referenced by mapped segments.
* @since 5.0.0
*/
names = [];
/**
* Resolved source file paths used by the map.
* @since 5.0.0
*/
sources = [];
/**
* Optional source root from the source map payload.
* @since 5.0.0
*/
sourceRoot = "";
/**
* Inline source contents aligned with {@link sources} by index.
* @since 5.0.0
*/
sourcesContent = [];
/**
* Internal mappings handler for encode/decode and lookups.
* @since 5.0.0
*/
mappings = new S();
/**
* Creates a new SourceService from a source map object or JSON string.
*
* @param source - Source map payload as object or JSON string
* @param fileOrOffset - Generated file path override, or generated line offset when numeric
* @param offset - Generated line offset when `fileOrOffset` is a file path
*
* @throws Error - If file path is missing after normalization
* @throws Error - If source map input is invalid or unsupported
*
* @since 5.0.0
*/
constructor(e, r, t) {
if (!e) return;
let o = n.parseSourceMap(e), s = typeof r == "string" ? r : o.file;
if (!s)
throw new Error("File Path not set for the sourcemap");
this.file = y(s);
let i = typeof r == "number" ? r : t ?? 0;
this.mappings.decode(o.mappings, 0, 0, i), this.sources.push(...this.resolveSources(o.sources)), o.names && this.names.push(...o.names), o.sourceRoot && (this.sourceRoot = o.sourceRoot), o.sourcesContent && this.sourcesContent.push(...o.sourcesContent);
}
/**
* Concatenates multiple source maps into one service instance.
*
* @param sources - Source services to merge in order
*
* @returns A new service containing merged mappings, names, sources, and contents
*
* @throws Error - If no source services are provided
*
* @remarks
* Name and source indices are offset during decode to preserve index
* correctness across all merged source maps.
*
* @example
* ```ts
* const merged = SourceService.assign(sourceA, sourceB, ...);
* ```
*
* @since 5.0.0
*/
static assign(...e) {
if (e.length < 1)
throw new Error("At least one source-map must be provided for assign.");
let r = new n();
for (let t of e)
r.mappings.decode(t.mappings, r.names.length, r.sources.length), r.names.push(...t.names), r.sources.push(...t.sources.map((o) => this.isURL(o) ? o : t.sourceRoot + o)), r.sourcesContent.push(...t.sourcesContent);
return r;
}
/**
* Serializes current data to a Source Map v3 object.
*
* @returns A source map object with encoded mappings
*
* @remarks
* Includes optional `file` and `sourceRoot` only when they are set.
*
* @example
* ```ts
* const map = source.getSourceObject();
* ```
*
* @since 5.0.0
*/
getSourceObject() {
let e = {
version: 3,
file: this.file,
names: this.names,
sources: this.sources,
mappings: this.mappings.encode(),
sourcesContent: this.sourcesContent
};
return this.sourceRoot && (e.sourceRoot = this.sourceRoot), e;
}
/**
* Resolves an original position from a generated position.
*
* @param line - 1-based generated line number
* @param column - 1-based generated column number
* @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND})
*
* @returns Position details, or `null` when no segment matches
*
* @remarks
* The returned `name` can be `null` when the mapped segment has no
* associated name index.
*
* @example
* ```ts
* const pos = source.getPosition(12, 8);
* ```
*
* @since 5.0.0
*/
getPosition(e, r, t = 0) {
let o = this.mappings.getSegment(e, r, t);
return o ? {
name: this.names[o.nameIndex ?? -1] ?? null,
line: o.line,
column: o.column,
source: this.sources[o.sourceIndex],
sourceRoot: this.sourceRoot,
sourceIndex: o.sourceIndex,
generatedLine: o.generatedLine,
generatedColumn: o.generatedColumn
} : null;
}
/**
* Resolves an original position and includes full source content for that file.
*
* @param line - 1-based generated line number
* @param column - 1-based generated column number
* @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND})
*
* @returns Position details with source content, or `null` when unavailable
*
* @remarks
* Returns `null` if no mapped segment is found for the generated position.
*
* @example
* ```ts
* const pos = source.getPositionWithContent(12, 8);
* ```
*
* @since 5.0.0
*/
getPositionWithContent(e, r, t = 0) {
let o = this.getPosition(e, r, t);
return o ? {
...o,
sourcesContent: this.sourcesContent[o.sourceIndex]
} : null;
}
/**
* Resolves an original position and includes surrounding code context.
*
* @param line - 1-based generated line number
* @param column - 1-based generated column number
* @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND})
* @param options - Optional line window configuration
*
* @returns Position details with extracted code context, or `null` when unavailable
*
* @remarks
* Default window uses `3` lines before and `4` lines after the target line.
*
* @example
* ```ts
* const pos = source.getPositionWithCode(12, 8, Bias.BOUND, {
* linesBefore: 2,
* linesAfter: 2
* });
* ```
*
* @since 5.0.0
*/
getPositionWithCode(e, r, t = 0, o) {
let s = this.getPosition(e, r, t);
if (!s || !this.sourcesContent[s.sourceIndex])
return null;
let i = Object.assign({
linesAfter: 4,
linesBefore: 3
}, o), c = this.sourcesContent[s.sourceIndex].split(`
`), l = (s.line ?? 1) - 1, a = Math.max(l - i.linesBefore, 0), u = Math.min(l + i.linesAfter, c.length - 1), f = c.slice(a, u + 1).join(`
`);
return {
...s,
code: f,
endLine: u,
startLine: a
};
}
/**
* Resolves a generated position from an original source position.
*
* @param line - 1-based original source line number
* @param column - 1-based original source column number
* @param sourceIndex - Source index or a partial source path to locate the index
* @param bias - Search behavior when no exact match exists (default: {@link Bias.BOUND})
*
* @returns Position details, or `null` when no segment/source matches
*
* @remarks
* String `sourceIndex` is matched using substring search against resolved
* source paths in {@link sources}.
*
* @example
* ```ts
* const pos = source.getPositionByOriginal(40, 3, 'src/main.ts');
* ```
*
* @since 5.0.0
*/
getPositionByOriginal(e, r, t, o = 0) {
let s = t;
if (typeof t == "string" && (s = this.sources.findIndex((c) => c.includes(t))), s < 0)
return null;
let i = this.mappings.getOriginalSegment(e, r, s, o);
return i ? {
name: this.names[i.nameIndex ?? -1] ?? null,
line: i.line,
column: i.column,
source: this.sources[i.sourceIndex],
sourceRoot: this.sourceRoot,
sourceIndex: i.sourceIndex,
generatedLine: i.generatedLine,
generatedColumn: i.generatedColumn
} : null;
}
/**
* Serializes the current source map object as JSON.
*
* @returns JSON string representation of {@link getSourceObject}
*
* @remarks
* Output is suitable for writing a `.map` file or transport over APIs.
*
* @example
* ```ts
* const json = source.toString();
* ```
*
* @since 5.0.0
*/
toString() {
return JSON.stringify(this.getSourceObject());
}
/**
* Parses and validates Source Map v3 input.
*
* @param input - Source map object or JSON string
*
* @returns Parsed and validated source map object
*
* @throws Error - If source map version is not `3`
* @throws Error - If required keys are missing
*
* @remarks
* Accepts JSON input for convenience and validates required structural keys
* before decode operations are attempted.
*
* @example
* ```ts
* const map = SourceService['parseSourceMap'](raw);
* ```
*
* @since 5.0.0
*/
static parseSourceMap(e) {
if (typeof e == "string" && (e = JSON.parse(e)), e.version !== 3)
throw new Error(`Unsupported SourceMap version: ${e.version}. Expected version 3.`);
let t = ["sources", "mappings"].filter((o) => !(o in e));
if (t.length > 0)
throw new Error(
`Invalid SourceMap: missing required ${t.length === 1 ? "key" : "keys"}: ${t.join(", ")}.`
);
return e;
}
/**
* Checks whether a source path is an HTTP(S) URL.
*
* @param path - Path or URL candidate to evaluate
*
* @returns `true` when the value starts with `http` or `https`, otherwise `false`
*
* @remarks
* This helper is used to preserve absolute URL sources and avoid filesystem
* path normalization for remote resources.
*
* @example
* ```ts
* SourceService['isURL']('https://cdn.example.com/app.ts'); // true
* SourceService['isURL']('src/app.ts'); // false
* ```
*
* @since 5.0.0
*/
static isURL(e) {
return e.startsWith("http") || e.startsWith("https");
}
/**
* Resolves source paths relative to the current working directory.
*
* @param sources - Raw source entries from source map payload
*
* @returns Normalized relative source file paths
*
* @remarks
* Each path is resolved from the generated file location, then converted to
* a path relative to `process.cwd()`.
*
* @example
* ```ts
* // internal utility used during construction
* ```
*
* @since 5.0.0
*/
resolveSources(e) {
return e?.map((r) => n.isURL(r) ? r : r.startsWith("..") ? L(D(), y(b(this.file), r)) : r) ?? [];
}
};
// src/services/resolve.service.ts
import { xterm as R } from "@remotex-labs/xansi/xterm.component";
function N(n) {
let e = n.fileName ?? n.source ?? "", r = n.functionName ? `${n.functionName} ` : "";
e.startsWith("http") && (e += `#L${n.line}`);
let t = n.line && n.column ? R.gray(`[${n.line}:${n.column}]`) : "";
return `at ${r}${R.darkGray(e)} ${t}`.trim();
}
function j(n, e) {
return e.line = n.line, e.column = n.column, n.name && (e.functionName = n.name), n.sourceRoot && !n.source.startsWith("http") && (e.fileName = `${n.sourceRoot}${n.source}`), {
...e,
code: n.code,
format: N(e),
stratLine: n.startLine,
sourceRoot: n.sourceRoot
};
}
function W(n, e) {
if (!e?.withNativeFrames && n.native || !n.line && !n.column && !n.fileName && !n.functionName) return;
let r = e?.getSource?.(n.fileName ?? "");
if (r && n.line && n.column) {
let t = r.getPositionWithCode(n.line, n.column, e?.bias ?? 0, {
linesAfter: e?.linesAfter ?? 4,
linesBefore: e?.linesBefore ?? 3
});
return t ? (t.line += e?.lineOffset ?? 0, t.endLine += e?.lineOffset ?? 0, t.startLine += e?.lineOffset ?? 0, j(t, n)) : void 0;
}
return {
...n,
format: N(n)
};
}
function te(n, e = {}) {
let r = n.stack.map((t) => W(t, e)).filter(Boolean);
return {
name: n.name,
stack: r,
message: n.message
};
}
export {
w as Bias,
E as SourceService,
T as basename,
v as createOffset,
O as decodeSegment,
_ as decodeSegmentRaw,
I as decodeVLQ,
b as dirname,
A as encodeArrayVLQ,
C as encodeSegment,
U as encodeVLQ,
N as formatStackLine,
Q as join,
F as normalize,
L as relative,
y as resolve,
te as resolveError,
W as stackEntry,
j as stackSourceEntry,
g as toPosix,
M as validateSegment
};
//# sourceMappingURL=index.js.map