| | |
| | | }); |
| | | exports.default = mergeSourceMap; |
| | | |
| | | function _sourceMap() { |
| | | const data = require("source-map"); |
| | | function _remapping() { |
| | | const data = require("@ampproject/remapping"); |
| | | |
| | | _sourceMap = function () { |
| | | _remapping = function () { |
| | | return data; |
| | | }; |
| | | |
| | | return data; |
| | | } |
| | | |
| | | function mergeSourceMap(inputMap, map) { |
| | | const input = buildMappingData(inputMap); |
| | | const output = buildMappingData(map); |
| | | const mergedGenerator = new (_sourceMap().SourceMapGenerator)(); |
| | | function mergeSourceMap(inputMap, map, sourceFileName) { |
| | | const source = sourceFileName.replace(/\\/g, "/"); |
| | | let found = false; |
| | | |
| | | for (const { |
| | | source |
| | | } of input.sources) { |
| | | if (typeof source.content === "string") { |
| | | mergedGenerator.setSourceContent(source.path, source.content); |
| | | const result = _remapping()(rootless(map), (s, ctx) => { |
| | | if (s === source && !found) { |
| | | found = true; |
| | | ctx.source = ""; |
| | | return rootless(inputMap); |
| | | } |
| | | } |
| | | |
| | | if (output.sources.length === 1) { |
| | | const defaultSource = output.sources[0]; |
| | | const insertedMappings = new Map(); |
| | | eachInputGeneratedRange(input, (generated, original, source) => { |
| | | eachOverlappingGeneratedOutputRange(defaultSource, generated, item => { |
| | | const key = makeMappingKey(item); |
| | | if (insertedMappings.has(key)) return; |
| | | insertedMappings.set(key, item); |
| | | mergedGenerator.addMapping({ |
| | | source: source.path, |
| | | original: { |
| | | line: original.line, |
| | | column: original.columnStart |
| | | }, |
| | | generated: { |
| | | line: item.line, |
| | | column: item.columnStart |
| | | }, |
| | | name: original.name |
| | | }); |
| | | }); |
| | | }); |
| | | |
| | | for (const item of insertedMappings.values()) { |
| | | if (item.columnEnd === Infinity) { |
| | | continue; |
| | | } |
| | | |
| | | const clearItem = { |
| | | line: item.line, |
| | | columnStart: item.columnEnd |
| | | }; |
| | | const key = makeMappingKey(clearItem); |
| | | |
| | | if (insertedMappings.has(key)) { |
| | | continue; |
| | | } |
| | | |
| | | mergedGenerator.addMapping({ |
| | | generated: { |
| | | line: clearItem.line, |
| | | column: clearItem.columnStart |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | const result = mergedGenerator.toJSON(); |
| | | |
| | | if (typeof input.sourceRoot === "string") { |
| | | result.sourceRoot = input.sourceRoot; |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | function makeMappingKey(item) { |
| | | return `${item.line}/${item.columnStart}`; |
| | | } |
| | | |
| | | function eachOverlappingGeneratedOutputRange(outputFile, inputGeneratedRange, callback) { |
| | | const overlappingOriginal = filterApplicableOriginalRanges(outputFile, inputGeneratedRange); |
| | | |
| | | for (const { |
| | | generated |
| | | } of overlappingOriginal) { |
| | | for (const item of generated) { |
| | | callback(item); |
| | | } |
| | | } |
| | | } |
| | | |
| | | function filterApplicableOriginalRanges({ |
| | | mappings |
| | | }, { |
| | | line, |
| | | columnStart, |
| | | columnEnd |
| | | }) { |
| | | return filterSortedArray(mappings, ({ |
| | | original: outOriginal |
| | | }) => { |
| | | if (line > outOriginal.line) return -1; |
| | | if (line < outOriginal.line) return 1; |
| | | if (columnStart >= outOriginal.columnEnd) return -1; |
| | | if (columnEnd <= outOriginal.columnStart) return 1; |
| | | return 0; |
| | | return null; |
| | | }); |
| | | } |
| | | |
| | | function eachInputGeneratedRange(map, callback) { |
| | | for (const { |
| | | source, |
| | | mappings |
| | | } of map.sources) { |
| | | for (const { |
| | | original, |
| | | generated |
| | | } of mappings) { |
| | | for (const item of generated) { |
| | | callback(item, original, source); |
| | | } |
| | | } |
| | | if (typeof inputMap.sourceRoot === "string") { |
| | | result.sourceRoot = inputMap.sourceRoot; |
| | | } |
| | | |
| | | return Object.assign({}, result); |
| | | } |
| | | |
| | | function buildMappingData(map) { |
| | | const consumer = new (_sourceMap().SourceMapConsumer)(Object.assign({}, map, { |
| | | function rootless(map) { |
| | | return Object.assign({}, map, { |
| | | sourceRoot: null |
| | | })); |
| | | const sources = new Map(); |
| | | const mappings = new Map(); |
| | | let last = null; |
| | | consumer.computeColumnSpans(); |
| | | consumer.eachMapping(m => { |
| | | if (m.originalLine === null) return; |
| | | let source = sources.get(m.source); |
| | | |
| | | if (!source) { |
| | | source = { |
| | | path: m.source, |
| | | content: consumer.sourceContentFor(m.source, true) |
| | | }; |
| | | sources.set(m.source, source); |
| | | } |
| | | |
| | | let sourceData = mappings.get(source); |
| | | |
| | | if (!sourceData) { |
| | | sourceData = { |
| | | source, |
| | | mappings: [] |
| | | }; |
| | | mappings.set(source, sourceData); |
| | | } |
| | | |
| | | const obj = { |
| | | line: m.originalLine, |
| | | columnStart: m.originalColumn, |
| | | columnEnd: Infinity, |
| | | name: m.name |
| | | }; |
| | | |
| | | if (last && last.source === source && last.mapping.line === m.originalLine) { |
| | | last.mapping.columnEnd = m.originalColumn; |
| | | } |
| | | |
| | | last = { |
| | | source, |
| | | mapping: obj |
| | | }; |
| | | sourceData.mappings.push({ |
| | | original: obj, |
| | | generated: consumer.allGeneratedPositionsFor({ |
| | | source: m.source, |
| | | line: m.originalLine, |
| | | column: m.originalColumn |
| | | }).map(item => ({ |
| | | line: item.line, |
| | | columnStart: item.column, |
| | | columnEnd: item.lastColumn + 1 |
| | | })) |
| | | }); |
| | | }, null, _sourceMap().SourceMapConsumer.ORIGINAL_ORDER); |
| | | return { |
| | | file: map.file, |
| | | sourceRoot: map.sourceRoot, |
| | | sources: Array.from(mappings.values()) |
| | | }; |
| | | } |
| | | |
| | | function findInsertionLocation(array, callback) { |
| | | let left = 0; |
| | | let right = array.length; |
| | | |
| | | while (left < right) { |
| | | const mid = Math.floor((left + right) / 2); |
| | | const item = array[mid]; |
| | | const result = callback(item); |
| | | |
| | | if (result === 0) { |
| | | left = mid; |
| | | break; |
| | | } |
| | | |
| | | if (result >= 0) { |
| | | right = mid; |
| | | } else { |
| | | left = mid + 1; |
| | | } |
| | | } |
| | | |
| | | let i = left; |
| | | |
| | | if (i < array.length) { |
| | | while (i >= 0 && callback(array[i]) >= 0) { |
| | | i--; |
| | | } |
| | | |
| | | return i + 1; |
| | | } |
| | | |
| | | return i; |
| | | } |
| | | |
| | | function filterSortedArray(array, callback) { |
| | | const start = findInsertionLocation(array, callback); |
| | | const results = []; |
| | | |
| | | for (let i = start; i < array.length && callback(array[i]) === 0; i++) { |
| | | results.push(array[i]); |
| | | } |
| | | |
| | | return results; |
| | | }); |
| | | } |