Provides functionality related to source maps including a SourceMap class with methods for translating between source and generated code positions, ranges, and locations with support for custom data filtering and fallback matching strategies.
npx @tessl/cli install tessl/npm-volar--source-map@2.4.0@volar/source-map provides functionality for working with source maps, including bidirectional translation between source and generated code positions, ranges, and locations. It features a SourceMap class that enables precise mapping functionality with configurable fallback strategies and custom data filtering capabilities.
npm install @volar/source-mapimport { SourceMap, translateOffset, type Mapping } from "@volar/source-map";For CommonJS:
const { SourceMap, translateOffset } = require("@volar/source-map");import { SourceMap, type Mapping } from "@volar/source-map";
// Create mappings between source and generated code
const mappings: Mapping[] = [
{
sourceOffsets: [10, 20],
generatedOffsets: [30, 50],
lengths: [5, 8],
data: { type: "identifier" }
},
{
sourceOffsets: [25],
generatedOffsets: [65],
lengths: [10],
generatedLengths: [12], // Optional, defaults to lengths
data: { type: "function" }
}
];
// Create SourceMap instance
const sourceMap = new SourceMap(mappings);
// Translate from generated to source
for (const [sourceOffset, mapping] of sourceMap.toSourceLocation(32)) {
console.log(`Generated offset 32 maps to source offset ${sourceOffset}`);
}
// Translate ranges with fallback matching
for (const [sourceStart, sourceEnd, startMapping, endMapping] of
sourceMap.toSourceRange(30, 40, true)) {
console.log(`Generated range [30-40] maps to source range [${sourceStart}-${sourceEnd}]`);
}Core functionality for translating between source and generated code positions using mapping data.
/**
* SourceMap class for bidirectional translation between source and generated code positions
*/
class SourceMap<Data = unknown> {
constructor(mappings: Mapping<Data>[]);
readonly mappings: Mapping<Data>[];
/**
* Maps generated range to source range(s)
* @param generatedStart - Start offset in generated code
* @param generatedEnd - End offset in generated code
* @param fallbackToAnyMatch - Allow start/end from different mappings
* @param filter - Optional function to filter mappings by data
* @returns Generator yielding [mappedStart, mappedEnd, startMapping, endMapping]
*/
toSourceRange(
generatedStart: number,
generatedEnd: number,
fallbackToAnyMatch: boolean,
filter?: (data: Data) => boolean
): Generator<[mappedStart: number, mappedEnd: number, startMapping: Mapping<Data>, endMapping: Mapping<Data>]>;
/**
* Maps source range to generated range(s)
* @param sourceStart - Start offset in source code
* @param sourceEnd - End offset in source code
* @param fallbackToAnyMatch - Allow start/end from different mappings
* @param filter - Optional function to filter mappings by data
* @returns Generator yielding [mappedStart, mappedEnd, startMapping, endMapping]
*/
toGeneratedRange(
sourceStart: number,
sourceEnd: number,
fallbackToAnyMatch: boolean,
filter?: (data: Data) => boolean
): Generator<[mappedStart: number, mappedEnd: number, startMapping: Mapping<Data>, endMapping: Mapping<Data>]>;
/**
* Maps generated offset to source offset(s)
* @param generatedOffset - Offset in generated code
* @param filter - Optional function to filter mappings by data
* @returns Generator yielding [mappedOffset, mapping]
*/
toSourceLocation(
generatedOffset: number,
filter?: (data: Data) => boolean
): Generator<[mappedOffset: number, mapping: Mapping<Data>]>;
/**
* Maps source offset to generated offset(s)
* @param sourceOffset - Offset in source code
* @param filter - Optional function to filter mappings by data
* @returns Generator yielding [mappedOffset, mapping]
*/
toGeneratedLocation(
sourceOffset: number,
filter?: (data: Data) => boolean
): Generator<[mappedOffset: number, mapping: Mapping<Data>]>;
/**
* Find matching offsets for a given position (internal method exposed publicly)
* @param offset - The offset to find matches for
* @param fromRange - Whether searching in source or generated offsets
* @param filter - Optional function to filter mappings by data
* @returns Generator yielding [mappedOffset, mapping]
*/
findMatchingOffsets(
offset: number,
fromRange: 'sourceOffsets' | 'generatedOffsets',
filter?: (data: Data) => boolean
): Generator<[mappedOffset: number, mapping: Mapping<Data>]>;
/**
* Find matching start/end ranges (internal method exposed publicly)
* @param start - Start offset
* @param end - End offset
* @param fallbackToAnyMatch - Allow start/end from different mappings
* @param fromRange - Whether searching in source or generated offsets
* @param filter - Optional function to filter mappings by data
* @returns Generator yielding [mappedStart, mappedEnd, startMapping, endMapping]
*/
findMatchingStartEnd(
start: number,
end: number,
fallbackToAnyMatch: boolean,
fromRange: 'sourceOffsets' | 'generatedOffsets',
filter?: (data: Data) => boolean
): Generator<[mappedStart: number, mappedEnd: number, startMapping: Mapping<Data>, endMapping: Mapping<Data>]>;
}Standalone utility function for translating offsets between ranges.
/**
* Translates an offset from one range to another using mapping arrays
* @param start - The offset to translate
* @param fromOffsets - Source range offsets (should be sorted for optimal performance)
* @param toOffsets - Target range offsets
* @param fromLengths - Lengths for source ranges
* @param toLengths - Lengths for target ranges (defaults to fromLengths)
* @returns Translated offset or undefined if no mapping found
*/
function translateOffset(
start: number,
fromOffsets: number[],
toOffsets: number[],
fromLengths: number[],
toLengths: number[] = fromLengths
): number | undefined;/**
* Represents a single mapping between source and generated code positions
*/
interface Mapping<Data = unknown> {
/** Array of source code offsets */
sourceOffsets: number[];
/** Array of generated code offsets */
generatedOffsets: number[];
/** Array of lengths for source offsets */
lengths: number[];
/** Optional array of lengths for generated offsets (defaults to lengths) */
generatedLengths?: number[];
/** Custom data associated with the mapping */
data: Data;
}import { translateOffset } from "@volar/source-map";
// Translate offset 15 from source to generated code
const generatedOffset = translateOffset(
15, // offset to translate
[10, 20, 30], // source offsets
[50, 80, 120], // generated offsets
[5, 8, 10], // source lengths
[8, 12, 15] // generated lengths
);
console.log(generatedOffset); // 55 (if offset 15 falls in first range)import { SourceMap, type Mapping } from "@volar/source-map";
interface CustomData {
type: 'identifier' | 'function' | 'comment';
important: boolean;
}
const mappings: Mapping<CustomData>[] = [
{
sourceOffsets: [10],
generatedOffsets: [30],
lengths: [5],
data: { type: 'identifier', important: true }
},
{
sourceOffsets: [20],
generatedOffsets: [40],
lengths: [8],
data: { type: 'comment', important: false }
}
];
const sourceMap = new SourceMap(mappings);
// Only map important identifiers
for (const [offset, mapping] of sourceMap.toGeneratedLocation(
12,
(data) => data.important && data.type === 'identifier'
)) {
console.log(`Important identifier at source offset 12 maps to ${offset}`);
}import { SourceMap, type Mapping } from "@volar/source-map";
const mappings: Mapping[] = [
{
sourceOffsets: [10, 30],
generatedOffsets: [100, 200],
lengths: [5, 10],
data: {}
}
];
const sourceMap = new SourceMap(mappings);
// Try exact range mapping first, then fallback to any match
for (const [start, end, startMapping, endMapping] of
sourceMap.toGeneratedRange(12, 35, true)) {
console.log(`Source range [12-35] maps to generated range [${start}-${end}]`);
console.log(`Start mapping:`, startMapping);
console.log(`End mapping:`, endMapping);
}translateOffset returns undefined when no mapping is found for the given offsetfromOffsets array is not sorted in translateOffset (performance optimization)