1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-26 20:39:08 +01:00
metamask-extension/development/sourcemap-validator.js

174 lines
5.1 KiB
JavaScript
Raw Normal View History

const fs = require('fs');
const path = require('path');
const { SourceMapConsumer } = require('source-map');
const pify = require('pify');
const fsAsync = pify(fs);
2018-04-28 03:17:21 +02:00
//
// Utility to help check if sourcemaps are working
//
// searches `dist/chrome/inpage.js` for "new Error" statements
// and prints their source lines using the sourcemaps.
// if not working it may error or print minified garbage
//
start().catch((error) => {
console.error(error);
process.exit(1);
});
2018-04-28 03:17:21 +02:00
2020-11-03 00:41:28 +01:00
async function start() {
const targetFiles = [
`background.js`,
// `bg-libs`, skipped because source maps are invalid due to browserify bug: https://github.com/browserify/browserify/issues/1971
// `contentscript.js`, skipped because the validator is erroneously sampling the inlined `inpage.js` script
`inpage.js`,
'phishing-detect.js',
`ui.js`,
// `ui-libs.js`, skipped because source maps are invalid due to browserify bug: https://github.com/browserify/browserify/issues/1971
];
let valid = true;
for (const buildName of targetFiles) {
const fileIsValid = await validateSourcemapForFile({ buildName });
valid = valid && fileIsValid;
}
if (!valid) {
process.exit(1);
}
}
2020-11-03 00:41:28 +01:00
async function validateSourcemapForFile({ buildName }) {
console.log(`build "${buildName}"`);
const platform = `chrome`;
// load build and sourcemaps
let rawBuild;
try {
2020-11-03 00:41:28 +01:00
const filePath = path.join(
__dirname,
`/../dist/${platform}/`,
`${buildName}`,
);
rawBuild = await fsAsync.readFile(filePath, 'utf8');
} catch (_) {
// empty
}
if (!rawBuild) {
2020-11-03 00:41:28 +01:00
throw new Error(
`SourcemapValidator - failed to load source file for "${buildName}"`,
);
}
// attempt to load in dist mode
let rawSourceMap;
try {
2020-11-03 00:41:28 +01:00
const filePath = path.join(
__dirname,
`/../dist/sourcemaps/`,
`${buildName}.map`,
);
rawSourceMap = await fsAsync.readFile(filePath, 'utf8');
} catch (_) {
// empty
}
// attempt to load in dev mode
try {
2020-11-03 00:41:28 +01:00
const filePath = path.join(
__dirname,
`/../dist/${platform}/`,
`${buildName}.map`,
);
rawSourceMap = await fsAsync.readFile(filePath, 'utf8');
} catch (_) {
// empty
}
if (!rawSourceMap) {
2020-11-03 00:41:28 +01:00
throw new Error(
`SourcemapValidator - failed to load sourcemaps for "${buildName}"`,
);
}
const consumer = await new SourceMapConsumer(rawSourceMap);
2018-04-28 03:17:21 +02:00
const hasContentsOfAllSources = consumer.hasContentsOfAllSources();
if (!hasContentsOfAllSources) {
console.warn('SourcemapValidator - missing content of some sources...');
}
console.log(` sampling from ${consumer.sources.length} files`);
let sampleCount = 0;
let valid = true;
const buildLines = rawBuild.split('\n');
const targetString = 'new Error';
// const targetString = 'null'
const matchesPerLine = buildLines.map((line) =>
indicesOf(targetString, line),
);
matchesPerLine.forEach((matchIndices, lineIndex) => {
matchIndices.forEach((matchColumn) => {
sampleCount += 1;
const position = { line: lineIndex + 1, column: matchColumn };
const result = consumer.originalPositionFor(position);
// warn if source content is missing
if (!result.source) {
valid = false;
2020-11-03 00:41:28 +01:00
console.warn(
`!! missing source for position: ${JSON.stringify(position)}`,
);
// const buildLine = buildLines[position.line - 1]
console.warn(` origin in build:`);
console.warn(` ${buildLines[position.line - 2]}`);
console.warn(`-> ${buildLines[position.line - 1]}`);
console.warn(` ${buildLines[position.line - 0]}`);
return;
}
const sourceContent = consumer.sourceContentFor(result.source);
const sourceLines = sourceContent.split('\n');
const line = sourceLines[result.line - 1];
// this sometimes includes the whole line though we tried to match somewhere in the middle
const portion = line.slice(result.column);
const isMaybeValid = portion.includes(targetString);
if (!isMaybeValid) {
valid = false;
2020-11-03 00:41:28 +01:00
console.error(
`Sourcemap seems invalid:\n${getFencedCode(result.source, line)}`,
);
}
});
});
console.log(` checked ${sampleCount} samples`);
return valid;
2018-04-28 03:17:21 +02:00
}
const CODE_FENCE_LENGTH = 80;
const TITLE_PADDING_LENGTH = 1;
2020-11-03 00:41:28 +01:00
function getFencedCode(filename, code) {
const title = `${' '.repeat(TITLE_PADDING_LENGTH)}${filename}${' '.repeat(
TITLE_PADDING_LENGTH,
)}`;
2020-11-03 00:41:28 +01:00
const openingFenceLength = Math.max(
CODE_FENCE_LENGTH - (filename.length + TITLE_PADDING_LENGTH * 2),
0,
);
const startOpeningFenceLength = Math.floor(openingFenceLength / 2);
const endOpeningFenceLength = Math.ceil(openingFenceLength / 2);
2020-11-03 00:41:28 +01:00
const openingFence = `${'='.repeat(
startOpeningFenceLength,
)}${title}${'='.repeat(endOpeningFenceLength)}`;
const closingFence = '='.repeat(CODE_FENCE_LENGTH);
return `${openingFence}\n${code}\n${closingFence}\n`;
}
2020-11-03 00:41:28 +01:00
function indicesOf(substring, string) {
const a = [];
let i = -1;
while ((i = string.indexOf(substring, i + 1)) >= 0) {
a.push(i);
}
return a;
2018-04-28 03:17:21 +02:00
}