/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const streamChunksOfSourceMap = require("./streamChunksOfSourceMap");
const splitIntoLines = require("./splitIntoLines");
const streamChunksOfCombinedSourceMap = (
source,
sourceMap,
innerSourceName,
innerSource,
innerSourceMap,
removeInnerSource,
onChunk,
onSource,
onName,
finalSource,
columns
) => {
let sourceMapping = new Map();
let nameMapping = new Map();
const sourceIndexMapping = [];
const nameIndexMapping = [];
const nameIndexValueMapping = [];
let innerSourceIndex = -2;
const innerSourceIndexMapping = [];
const innerSourceIndexValueMapping = [];
const innerSourceContents = [];
const innerSourceContentLines = [];
const innerNameIndexMapping = [];
const innerNameIndexValueMapping = [];
const innerSourceMapLineData = [];
const findInnerMapping = (line, column) => {
if (line > innerSourceMapLineData.length) return -1;
const { mappingsData } = innerSourceMapLineData[line - 1];
let l = 0;
let r = mappingsData.length / 5;
while (l < r) {
let m = (l + r) >> 1;
if (mappingsData[m * 5] <= column) {
l = m + 1;
} else {
r = m;
}
}
if (l === 0) return -1;
return l - 1;
};
return streamChunksOfSourceMap(
source,
sourceMap,
(
chunk,
generatedLine,
generatedColumn,
sourceIndex,
originalLine,
originalColumn,
nameIndex
) => {
// Check if this is a mapping to the inner source
if (sourceIndex === innerSourceIndex) {
// Check if there is a mapping in the inner source
const idx = findInnerMapping(originalLine, originalColumn);
if (idx !== -1) {
const { chunks, mappingsData } = innerSourceMapLineData[
originalLine - 1
];
const mi = idx * 5;
const innerSourceIndex = mappingsData[mi + 1];
const innerOriginalLine = mappingsData[mi + 2];
let innerOriginalColumn = mappingsData[mi + 3];
let innerNameIndex = mappingsData[mi + 4];
if (innerSourceIndex >= 0) {
// Check for an identity mapping
// where we are allowed to adjust the original column
const innerChunk = chunks[idx];
const innerGeneratedColumn = mappingsData[mi];
const locationInChunk = originalColumn - innerGeneratedColumn;
if (locationInChunk > 0) {
let originalSourceLines =
innerSourceIndex < innerSourceContentLines.length
? innerSourceContentLines[innerSourceIndex]
: null;
if (originalSourceLines === undefined) {
const originalSource = innerSourceContents[innerSourceIndex];
originalSourceLines = originalSource
? splitIntoLines(originalSource)
: null;
innerSourceContentLines[innerSourceIndex] = originalSourceLines;
}
if (originalSourceLines !== null) {
const originalChunk =
innerOriginalLine <= originalSourceLines.length
? originalSourceLines[innerOriginalLine - 1].slice(
innerOriginalColumn,
innerOriginalColumn + locationInChunk
)
: "";
if (innerChunk.slice(0, locationInChunk) === originalChunk) {
innerOriginalColumn += locationInChunk;
innerNameIndex = -1;
}
}
}
// We have a inner mapping to original source
// emit source when needed and compute global source index
let sourceIndex =
innerSourceIndex < innerSourceIndexMapping.length
? innerSourceIndexMapping[innerSourceIndex]
: -2;
if (sourceIndex === -2) {
const [source, sourceContent] =
innerSourceIndex < innerSourceIndexValueMapping.length
? innerSourceIndexValueMapping[innerSourceIndex]
: [null, undefined];
let globalIndex = sourceMapping.get(source);
if (globalIndex === undefined) {
sourceMapping.set(source, (globalIndex = sourceMapping.size));
onSource(globalIndex, source, sourceContent);
}
sourceIndex = globalIndex;
innerSourceIndexMapping[innerSourceIndex] = sourceIndex;
}
// emit name when needed and compute global name index
let finalNameIndex = -1;
if (innerNameIndex >= 0) {
// when we have a inner name
finalNameIndex =
innerNameIndex < innerNameIndexMapping.length
? innerNameIndexMapping[innerNameIndex]
: -2;
if (finalNameIndex === -2) {
const name =
innerNameIndex < innerNameIndexValueMapping.length
? innerNameIndexValueMapping[innerNameIndex]
: undefined;
if (name) {
let globalIndex = nameMapping.get(name);
if (globalIndex === undefined) {
nameMapping.set(name, (globalIndex = nameMapping.size));
onName(globalIndex, name);
}
finalNameIndex = globalIndex;
} else {
finalNameIndex = -1;
}
innerNameIndexMapping[innerNameIndex] = finalNameIndex;
}
} else if (nameIndex >= 0) {
// when we don't have an inner name,
// but we have an outer name
// it can be used when inner original code equals to the name
let originalSourceLines =
innerSourceContentLines[innerSourceIndex];
if (originalSourceLines === undefined) {
const originalSource = innerSourceContents[innerSourceIndex];
originalSourceLines = originalSource
? splitIntoLines(originalSource)
: null;
innerSourceContentLines[innerSourceIndex] = originalSourceLines;
}
if (originalSourceLines !== null) {
const name = nameIndexValueMapping[nameIndex];
const originalName =
innerOriginalLine <= originalSourceLines.length
? originalSourceLines[innerOriginalLine - 1].slice(
innerOriginalColumn,
innerOriginalColumn + name.length
)
: "";
if (name === originalName) {
finalNameIndex =
nameIndex < nameIndexMapping.length
? nameIndexMapping[nameIndex]
: -2;
if (finalNameIndex === -2) {
const name = nameIndexValueMapping[nameIndex];
if (name) {
let globalIndex = nameMapping.get(name);
if (globalIndex === undefined) {
nameMapping.set(name, (globalIndex = nameMapping.size));
onName(globalIndex, name);
}
finalNameIndex = globalIndex;
} else {
finalNameIndex = -1;
}
nameIndexMapping[nameIndex] = finalNameIndex;
}
}
}
}
onChunk(
chunk,
generatedLine,
generatedColumn,
sourceIndex,
innerOriginalLine,
innerOriginalColumn,
finalNameIndex
);
return;
}
}
// We have a mapping to the inner source, but no inner mapping
if (removeInnerSource) {
onChunk(chunk, generatedLine, generatedColumn, -1, -1, -1, -1);
return;
} else {
if (sourceIndexMapping[sourceIndex] === -2) {
let globalIndex = sourceMapping.get(innerSourceName);
if (globalIndex === undefined) {
sourceMapping.set(source, (globalIndex = sourceMapping.size));
onSource(globalIndex, innerSourceName, innerSource);
}
sourceIndexMapping[sourceIndex] = globalIndex;
}
}
}
const finalSourceIndex =
sourceIndex < 0 || sourceIndex >= sourceIndexMapping.length
? -1
: sourceIndexMapping[sourceIndex];
if (finalSourceIndex < 0) {
// no source, so we make it a generated chunk
onChunk(chunk, generatedLine, generatedColumn, -1, -1, -1, -1);
} else {
// Pass through the chunk with mapping
let finalNameIndex = -1;
if (nameIndex >= 0 && nameIndex < nameIndexMapping.length) {
finalNameIndex = nameIndexMapping[nameIndex];
if (finalNameIndex === -2) {
const name = nameIndexValueMapping[nameIndex];
let globalIndex = nameMapping.get(name);
if (globalIndex === undefined) {
nameMapping.set(name, (globalIndex = nameMapping.size));
onName(globalIndex, name);
}
finalNameIndex = globalIndex;
nameIndexMapping[nameIndex] = finalNameIndex;
}
}
onChunk(
chunk,
generatedLine,
generatedColumn,
finalSourceIndex,
originalLine,
originalColumn,
finalNameIndex
);
}
},
(i, source, sourceContent) => {
if (source === innerSourceName) {
innerSourceIndex = i;
if (innerSource !== undefined) sourceContent = innerSource;
else innerSource = sourceContent;
sourceIndexMapping[i] = -2;
streamChunksOfSourceMap(
sourceContent,
innerSourceMap,
(
chunk,
generatedLine,
generatedColumn,
sourceIndex,
originalLine,
originalColumn,
nameIndex
) => {
while (innerSourceMapLineData.length < generatedLine) {
innerSourceMapLineData.push({
mappingsData: [],
chunks: []
});
}
const data = innerSourceMapLineData[generatedLine - 1];
data.mappingsData.push(
generatedColumn,
sourceIndex,
originalLine,
originalColumn,
nameIndex
);
data.chunks.push(chunk);
},
(i, source, sourceContent) => {
innerSourceContents[i] = sourceContent;
innerSourceContentLines[i] = undefined;
innerSourceIndexMapping[i] = -2;
innerSourceIndexValueMapping[i] = [source, sourceContent];
},
(i, name) => {
innerNameIndexMapping[i] = -2;
innerNameIndexValueMapping[i] = name;
},
false,
columns
);
} else {
let globalIndex = sourceMapping.get(source);
if (globalIndex === undefined) {
sourceMapping.set(source, (globalIndex = sourceMapping.size));
onSource(globalIndex, source, sourceContent);
}
sourceIndexMapping[i] = globalIndex;
}
},
(i, name) => {
nameIndexMapping[i] = -2;
nameIndexValueMapping[i] = name;
},
finalSource,
columns
);
};
module.exports = streamChunksOfCombinedSourceMap;