test(packaging): added test for source map correctness

This commit is contained in:
Kara Erickson
2017-09-07 10:16:03 -07:00
committed by Matias Niemelä
parent 9d93c859d7
commit 86f7b4170c
10 changed files with 153 additions and 8 deletions

View File

@ -0,0 +1,22 @@
const fs = require('fs');
const path = require('path');
const sourceMapTest = require('../source-map-test');
const excludedPackages = ['bazel', 'tsc-wrapped', 'benchpress', 'compiler-cli', 'language-service'];
module.exports = (gulp) => () => {
const packageDir = path.resolve(process.cwd(), 'dist/packages-dist/');
const packages =
fs.readdirSync(packageDir).filter(package => excludedPackages.indexOf(package) === -1);
packages.forEach(package => {
if (sourceMapTest(package).length) {
process.exit(1);
}
});
if (!packages.length) {
console.log('No packages found in packages-dist. Unable to run source map test.');
process.exit(1);
}
};

View File

@ -0,0 +1,66 @@
#!/usr/bin/env node
const path = require('path');
const getMappings = require('./parseMap');
const CLOSURE_REGEX = /tsickle_Closure_declarations\(\)/g;
const VAR_REGEX = /(export )?(var (\S+) =)/g;
const FUNCTION_REGEX = /(export )?function (\S+)\((.*)\) {/g;
const CLASS_REGEX = /var (\S+) = \(function \((\S*)\) {/g;
const PROPERTY_REGEX = /Object.defineProperty\((\S+)\.prototype, "(\S+)", {/g;
const METHOD_REGEX = /(\S+)\.prototype\.(\S+) = function \((\S*)\) {/g;
const GETTER_REGEX = /get_REGEX = function \((\S*)\) {/g;
const TYPE_COMMENT_REGEX = /\/\*\* @type {\?} \*\/ /g;
const AFTER_EQUALS_REGEX = /(.+)=(.*)/g;
const EXPORT_REGEX = /export /g;
const TSLIB_REGEX = /tslib_\d\.__/g;
const STRIP_PREFIX_REGEX = /ɵ/g;
const STRIP_SUFFIX_REGEX = /([^$]+)(\$)+\d/g;
module.exports = function sourceMapTest(package) {
const mappings =
getMappings(getBundlePath(package)).filter(mapping => shouldCheckMapping(mapping.sourceText));
console.log(`Analyzing ${mappings.length} mappings for ${package}...`);
const failures = mappings.filter(
mapping => { return cleanSource(mapping.sourceText) !== cleanGen(mapping.genText); });
logResults(failures);
return failures;
};
function shouldCheckMapping(text) {
// tsickle closure declaration does not exist in final bundle, so can't be checked
if (CLOSURE_REGEX.test(text)) return false;
return VAR_REGEX.test(text) || FUNCTION_REGEX.test(text) || CLASS_REGEX.test(text) ||
PROPERTY_REGEX.test(text) || METHOD_REGEX.test(text) || GETTER_REGEX.test(text);
}
function cleanSource(source) {
return source.replace(TYPE_COMMENT_REGEX, '')
.replace(EXPORT_REGEX, '')
.replace(STRIP_PREFIX_REGEX, '')
.replace(TSLIB_REGEX, '__')
.replace(AFTER_EQUALS_REGEX, '$1=');
}
function cleanGen(gen) {
return gen.replace(TYPE_COMMENT_REGEX, '')
.replace(STRIP_PREFIX_REGEX, '')
.replace(STRIP_SUFFIX_REGEX, '$1')
.replace(AFTER_EQUALS_REGEX, '$1=');
}
function logResults(failures) {
if (failures.length) {
console.error(`... and source maps appear to be broken: ${failures.length} failures.`);
failures.forEach(failure => console.error(failure));
} else {
console.log('... and source maps look good! 100% match');
}
}
function getBundlePath(package) {
return path.resolve(process.cwd(), 'dist/packages-dist/', package, 'esm5/index.js');
}

View File

@ -0,0 +1,57 @@
#!/usr/bin/env node
const vlq = require('vlq');
const fs = require('fs');
const path = require('path');
module.exports =
function getMappings(bundlePath) {
const sourceMap = JSON.parse(getFile(`${bundlePath}.map`));
const sourcesContent = sourceMap.sourcesContent.map(file => file.split('\n'));
const bundleLines = getFile(bundlePath).split('\n');
let sourceLines = sourcesContent[0];
let sourceFileIndex = 0;
let sourceLineIndex = 0;
let sourceColIndex = 0;
return decodeLines(sourceMap).reduce((matchData, mapLine, genLineIndex) => {
mapLine.forEach((segment, index) => {
if (segment.length) {
const [genColDiff, sourceFileDiff, sourceLineDiff, sourceColDiff] = segment;
// if source file changes, grab new file from sourcesContent
if (sourceFileDiff !== 0) {
sourceFileIndex += sourceFileDiff;
sourceLines = sourcesContent[sourceFileIndex];
}
const genText = bundleLines[genLineIndex].trim();
const sourceText = sourceLines[sourceLineIndex + sourceLineDiff].trim();
// only record mappings that are long enough to be meaningful
if (index === 0 && genText.length > 15 && sourceText.length > 15) {
matchData.push({
genLineIndex,
sourceLineIndex,
sourceFile: sourceMap.sources[sourceFileIndex], genText, sourceText
});
}
sourceLineIndex += sourceLineDiff;
sourceColIndex += sourceColDiff;
}
});
return matchData;
}, []);
}
function getFile(filePath) {
return fs.readFileSync(path.resolve(process.cwd(), filePath), 'UTF-8');
}
function decodeLines(sourceMap) {
return sourceMap.mappings.split(';').map(
line => { return line.split(',').map(seg => vlq.decode(seg)); });
}