<commit_before>/**
 * Module dependencies
 */

var rework = require('rework');
var whitespace = require('./whitespace');
var resolve = require('path').resolve;
var dirname = require('path').dirname;

/**
 * Initialize the rework modules
 *
 * The modules object is a path/function pair that
 * tells rework-modules how to load a module. It's only requirement
 * is that it has an 'index' property:
 *
 *   {
 *     'index': function() { return 'body { background: red; }' }
 *     'my-other-module': function() { return fs.readFileSync('my-styles.css', 'utf8'); }
 *   }
 *
 * @param {Object} modules
 * @return {Rework}
 */

module.exports = function(modules, index) {
  var parser = new Parser(modules);
  var out = parser.load(index || 'index');
  parser.insertPlaceholders(out);
  return out;
};

/**
 * Parse a rework-module
 *
 * @param {Parser} parser
 * @param {String?} name
 * @api private
 */

function parse(parser, name) {
  return function(style) {
    parser.parse(style, name);
  };
}

/**
 * Create a parser object
 *
 * @param {Object} modules
 * @api private
 */

function Parser (modules) {
  this.modules = modules;
  this.requires = {};
  this.exports = {};
  this.locals = {};
  this.cache = {};
}

/**
 * Load content by name
 *
 * @param {String} name
 * @return {Rework}
 */

Parser.prototype.load = function(path, position, parent) {
  if (this.cache[path]) return this.cache[path];
  var fn = this.modules[path];

  // try it as a dependency
  if (!fn && parent) fn = this.modules[parent + '/deps/' + path];

  if (!fn) throw new ParseError('could not find module \'' + path + '\'', position);

  if (typeof fn === 'string') return this.load(fn);

  var source = fn.source || path;

  var processed = whitespace(fn());

  var out = rework(processed, {source: source})
    .use(parse(this, source));
  out.source = source;

  this.cache[path] = out;

  return out;
};

/**
 * Parse the css ast
 *
 * @param {AST} style
 * @param {String} name
 */

Parser.prototype.parse = function(style, source) {
  var self = this;
  var rules = [];
  style.rules.forEach(function(rule) {
    if (rule.type === 'keyframes') return self.parseKeyframes(rule, source, rules);
    if (rule.type === 'supports') self.parse(rule, source);
    if (rule.type === 'host') self.parse(rule, source);
    if (rule.type === 'media') self.parse(rule, source);
    if (rule.type === 'document') self.parse(rule, source);
    if (select(rule, ':require')) return self.parseRequire(rule, source, rules);
    if (select(rule, ':content')) return self.parseContent(rule, source, rules);
    if (select(rule, ':exports')) return self.parseExports(rule, source, rules);
    if (select(rule, ':locals')) return self.parseLocals(rule, source, rules);
    if (select(rule, '%')) return self.parsePlaceholders(rule, source, rules);
    self.parseVariables(rule, source, rules);
  });
  style.rules = rules;
};

/**
 * Parse require statements
 *
 * @param {Object} rule
 * @param {String} source
 */

Parser.prototype.parseRequire = function(rule, source) {
  var self = this;
  var deps = this.requires[source] = {};

  rule.declarations.forEach(function(dep) {
    var name = dep.property;
    var absolute = self.resolve(source, dep.value);
    deps['$' + name] = self.load(absolute, dep.position, dirname(source));
  });
};

/**
 * Resolve the path to a module
 *
 * @param {String} source
 * @param {String} target
 */

Parser.prototype.resolve = function(source, target) {
  if (target.indexOf('.') !== 0) return target;
  source = dirname('/' + source);
  return resolve(source, target).substr(1);
};

/**
 * Parse conent statements
 *
 * @param {Object} rule
 * @param {String} source
 * @param {Array} rules
 */

Parser.prototype.parseContent = function(rule, source, rules) {
  var deps = this.requires[source] || {};<commit_msg>improve dependency resolution<commit_after>/**
 * Module dependencies
 */

var rework = require('rework');
var whitespace = require('./whitespace');
var resolve = require('path').resolve;
var dirname = require('path').dirname;

/**
 * Initialize the rework modules
 *
 * The modules object is a path/function pair that
 * tells rework-modules how to load a module. It's only requirement
 * is that it has an 'index' property:
 *
 *   {
 *     'index': function() { return 'body { background: red; }' }
 *     'my-other-module': function() { return fs.readFileSync('my-styles.css', 'utf8'); }
 *   }
 *
 * @param {Object} modules
 * @return {Rework}
 */

module.exports = function(modules, index) {
  var parser = new Parser(modules);
  var out = parser.load(index || 'index');
  parser.insertPlaceholders(out);
  return out;
};

/**
 * Parse a rework-module
 *
 * @param {Parser} parser
 * @param {String?} name
 * @api private
 */

function parse(parser, name) {
  return function(style) {
    parser.parse(style, name);
  };
}

/**
 * Create a parser object
 *
 * @param {Object} modules
 * @api private
 */

function Parser (modules) {
  this.modules = modules;
  this.requires = {};
  this.exports = {};
  this.locals = {};
  this.cache = {};
}

/**
 * Load content by name
 *
 * @param {String} name
 * @return {Rework}
 */

Parser.prototype.load = function(path, position, parent) {
  if (this.cache[path]) return this.cache[path];
  var fn = this.modules[path];

  // try it as a dependency
  if (!fn && parent) fn = this.modules[parent + '/deps/' + path];

  if (!fn) throw new ParseError('could not find module \'' + path + '\'', position);

  if (typeof fn === 'string') return this.load(fn);

  var source = fn.source || path;

  var processed = whitespace(fn());

  var out = rework(processed, {source: source})
    .use(parse(this, source));
  out.source = source;

  this.cache[path] = out;

  return out;
};

/**
 * Parse the css ast
 *
 * @param {AST} style
 * @param {String} name
 */

Parser.prototype.parse = function(style, source) {
  var self = this;
  var rules = [];
  style.rules.forEach(function(rule) {
    if (rule.type === 'keyframes') return self.parseKeyframes(rule, source, rules);
    if (rule.type === 'supports') self.parse(rule, source);
    if (rule.type === 'host') self.parse(rule, source);
    if (rule.type === 'media') self.parse(rule, source);
    if (rule.type === 'document') self.parse(rule, source);
    if (select(rule, ':require')) return self.parseRequire(rule, source, rules);
    if (select(rule, ':content')) return self.parseContent(rule, source, rules);
    if (select(rule, ':exports')) return self.parseExports(rule, source, rules);
    if (select(rule, ':locals')) return self.parseLocals(rule, source, rules);
    if (select(rule, '%')) return self.parsePlaceholders(rule, source, rules);
    self.parseVariables(rule, source, rules);
  });
  style.rules = rules;
};

/**
 * Parse require statements
 *
 * @param {Object} rule
 * @param {String} source
 */

Parser.prototype.parseRequire = function(rule, source) {
  var self = this;
  var deps = this.requires[source] = {};

  rule.declarations.forEach(function(dep) {
    var name = dep.property;
    var absolute = self.resolve(source, dep.value);
    deps['$' + name] = self.load(absolute, dep.position, source.split('/')[0]);
  });
};

/**
 * Resolve the path to a module
 *
 * @param {String} source
 * @param {String} target
 */

Parser.prototype.resolve = function(source, target) {
  if (target.indexOf('.') !== 0) return target;
  source = dirname('/' + source);
  return resolve(source, target).substr(1);
};

/**
 * Parse conent statements
 *
 * @param {Object} rule
 * @param {String} source
 * @param {Array} rules
 */

Parser.prototype.parseContent = function(rule, source, rules) {
  var deps = this.requires[source] || {};
{'fraction_non_alphanumeric': 0.1732899022801303, 'fraction_numerical': 0.0009120521172638436, 'mean_word_length': 3.69480122324159, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6386215', 'n_tokens_mistral': 2488, 'n_tokens_neox': 2445, 'n_words': 680}