source-reader.js 2.69 KB
var path = require('path');
var rewriteUrls = require('../urls/rewrite');

var REMOTE_RESOURCE = /^(https?:)?\/\//;

function SourceReader(context, data) {
  this.outerContext = context;
  this.data = data;
  this.sources = {};
}

SourceReader.prototype.sourceAt = function (path) {
  return this.sources[path];
};

SourceReader.prototype.trackSource = function (path, source) {
  this.sources[path] = {};
  this.sources[path][path] = source;
};

SourceReader.prototype.toString = function () {
  if (typeof this.data == 'string')
    return fromString(this);
  if (Buffer.isBuffer(this.data))
    return fromBuffer(this);
  if (Array.isArray(this.data))
    return fromArray(this);

  return fromHash(this);
};

function fromString(self) {
  var data = self.data;
  self.trackSource(undefined, data);
  return data;
}

function fromBuffer(self) {
  var data = self.data.toString();
  self.trackSource(undefined, data);
  return data;
}

function fromArray(self) {
  return self.data
    .map(function (source) {
      return self.outerContext.options.processImport === false ?
        source + '@shallow' :
        source;
    })
    .map(function (source) {
      return !self.outerContext.options.relativeTo || /^https?:\/\//.test(source) ?
        source :
        path.relative(self.outerContext.options.relativeTo, source);
    })
    .map(function (source) { return '@import url(' + source + ');'; })
    .join('');
}

function fromHash(self) {
  var data = [];
  var toBase = path.resolve(self.outerContext.options.target || self.outerContext.options.root);

  for (var source in self.data) {
    var styles = self.data[source].styles;
    var inputSourceMap = self.data[source].sourceMap;
    var isRemote = REMOTE_RESOURCE.test(source);
    var absoluteSource = isRemote ? source : path.resolve(source);
    var absoluteSourcePath = path.dirname(absoluteSource);

    var rewriteOptions = {
      absolute: self.outerContext.options.explicitRoot,
      relative: !self.outerContext.options.explicitRoot,
      imports: true,
      rebase: self.outerContext.options.rebase,
      fromBase: absoluteSourcePath,
      toBase: isRemote ? absoluteSourcePath : toBase,
      urlQuotes: self.outerContext.options.compatibility.properties.urlQuotes
    };
    styles = rewriteUrls(styles, rewriteOptions, self.outerContext);

    self.trackSource(source, styles);

    styles = self.outerContext.sourceTracker.store(source, styles);

    // here we assume source map lies in the same directory as `source` does
    if (self.outerContext.options.sourceMap && inputSourceMap)
      self.outerContext.inputSourceMapTracker.trackLoaded(source, source, inputSourceMap);

    data.push(styles);
  }

  return data.join('');
}

module.exports = SourceReader;