/* 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;