Skip to content
This repository was archived by the owner on Apr 3, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"gcp-metadata": "^4.0.0",
"p-limit": "^3.0.1",
"semver": "^7.0.0",
"source-map": "^0.6.1",
"source-map": "^0.7.3",
"split": "^1.0.0"
},
"devDependencies": {
Expand Down
84 changes: 49 additions & 35 deletions src/agent/io/sourcemapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const readFilep = promisify(fs.readFile);
export interface MapInfoInput {
outputFile: string;
mapFile: string;
mapConsumer: sourceMap.RawSourceMap;
mapConsumer: sourceMap.SourceMapConsumer;
// the sources are in ascending order from
// shortest to longest
sources: string[];
Expand Down Expand Up @@ -59,24 +59,23 @@ async function processSourcemap(
}
mapPath = path.normalize(mapPath);

let contents;
let rawSourceMapString;
try {
contents = await readFilep(mapPath, 'utf8');
rawSourceMapString = await readFilep(mapPath, 'utf8');
} catch (e) {
throw new Error('Could not read sourcemap file ' + mapPath + ': ' + e);
}

let consumer: sourceMap.RawSourceMap;
let rawSourceMap;
try {
// TODO: Determine how to reconsile the type conflict where `consumer`
// is constructed as a SourceMapConsumer but is used as a
// RawSourceMap.
// TODO: Resolve the cast of `contents as any` (This is needed because the
// type is expected to be of `RawSourceMap` but the existing
// working code uses a string.)
consumer = new sourceMap.SourceMapConsumer(
contents as {} as sourceMap.RawSourceMap
) as {} as sourceMap.RawSourceMap;
rawSourceMap = JSON.parse(rawSourceMapString);
} catch (e) {
throw new Error('Could not parse the raw sourcemap ' + mapPath + ': ' + e);
}

let consumer: sourceMap.SourceMapConsumer;
try {
consumer = await new sourceMap.SourceMapConsumer(rawSourceMapString);
} catch (e) {
throw new Error(
'An error occurred while reading the ' +
Expand All @@ -93,40 +92,57 @@ async function processSourcemap(
* containing the map file. Otherwise, use the name of the output
* file (with the .map extension removed) as the output file.
*/
const outputBase = consumer.file
? consumer.file
const outputBase = rawSourceMap.file
? rawSourceMap.file
: path.basename(mapPath, '.map');
const parentDir = path.dirname(mapPath);
const outputPath = path.normalize(path.join(parentDir, outputBase));

// the sources are in ascending order from shortest to longest
const nonemptySources = consumer.sources
.filter(val => !!val)
.sort((src1, src2) => src1.length - src2.length);
// The paths of the sources that are relative to the source map file. Sort
// them in ascending order from shortest to longest.
// For webpack file path, normalize the path after the webpack prefix so that
// the source map library can recognize it.
const sourcesRelToSrcmap = rawSourceMap.sources
.filter((val: string) => !!val)
.map((val: string) => {
if (val.toLowerCase().startsWith(WEBPACK_PREFIX)) {
return (
WEBPACK_PREFIX +
path.normalize(val.substr(WEBPACK_PREFIX.length)).replace(/\\/g, '/')
);
}
return val;
})
.sort((src1: string, src2: string) => src1.length - src2.length);

const normalizedSources = nonemptySources
// The paths of the sources that are relative to the current process's working
// directory. These are the ones that are used for the fuzzy search (thus are
// platform specific, e.g. using '\\' on Windows and using '/' in Unix, etc.).
// For webpack file path, the prefix is filtered out for better fuzzy search
// result.
const normalizedSourcesRelToProc = sourcesRelToSrcmap
.map((src: string) => {
if (src.toLowerCase().startsWith(WEBPACK_PREFIX)) {
return src.substring(WEBPACK_PREFIX.length);
}
return src;
})
.map((relPath: string) => {
// resolve the paths relative to the map file so that
// they are relative to the process's current working
// directory
// resolve the paths relative to the map file so that they are relative to
// the process's current working directory
return path.normalize(path.join(parentDir, relPath));
});

if (normalizedSources.length === 0) {
if (normalizedSourcesRelToProc.length === 0) {
throw new Error('No sources listed in the sourcemap file ' + mapPath);
}
for (const src of normalizedSources) {

for (const src of normalizedSourcesRelToProc) {
infoMap.set(path.normalize(src), {
outputFile: outputPath,
mapFile: mapPath,
mapConsumer: consumer,
sources: nonemptySources,
sources: sourcesRelToSrcmap,
});
}
}
Expand Down Expand Up @@ -223,7 +239,6 @@ export class SourceMapper {
const relPath = path
.relative(path.dirname(entry.mapFile), inputPath)
.replace(/\\/g, '/');

/**
* Note: Since `entry.sources` is in ascending order from shortest
* to longest, the first source path that ends with the
Expand All @@ -245,27 +260,26 @@ export class SourceMapper {
column: colNumber, // to be zero-based
};

// TODO: Determine how to remove the explicit cast here.
const consumer: sourceMap.SourceMapConsumer =
entry.mapConsumer as {} as sourceMap.SourceMapConsumer;
const allPos = consumer.allGeneratedPositionsFor(sourcePos);
const allPos = entry.mapConsumer.allGeneratedPositionsFor(sourcePos);
/*
* Based on testing, it appears that the following code is needed to
* properly get the correct mapping information.
*
* In particular, the generatedPositionFor() alone doesn't appear to
* give the correct mapping information.
*/
const mappedPos: sourceMap.Position =
const mappedPos: sourceMap.NullablePosition =
allPos && allPos.length > 0
? allPos.reduce((accumulator, value) => {
return value.line < accumulator.line ? value : accumulator;
return (value.line ?? 0) < (accumulator.line ?? 0)
? value
: accumulator;
})
: consumer.generatedPositionFor(sourcePos);
: entry.mapConsumer.generatedPositionFor(sourcePos);

return {
file: entry.outputFile,
line: mappedPos.line - 1, // convert the one-based line numbers returned
line: (mappedPos.line ?? 0) - 1, // convert the one-based line numbers returned
// by the SourceMapConsumer to the expected
// zero-based output.
// TODO: The `sourceMap.Position` type definition has a `column`
Expand Down