You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							245 lines
						
					
					
						
							7.7 KiB
						
					
					
				
			
		
		
	
	
							245 lines
						
					
					
						
							7.7 KiB
						
					
					
				var fs = require('fs'); | 
						|
var path = require('path'); | 
						|
 | 
						|
var isAllowedResource = require('./is-allowed-resource'); | 
						|
var matchDataUri = require('./match-data-uri'); | 
						|
var rebaseLocalMap = require('./rebase-local-map'); | 
						|
var rebaseRemoteMap = require('./rebase-remote-map'); | 
						|
 | 
						|
var Token = require('../tokenizer/token'); | 
						|
var hasProtocol = require('../utils/has-protocol'); | 
						|
var isDataUriResource = require('../utils/is-data-uri-resource'); | 
						|
var isRemoteResource = require('../utils/is-remote-resource'); | 
						|
 | 
						|
var MAP_MARKER_PATTERN = /^\/\*# sourceMappingURL=(\S+) \*\/$/; | 
						|
 | 
						|
function applySourceMaps(tokens, context, callback) { | 
						|
  var applyContext = { | 
						|
    callback: callback, | 
						|
    fetch: context.options.fetch, | 
						|
    index: 0, | 
						|
    inline: context.options.inline, | 
						|
    inlineRequest: context.options.inlineRequest, | 
						|
    inlineTimeout: context.options.inlineTimeout, | 
						|
    inputSourceMapTracker: context.inputSourceMapTracker, | 
						|
    localOnly: context.localOnly, | 
						|
    processedTokens: [], | 
						|
    rebaseTo: context.options.rebaseTo, | 
						|
    sourceTokens: tokens, | 
						|
    warnings: context.warnings | 
						|
  }; | 
						|
 | 
						|
  return context.options.sourceMap && tokens.length > 0 ? | 
						|
    doApplySourceMaps(applyContext) : | 
						|
    callback(tokens); | 
						|
} | 
						|
 | 
						|
function doApplySourceMaps(applyContext) { | 
						|
  var singleSourceTokens = []; | 
						|
  var lastSource = findTokenSource(applyContext.sourceTokens[0]); | 
						|
  var source; | 
						|
  var token; | 
						|
  var l; | 
						|
 | 
						|
  for (l = applyContext.sourceTokens.length; applyContext.index < l; applyContext.index++) { | 
						|
    token = applyContext.sourceTokens[applyContext.index]; | 
						|
    source = findTokenSource(token); | 
						|
 | 
						|
    if (source != lastSource) { | 
						|
      singleSourceTokens = []; | 
						|
      lastSource = source; | 
						|
    } | 
						|
 | 
						|
    singleSourceTokens.push(token); | 
						|
    applyContext.processedTokens.push(token); | 
						|
 | 
						|
    if (token[0] == Token.COMMENT && MAP_MARKER_PATTERN.test(token[1])) { | 
						|
      return fetchAndApplySourceMap(token[1], source, singleSourceTokens, applyContext); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return applyContext.callback(applyContext.processedTokens); | 
						|
} | 
						|
 | 
						|
function findTokenSource(token) { | 
						|
  var scope; | 
						|
  var metadata; | 
						|
 | 
						|
  if (token[0] == Token.AT_RULE || token[0] == Token.COMMENT) { | 
						|
    metadata = token[2][0]; | 
						|
  } else { | 
						|
    scope = token[1][0]; | 
						|
    metadata = scope[2][0]; | 
						|
  } | 
						|
 | 
						|
  return metadata[2]; | 
						|
} | 
						|
 | 
						|
function fetchAndApplySourceMap(sourceMapComment, source, singleSourceTokens, applyContext) { | 
						|
  return extractInputSourceMapFrom(sourceMapComment, applyContext, function (inputSourceMap) { | 
						|
    if (inputSourceMap) { | 
						|
      applyContext.inputSourceMapTracker.track(source, inputSourceMap); | 
						|
      applySourceMapRecursively(singleSourceTokens, applyContext.inputSourceMapTracker); | 
						|
    } | 
						|
 | 
						|
    applyContext.index++; | 
						|
    return doApplySourceMaps(applyContext); | 
						|
  }); | 
						|
} | 
						|
 | 
						|
function extractInputSourceMapFrom(sourceMapComment, applyContext, whenSourceMapReady) { | 
						|
  var uri = MAP_MARKER_PATTERN.exec(sourceMapComment)[1]; | 
						|
  var absoluteUri; | 
						|
  var sourceMap; | 
						|
  var rebasedMap; | 
						|
 | 
						|
  if (isDataUriResource(uri)) { | 
						|
    sourceMap = extractInputSourceMapFromDataUri(uri); | 
						|
    return whenSourceMapReady(sourceMap); | 
						|
  } else if (isRemoteResource(uri)) { | 
						|
    return loadInputSourceMapFromRemoteUri(uri, applyContext, function (sourceMap) { | 
						|
      var parsedMap; | 
						|
 | 
						|
      if (sourceMap) { | 
						|
        parsedMap = JSON.parse(sourceMap); | 
						|
        rebasedMap = rebaseRemoteMap(parsedMap, uri); | 
						|
        whenSourceMapReady(rebasedMap); | 
						|
      } else { | 
						|
        whenSourceMapReady(null); | 
						|
      } | 
						|
    }); | 
						|
  } else { | 
						|
    // at this point `uri` is already rebased, see lib/reader/rebase.js#rebaseSourceMapComment | 
						|
    // it is rebased to be consistent with rebasing other URIs | 
						|
    // however here we need to resolve it back to read it from disk | 
						|
    absoluteUri = path.resolve(applyContext.rebaseTo, uri); | 
						|
    sourceMap = loadInputSourceMapFromLocalUri(absoluteUri, applyContext); | 
						|
 | 
						|
    if (sourceMap) { | 
						|
      rebasedMap = rebaseLocalMap(sourceMap, absoluteUri, applyContext.rebaseTo); | 
						|
      return whenSourceMapReady(rebasedMap); | 
						|
    } else { | 
						|
      return whenSourceMapReady(null); | 
						|
    } | 
						|
  } | 
						|
} | 
						|
 | 
						|
function extractInputSourceMapFromDataUri(uri) { | 
						|
  var dataUriMatch = matchDataUri(uri); | 
						|
  var charset = dataUriMatch[2] ? dataUriMatch[2].split(/[=;]/)[2] : 'us-ascii'; | 
						|
  var encoding = dataUriMatch[3] ? dataUriMatch[3].split(';')[1] : 'utf8'; | 
						|
  var data = encoding == 'utf8' ? global.unescape(dataUriMatch[4]) : dataUriMatch[4]; | 
						|
 | 
						|
  var buffer = new Buffer(data, encoding); | 
						|
  buffer.charset = charset; | 
						|
 | 
						|
  return JSON.parse(buffer.toString()); | 
						|
} | 
						|
 | 
						|
function loadInputSourceMapFromRemoteUri(uri, applyContext, whenLoaded) { | 
						|
  var isAllowed = isAllowedResource(uri, true, applyContext.inline); | 
						|
  var isRuntimeResource = !hasProtocol(uri); | 
						|
 | 
						|
  if (applyContext.localOnly) { | 
						|
    applyContext.warnings.push('Cannot fetch remote resource from "' + uri + '" as no callback given.'); | 
						|
    return whenLoaded(null); | 
						|
  } else if (isRuntimeResource) { | 
						|
    applyContext.warnings.push('Cannot fetch "' + uri + '" as no protocol given.'); | 
						|
    return whenLoaded(null); | 
						|
  } else if (!isAllowed) { | 
						|
    applyContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.'); | 
						|
    return whenLoaded(null); | 
						|
  } | 
						|
 | 
						|
  applyContext.fetch(uri, applyContext.inlineRequest, applyContext.inlineTimeout, function (error, body) { | 
						|
    if (error) { | 
						|
      applyContext.warnings.push('Missing source map at "' + uri + '" - ' + error); | 
						|
      return whenLoaded(null); | 
						|
    } | 
						|
 | 
						|
    whenLoaded(body); | 
						|
  }); | 
						|
} | 
						|
 | 
						|
function loadInputSourceMapFromLocalUri(uri, applyContext) { | 
						|
  var isAllowed = isAllowedResource(uri, false, applyContext.inline); | 
						|
  var sourceMap; | 
						|
 | 
						|
  if (!fs.existsSync(uri) || !fs.statSync(uri).isFile()) { | 
						|
    applyContext.warnings.push('Ignoring local source map at "' + uri + '" as resource is missing.'); | 
						|
    return null; | 
						|
  } else if (!isAllowed) { | 
						|
    applyContext.warnings.push('Cannot fetch "' + uri + '" as resource is not allowed.'); | 
						|
    return null; | 
						|
  } | 
						|
 | 
						|
  sourceMap = fs.readFileSync(uri, 'utf-8'); | 
						|
  return JSON.parse(sourceMap); | 
						|
} | 
						|
 | 
						|
function applySourceMapRecursively(tokens, inputSourceMapTracker) { | 
						|
  var token; | 
						|
  var i, l; | 
						|
 | 
						|
  for (i = 0, l = tokens.length; i < l; i++) { | 
						|
    token = tokens[i]; | 
						|
 | 
						|
    switch (token[0]) { | 
						|
      case Token.AT_RULE: | 
						|
        applySourceMapTo(token, inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.AT_RULE_BLOCK: | 
						|
        applySourceMapRecursively(token[1], inputSourceMapTracker); | 
						|
        applySourceMapRecursively(token[2], inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.AT_RULE_BLOCK_SCOPE: | 
						|
        applySourceMapTo(token, inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.NESTED_BLOCK: | 
						|
        applySourceMapRecursively(token[1], inputSourceMapTracker); | 
						|
        applySourceMapRecursively(token[2], inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.NESTED_BLOCK_SCOPE: | 
						|
        applySourceMapTo(token, inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.COMMENT: | 
						|
        applySourceMapTo(token, inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.PROPERTY: | 
						|
        applySourceMapRecursively(token, inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.PROPERTY_BLOCK: | 
						|
        applySourceMapRecursively(token[1], inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.PROPERTY_NAME: | 
						|
        applySourceMapTo(token, inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.PROPERTY_VALUE: | 
						|
        applySourceMapTo(token, inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.RULE: | 
						|
        applySourceMapRecursively(token[1], inputSourceMapTracker); | 
						|
        applySourceMapRecursively(token[2], inputSourceMapTracker); | 
						|
        break; | 
						|
      case Token.RULE_SCOPE: | 
						|
        applySourceMapTo(token, inputSourceMapTracker); | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  return tokens; | 
						|
} | 
						|
 | 
						|
function applySourceMapTo(token, inputSourceMapTracker) { | 
						|
  var value = token[1]; | 
						|
  var metadata = token[2]; | 
						|
  var newMetadata = []; | 
						|
  var i, l; | 
						|
 | 
						|
  for (i = 0, l = metadata.length; i < l; i++) { | 
						|
    newMetadata.push(inputSourceMapTracker.originalPositionFor(metadata[i], value.length)); | 
						|
  } | 
						|
 | 
						|
  token[2] = newMetadata; | 
						|
} | 
						|
 | 
						|
module.exports = applySourceMaps;
 | 
						|
 |