diff options
| author | Jez Ng <me@jezng.com> | 2013-06-19 11:22:35 -0700 | 
|---|---|---|
| committer | Jez Ng <me@jezng.com> | 2013-06-19 14:15:18 -0700 | 
| commit | d680f5e81b4bae6cbc46ba04b24c5809679d0bcc (patch) | |
| tree | d96043802f514e2bc75a0e458a9aa655a6cb52de /tools/source-maps/sourcemapper.js | |
| parent | 050335fd91a09cde7ccea9f1fc0ecf536918b95a (diff) | |
Implement source maps for optimized builds.
Diffstat (limited to 'tools/source-maps/sourcemapper.js')
| -rwxr-xr-x | tools/source-maps/sourcemapper.js | 106 | 
1 files changed, 79 insertions, 27 deletions
diff --git a/tools/source-maps/sourcemapper.js b/tools/source-maps/sourcemapper.js index c7021a0f..ba993aa1 100755 --- a/tools/source-maps/sourcemapper.js +++ b/tools/source-maps/sourcemapper.js @@ -2,6 +2,9 @@  "use strict"; +var fs = require('fs'); +var path = require('path'); +  function countLines(s) {    var count = 0;    for (var i = 0, l = s.length; i < l; i ++) { @@ -63,21 +66,39 @@ function extractComments(source, commentHandler) {    }  } -function generateMap(fileName, sourceRoot, mapFileBaseName, generatedLineOffset) { -  var fs = require('fs'); -  var path = require('path'); +function getMappings(source) { +  // generatedLineNumber -> { originalLineNumber, originalFileName } +  var mappings = {}; +  extractComments(source, function(content, generatedLineNumber) { +    var matches = /@line (\d+)(?: "([^"]*)")?/.exec(content); +    if (matches === null) return; +    var originalFileName = matches[2]; +    mappings[generatedLineNumber] = { +      originalLineNumber: parseInt(matches[1], 10), +      originalFileName: originalFileName +    } +  }); +  return mappings; +} + +function generateMap(mappings, sourceRoot, mapFileBaseName, generatedLineOffset) {    var SourceMapGenerator = require('source-map').SourceMapGenerator;    var generator = new SourceMapGenerator({ file: mapFileBaseName }); -  var generatedSource = fs.readFileSync(fileName, 'utf-8');    var seenFiles = Object.create(null); -  extractComments(generatedSource, function(content, generatedLineNumber) { -    var matches = /@line (\d+) "([^"]*)"/.exec(content); -    if (matches === null) return; -    var originalLineNumber = parseInt(matches[1], 10); -    var originalFileName = matches[2]; +  for (var generatedLineNumber in mappings) { +    var generatedLineNumber = parseInt(generatedLineNumber, 10); +    var mapping = mappings[generatedLineNumber]; +    var originalFileName = mapping.originalFileName; +    generator.addMapping({ +      generated: { line: generatedLineNumber + generatedLineOffset, column: 0 }, +      original: { line: mapping.originalLineNumber, column: 0 }, +      source: originalFileName +    }); +    // we could call setSourceContent repeatedly, but readFileSync is slow, so +    // avoid doing it unnecessarily      if (!(originalFileName in seenFiles)) {        seenFiles[originalFileName] = true;        var rootedPath = originalFileName[0] === path.sep ? @@ -89,33 +110,64 @@ function generateMap(fileName, sourceRoot, mapFileBaseName, generatedLineOffset)            " at " + rootedPath);        }      } +  } -    generator.addMapping({ -      generated: { line: generatedLineNumber + generatedLineOffset, column: 0 }, -      original: { line: originalLineNumber, column: 0 }, -      source: originalFileName -    }); -  }); - -  var mapFileName = mapFileBaseName + '.map'; -  fs.writeFileSync(mapFileName, generator.toString()); +  fs.writeFileSync(mapFileBaseName + '.map', generator.toString()); +} -  var lastLine = generatedSource.slice(generatedSource.lastIndexOf('\n')); +function appendMappingURL(fileName, source, mapFileName) { +  var lastLine = source.slice(source.lastIndexOf('\n'));    if (!/sourceMappingURL/.test(lastLine))      fs.appendFileSync(fileName, '//@ sourceMappingURL=' + path.basename(mapFileName));  } +function parseArgs(args) { +  var rv = { _: [] }; // unflagged args go into `_`; similar to the optimist library +  for (var i = 0; i < args.length; i++) { +    if (/^--/.test(args[i])) rv[args[i].slice(2)] = args[++i]; +    else rv._.push(args[i]); +  } +  return rv; +} +  if (require.main === module) {    if (process.argv.length < 3) { -    console.log('Usage: ./sourcemapper.js <filename> <source root (default: .)> ' + -                '<map file basename (default: filename)>' + -                '<generated line offset (default: 0)>'); +    console.log('Usage: ./sourcemapper.js <original js> <optimized js file ...> \\\n' + +                '\t--sourceRoot <default "."> \\\n' + +                '\t--mapFileBaseName <default `filename`> \\\n' + +                '\t--offset <default 0>');      process.exit(1);    } else { -    var sourceRoot = process.argv.length > 3 ? process.argv[3] : "."; -    var mapFileBaseName = process.argv.length > 4 ? process.argv[4] : process.argv[2]; -    var generatedLineOffset = process.argv.length > 5 ? -        parseInt(process.argv[5], 10) : 0; -    generateMap(process.argv[2], sourceRoot, mapFileBaseName, generatedLineOffset); +    var opts = parseArgs(process.argv.slice(2)); +    var fileName = opts._[0]; +    var sourceRoot = opts.sourceRoot ? opts.sourceRoot : "."; +    var mapFileBaseName = opts.mapFileBaseName ? opts.mapFileBaseName : fileName; +    var generatedLineOffset = opts.offset ? parseInt(opts.offset, 10) : 0; + +    var generatedSource = fs.readFileSync(fileName, 'utf-8'); +    var source = generatedSource; +    var mappings = getMappings(generatedSource); +    for (var i = 1, l = opts._.length; i < l; i ++) { +      var optimizedSource = fs.readFileSync(opts._[i], 'utf-8') +      var optimizedMappings = getMappings(optimizedSource); +      var newMappings = {}; +      // uglify processes the code between EMSCRIPTEN_START_FUNCS and +      // EMSCRIPTEN_END_FUNCS, so its line number maps are relative to those +      // markers. we correct for that here. +      var startFuncsLineNumber = countLines( +        source.slice(0, source.indexOf('// EMSCRIPTEN_START_FUNCS'))) + 2; +      for (var line in optimizedMappings) { +        var originalLineNumber = optimizedMappings[line].originalLineNumber + startFuncsLineNumber; +        if (originalLineNumber in mappings) { +          newMappings[line] = mappings[originalLineNumber]; +        } +      } +      mappings = newMappings; +      source = optimizedSource; +    } + +    generateMap(mappings, sourceRoot, mapFileBaseName, generatedLineOffset); +    appendMappingURL(opts._[opts._.length - 1], generatedSource, +                     opts.mapFileBaseName + '.map');    }  }  | 
