/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
// Initialize the source-map library right away so that all other code can use it.
SourceMapConsumer.initialize({ "lib/mappings.wasm": "resource://devtools/client/shared/vendor/source-map/lib/mappings.wasm",
});
/** * Create "original source info" objects being handed over to the main thread * to describe original sources referenced in a source map
*/ function mapToOriginalSourceInfos(generatedId, urls) { return urls.map(url => { return {
id: generatedToOriginalId(generatedId, url),
url,
};
});
}
/** * Load the source map and retrieved infos about all the original sources * referenced in that source map. * * @param {Object} generatedSource * Source object for a bundle referencing a source map * @return {Array<Object>|null} * List of object with id and url attributes describing the original sources.
*/
async function getOriginalURLs(generatedSource) { const { resolvedSourceMapURL, baseURL } =
resolveSourceMapURL(generatedSource); const map = await fetchSourceMap(
generatedSource,
resolvedSourceMapURL,
baseURL
); return map ? mapToOriginalSourceInfos(generatedSource.id, map.sources) : null;
}
/** * Load the source map for a given bundle and return information * about the related original sources and the source map itself. * * @param {Object} generatedSource * Source object for the bundle. * @return {Object} * - {Array<Object>} sources * Object with id and url attributes, refering to the related original sources * referenced in the source map. * - [String} resolvedSourceMapURL * Absolute URL for the source map file. * - {Array<String>} ignoreListUrls * List of URLs of sources, designated by the source map, to be ignored in the debugger. * - {String} exception * In case of error, a string describing the situation.
*/
async function loadSourceMap(generatedSource) { const { resolvedSourceMapURL, baseURL } =
resolveSourceMapURL(generatedSource); try { const map = await fetchSourceMap(
generatedSource,
resolvedSourceMapURL,
baseURL
); if (!map.sources.length) { thrownew Error("No sources are declared in this source map.");
}
let ignoreListUrls = []; if (map.x_google_ignoreList?.length) {
ignoreListUrls = map.x_google_ignoreList.map(
sourceIndex => map.sources[sourceIndex]
);
} return {
sources: mapToOriginalSourceInfos(generatedSource.id, map.sources),
resolvedSourceMapURL,
ignoreListUrls,
};
} catch (e) { return {
sources: [],
resolvedSourceMapURL,
ignoreListUrls: [],
exception: e.message,
};
}
}
const COMPUTED_SPANS = new WeakSet();
const SOURCE_MAPPINGS = new WeakMap();
async function getOriginalRanges(sourceId) { if (!isOriginalId(sourceId)) { return [];
}
/** * Given an original location, find the ranges on the generated file that * are mapped from the original range containing the location.
*/
async function getGeneratedRanges(location) { if (!isOriginalId(location.sourceId)) { return [];
}
if (!COMPUTED_SPANS.has(map)) {
COMPUTED_SPANS.add(map);
map.computeColumnSpans();
}
// We want to use 'allGeneratedPositionsFor' to get the _first_ generated // location, but it hard-codes SourceMapConsumer.LEAST_UPPER_BOUND as the // bias, making it search in the wrong direction for this usecase. // To work around this, we use 'generatedPositionFor' and then look up the // exact original location, making any bias value unnecessary, and then // use that location for the call to 'allGeneratedPositionsFor'. const genPos = map.generatedPositionFor({
source: urlsById.get(location.sourceId),
line: location.line,
column: location.column == null ? 0 : location.column,
bias: SourceMapConsumer.GREATEST_LOWER_BOUND,
}); if (genPos.line === null) { return [];
}
// Prior to source-map 0.7, the source-map module returned the earliest // generated location in the file when there were multiple generated // locations. The current comparison fn in 0.7 does not appear to take // generated location into account properly.
let match; for (const pos of positions) { if (!match || pos.line < match.line || pos.column < match.column) {
match = pos;
}
}
/** * Map the breakable positions (line and columns) from generated to original locations. * * @param {Object} breakpointPositions * List of columns per line refering to the breakable columns per line * for a given source: * { * 1: [2, 6], // On line 1, column 2 and 6 are breakable. * ... * } * @param {string} sourceId * The ID for the generated source.
*/
async function getOriginalLocations(breakpointPositions, sourceId) { const map = await getSourceMap(sourceId); if (!map) { returnnull;
} for (const line in breakpointPositions) { const breakableColumnsPerLine = breakpointPositions[line]; for (let i = 0; i < breakableColumnsPerLine.length; i++) { const column = breakableColumnsPerLine[i]; const mappedLocation = getOriginalLocationSync(map, {
sourceId,
line: parseInt(line, 10),
column,
}); if (mappedLocation) { // As we replace the `column` with the mappedLocation, // also transfer the generated column so that we can compute both original and generated locations // in the main thread.
mappedLocation.generatedColumn = column;
breakableColumnsPerLine[i] = mappedLocation;
}
}
} return breakpointPositions;
}
/** * Query the source map for a mapping from bundle location to original location. * * @param {SourceMapConsumer} map * The source map for the bundle source. * @param {Object} location * A location within a bundle to map to an original location. * @param {Object} options * @param {Boolean} options.looseSearch * Optional, if true, will do a loose search on first column and next lines * until a mapping is found. * @return {location} * The mapped location in the original source.
*/ function getOriginalLocationSync(map, location, { looseSearch = false } = {}) { // First check for an exact match
let match = map.originalPositionFor({
line: location.line,
column: location.column == null ? 0 : location.column,
});
// Then check for a loose match by sliding to first column and next lines if (match.sourceUrl == null && looseSearch) {
let line = location.line; // if a non-0 column was passed, we want to do the search from the beginning of the line, // otherwise, we can start looking into next lines
let firstLineChecked = (location.column || 0) !== 0;
// Avoid looping through the whole file and limit the sliding search to the next 10 lines. while (match.sourceUrl === null && line < location.line + 10) { if (firstLineChecked) {
line++;
} else {
firstLineChecked = true;
}
match = map.originalPositionFor({
line,
column: 0,
bias: SourceMapConsumer.LEAST_UPPER_BOUND,
});
}
}
const url = urlsById.get(originalSourceId);
let text = map.sourceContentFor(url, true); if (!text) { try { const response = await networkRequest(url, {
sourceMapBaseURL: map.sourceMapBaseURL,
loadFromCache: false,
allowsRedirects: false,
});
text = response.content;
} catch (err) { // Workers exceptions are processed by worker-utils module and // only metadata attribute is transferred between threads. // Notify the main thread about which url failed loading.
err.metadata = {
url,
}; throw err;
}
}
/** * Find the set of ranges on the generated file that map from the original * file's locations. * * @param sourceId - The original ID of the file we are processing. * @param url - The original URL of the file we are processing. * @param mergeUnmappedRegions - If unmapped regions are encountered between * two mappings for the given original file, allow the two mappings to be * merged anyway. This is useful if you are more interested in the general * contiguous ranges associated with a file, rather than the specifics of * the ranges provided by the sourcemap.
*/ const GENERATED_MAPPINGS = new WeakMap();
async function getGeneratedRangesForOriginal(
sourceId,
mergeUnmappedRegions = false
) { assert(isOriginalId(sourceId), "Source is not an original source");
const data = await getSourceMapWithMetadata(originalToGeneratedId(sourceId)); // NOTE: this is only needed for Flow if (!data) { return [];
} const { urlsById, map } = data; const url = urlsById.get(sourceId);
if (!COMPUTED_SPANS.has(map)) {
COMPUTED_SPANS.add(map);
map.computeColumnSpans();
}
if (!GENERATED_MAPPINGS.has(map)) {
GENERATED_MAPPINGS.set(map, new Map());
}
const generatedRangesMap = GENERATED_MAPPINGS.get(map); if (!generatedRangesMap) { return [];
}
if (generatedRangesMap.has(sourceId)) { // NOTE we need to coerce the result to an array for Flow return generatedRangesMap.get(sourceId) || [];
}
// Gather groups of mappings on the generated file, with new groups created // if we cross a mapping for a different file.
let currentGroup = []; const originalGroups = [currentGroup];
map.eachMapping(
mapping => { if (mapping.source === url) {
currentGroup.push({
start: {
line: mapping.generatedLine,
column: mapping.generatedColumn,
},
end: {
line: mapping.generatedLine, // The lastGeneratedColumn value is an inclusive value so we add // one to it to get the exclusive end position.
column: mapping.lastGeneratedColumn + 1,
},
});
} elseif (typeof mapping.source === "string" && currentGroup.length) { // If there is a URL, but it is for a _different_ file, we create a // new group of mappings so that we can tell
currentGroup = [];
originalGroups.push(currentGroup);
}
}, null,
SourceMapConsumer.GENERATED_ORDER
);
const generatedMappingsForOriginal = []; if (mergeUnmappedRegions) { // If we don't care about excluding unmapped regions, then we just need to // create a range that is the fully encompasses each group, ignoring the // empty space between each individual range. for (const group of originalGroups) { if (group.length) {
generatedMappingsForOriginal.push({
start: group[0].start,
end: group[group.length - 1].end,
});
}
}
} else {
let lastEntry; for (const group of originalGroups) {
lastEntry = null; for (const { start, end } of group) { const lastEnd = lastEntry
? wrappedMappingPosition(lastEntry.end)
: null;
// If this entry comes immediately after the previous one, extend the // range of the previous entry instead of adding a new one. if (
lastEntry &&
lastEnd &&
lastEnd.line === start.line &&
lastEnd.column === start.column
) {
lastEntry.end = end;
} else { const newEntry = { start, end };
generatedMappingsForOriginal.push(newEntry);
lastEntry = newEntry;
}
}
}
}
/** * Set the sourceMap for multiple passed source ids. * * @param {Array<string>} generatedSourceIds * @param {Object} map: An actual sourcemap (as generated with SourceMapGenerator#toJSON)
*/ function setSourceMapForGeneratedSources(generatedSourceIds, map) { const sourceMapConsumer = new SourceMapConsumer(map); for (const generatedId of generatedSourceIds) {
setSourceMap(generatedId, Promise.resolve(sourceMapConsumer));
}
}
function clearSourceMaps() {
clearSourceMapsRequests();
clearWasmXScopes();
clearOriginalURLs();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.