text: 'use strict';
  
  var CACHE_DIR;
  var _ = require('./util.js');
  var rType = /\s+type\s*=\s*(['"]?)((?:text|application)\/(.*?))\1/i;
  var memoryCache = {};
  var compileStack = [];
  var path = require('path');
  
  // 待编译完成后，清空内存缓存
  hack.on('release:end', function() {
    memoryCache = {};
    compileStack = [];
  });
  hack.on('release:error', function() {
    memoryCache = {};
    compileStack = [];
  });
  
  function revertFromMemory(file, inCache) {
    file.revertFromCacheData(inCache.getCacheData());
    file.cache = inCache.cache;
    file.setContent(inCache.getContent());
  };
  
  /**
   * 编译相关函数入口, 输入为 文件对象，没有输出，直接修改文件对象内容。
   *
   * 默认文件编译会尝试从缓存中读取，如果缓存有效，将跳过编译。
   *
   * @example
   * var file = hack.file(filepath);
   * hack.compile(file);
   * console.log(file.getContent());
   *
   * @function
   * @param {String} file 文件对象
   * @namespace hack.compile
   */
  var exports = module.exports = function(file, context) {
    if (!CACHE_DIR) {
      hack.log.error('uninitialized compile cache directory.');
    }
    file = hack.file.wrap(file);
  
    if (!file.realpath) {
      error('unable to compile [' + file.subpath + ']: Invalid file realpath.');
    }
  
    if (file.useCache && memoryCache[file.realpath]) {
      revertFromMemory(file, memoryCache[file.realpath]);
      return file;
    }
  
    // 只有启用 cache 的时候才存入 memoryCache
    file.useCache && (memoryCache[file.realpath] = file);
  
    // lint 置前，不使用文件缓存
    if (file.isText() && exports.settings.useLint) {
      pipe(file, 'lint', true);
    }
  
    adjust(file);
  
    hack.log.debug('compile [' + file.realpath + '] start');
    hack.emit('compile:start', file);
    ~compileStack.indexOf(file.realpath) || compileStack.push(file.realpath);
    if (file.isFile()) {
      if (file.useCompile && file.ext && file.ext !== '.') {
        var cache = file.cache = hack.cache(file.realpath, CACHE_DIR),
          revertObj = {};
        if (file.useCache && cache.revert(revertObj)) {
          exports.settings.beforeCacheRevert(file);
          file.revertFromCacheData(revertObj.info);
          if (file.isText()) {
            revertObj.content = revertObj.content.toString('utf8');
          }
          file.setContent(revertObj.content);
          exports.settings.afterCacheRevert(file);
        } else {
          exports.settings.beforeCompile(file);
          file.setContent(hack.util.read(file.realpath));
          process(file);
          exports.settings.afterCompile(file);
          hack.log.debug('Save cache [%s] start', file.subpath);
          file.useCache && cache.save(file.getContent(), file.getCacheData());
          hack.log.debug('Save cache [%s] end', file.subpath);
        }
      } else {
        file.setContent(file.isText() ? hack.util.read(file.realpath) : hack.util.fs.readFileSync(file.realpath));
      }
    } else if (file.useCompile && file.ext && file.ext !== '.') {
      process(file, context);
    }
    if (file.useHash) {
      file.getHash();
    }
    file.compiled = true;
    hack.log.debug('compile [' + file.realpath + '] end');
    hack.emit('compile:end', file);
  
    // 是文件变化触发的重新编译，不进入相关文件编译。
    context && context.fromWatch || file.links.forEach(function(subpath) {
      var f = hack.file.wrap(hack.project.getProjectPath() + subpath);
  
      if (f.exists() && !~compileStack.indexOf(f.realpath)) {
        compileStack.push(f.realpath);
        hack.emit('compile:add', f);
      }
    });
  
    compileStack.pop();
    return file;
  };
  
  /**
   * hack 编译默认的配置项
   * @property {Boolean} debug 如果设置成了 true, 那么编译缓存将会使用 debug 文件夹来存储缓存。
   * @property {Function} beforeCacheRevert 当文件从缓存中还原回来前执行。
   * @property {Function} afterCacheRevert 当文件从缓存中还原回来后执行。
   * @property {Function} beforeCompile 当文件开始编译前执行。
   * @property {Function} afterCompile 当文件开始编译后执行。
   * @name settings
   * @memberOf hack.compile
   */
  exports.settings = {
    debug: false,
    useLint: false,
    beforeCacheRevert: function() {},
    afterCacheRevert: function() {},
    beforeCompile: function() {},
    afterCompile: function() {}
  };
  
  /**
   * 在编译前，初始化配置项。关于配置项，请查看 {@link hack.compile.settings}
   * @param  {Object} opt
   * @return {String}     缓存文件夹路径
   * @memberOf hack.compile
   * @name setup
   * @function
   */
  exports.setup = function(opt) {
    var settings = exports.settings;
    if (opt) {
      hack.util.map(settings, function(key) {
        if (typeof opt[key] !== 'undefined') {
          settings[key] = opt[key];
        }
      });
    }
  
    CACHE_DIR = 'compile/';
    if (settings.unique) {
      CACHE_DIR += Date.now() + '-' + Math.random();
    } else {
      CACHE_DIR += '' + (settings.debug ? 'debug' : 'release') + '-' + hack.project.currentMedia();
    }
  
    return CACHE_DIR;
  };
  
  /**
   * 清除缓存
   * @param  {String} name 想要清除的缓存目录，缺省为清除默认缓存或全部缓存
   * @memberOf hack.compile
   * @name clean
   * @function
   */
  exports.clean = function(name) {
    if (name) {
      hack.cache.clean('compile/' + name);
    } else if (CACHE_DIR) {
      hack.cache.clean(CACHE_DIR);
    } else {
      hack.cache.clean('compile');
    }
  };
  
  
  /**
   * hack 中间码管理器。
   * @namespace hack.compile.lang
   */
  var map = exports.lang = (function() {
    var keywords = [];
    var delim = '\u001F'; // Unit Separator
    var rdelim = '\\u001F';
    var slice = [].slice;
    var regInvalid = false;
    var reg = null;
    var map = {
  
      /**
       * 添加其他中间码类型。
       * @param {String} type 类型
       * @function add
       * @memberOf hack.compile.lang
       */
      add: function(type) {
        if (~keywords.indexOf(type)) {
          return this;
        }
        var stack = [];
        keywords.push(type);
        regInvalid = true;
        map[type] = {
          wrap: function(value) {
            return this.ld + slice.call(arguments, 0).join(delim) + this.rd;
          }
        };
  
        // 定义map.ld
        Object.defineProperty(map[type], 'ld', {
          get: function() {
            var depth = stack.length;
            stack.push(depth);
            return delim + type + depth + delim;
          }
        });
  
        // 定义map.rd
        Object.defineProperty(map[type], 'rd', {
          get: function() {
            return delim + stack.pop() + type + delim;
          }
        });
      }
    };
  
    /**
     * 获取能识别中间码的正则
     * @name reg
     * @type {RegExp}
     * @memberOf hack.compile.lang
     */
    Object.defineProperty(map, 'reg', {
      get: function() {
        if (regInvalid || !reg) {
          reg = new RegExp(
            rdelim + '(' + keywords.join('|') + ')(\\d+?)' + rdelim + '([^' + rdelim + ']*?)(?:' + rdelim + '([^' + rdelim + ']+?))?' + rdelim + '\\2\\1' + rdelim,
            'g'
          );
          regInvalid = false;
        }
  
        return reg;
      }
    });
  
    // 默认支持的中间码
    [
      'require', // 同步依赖文件。
      'jsRequire', // 同步 js 依赖
      'embed', // 内嵌其他文件
      'jsEmbed', // 内嵌 js 文件内容
      'async', // 异步依赖
      'jsAsync', // js 异步依赖
      'uri', // 替换成目标文件的 url
      'dep', // 简单的标记依赖
      'id', // 替换成目标文件的 id
      'hash', // 替换成目标文件的 md5 戳。
      'moduleId', // 替换成目标文件的 moduleId
      'xlang', // 用来内嵌其他语言
      'inlineStyle', // 内联样式
      'sourceMap',
      'info' // 能用来包括其他中间码，包裹后可以起到其他中间码的作用，但是不会修改代码源码。
    ].forEach(map.add);
  
    return map;
  })();
  
  /**
   * 判断info.query是否为inline
   *
   * - `abc?__inline` return true
   * - `abc?__inlinee` return false
   * - `abc?a=1&__inline'` return true
   * - `abc?a=1&__inline=` return true
   * - `abc?a=1&__inline&` return true
   * - `abc?a=1&__inline` return true
   * @param {Object} info
   * @memberOf hack.compile
   */
  function isInline(info) {
    return /[?&]__inline(?:[=&'"]|$)/.test(info.query);
  }
  
  /**
   * 分析注释中依赖用法。
   * @param {String} comment 注释内容
   * @param {Callback} [callback] 可以通过此参数来替换原有替换回调函数。
   * @memberOf hack.compile
   */
  function analyseComment(comment, callback) {
    var reg = /(@(require|async|require\.async)\s+)('[^']+'|"[^"]+"|[^\s;!@#%^&*()]+)/g;
    callback = callback || function(m, prefix, type, value) {
      type = type === 'require' ? type : 'async';
  
      return prefix + map[type].wrap(value);
    };
  
    return comment.replace(reg, callback).replace(/(?:@|#)\s+sourceMappingURL=([^\s]+)/g, function(_, value) {
      return '# sourceMappingURL=' + map.sourceMap.wrap(value);
    });
  }
  
  /**
   * 标准化处理 javascript 内容, 识别 __inline、__uri 和 __require 的用法，并将其转换成中间码。
   *
   * - [@require id] in comment to require resource
   * - __inline(path) to embedd resource content or base64 encodings
   * - __uri(path) to locate resource
   * - require(path) to require resource
   *
   * @param {String} content js 内容
   * @param {Callback} callback 正则替换回调函数，如果不想替换，请传入 null.
   * @param {File} file js 内容所在文件。
   * @memberOf hack.compile
   */
  function extJs(content, callback, file) {
    var reg = /"(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|(\/\/[^\r\n\f]+|\/\*[\s\S]*?(?:\*\/|$))|\b(__inline|__uri|__require|__id|__moduleId|__hash)\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*')\s*\)/g;
    callback = callback || function(m, comment, type, value) {
      if (type) {
        switch (type) {
          case '__inline':
            m = map.jsEmbed.wrap(value);
            break;
          case '__uri':
            m = map.uri.wrap(value);
            break;
          case '__id':
            m = map.id.wrap(value);
            break;
          case '__moduleId':
            m = map.moduleId.wrap(value);
            break;
          case '__require':
            m = 'require(' + map.jsRequire.wrap(value) + ')';
            break;
          case '__hash':
            m = map.hash.wrap(value);
            break;
        }
      } else if (comment) {
        m = analyseComment(comment);
      }
      return m;
    };
    content = content.replace(reg, callback);
    var info = {
      file: file,
      content: content
    };
  
    hack.emit('standard:js', info);
    return info.content;
  }
  
  /**
   * 标准化处理 css 内容, 识别各种外链用法，并将其转换成中间码。
   *
   * - [@require id] in comment to require resource
   * - [@import url(path?__inline)] to embed resource content
   * - url(path) to locate resource
   * - url(path?__inline) to embed resource content or base64 encodings
   * - src=path to locate resource
   *
   * @param {String} content css 内容。
   * @param {Callback} callback 正则替换回调函数，如果不想替换，请传入 null.
   * @param {File} file js 内容所在文件。
   * @memberOf hack.compile
   */
  function extCss(content, callback, file) {
    var reg = /(\/\*[\s\S]*?(?:\*\/|$))|(?:@import\s+)?\burl\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^)}\s]+)\s*\)(\s*;?)|\bsrc\s*=\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^\s}]+)/g;
    callback = callback || function(m, comment, url, last, filter) {
      if (url) {
        var key = isInline(hack.util.query(url)) ? 'embed' : 'uri';
        if (m.indexOf('@') === 0) {
          if (key === 'embed') {
            m = map.embed.wrap(url) + last.replace(/;$/, '');
          } else {
            m = '@import url(' + map.uri.wrap(url) + ')' + last;
          }
        } else {
          m = 'url(' + map[key].wrap(url) + ')' + last;
        }
      } else if (filter) {
        m = 'src=' + map.uri.wrap(filter);
      } else if (comment) {
        m = analyseComment(comment);
      }
      return m;
    };
    content = content.replace(reg, callback);
  
    var info = {
      file: file,
      content: content
    };
  
    hack.emit('standard:css', info);
  
    return info.content;
  }
  
  /**
   * 标准化处理 html 内容, 识别各种语法，并将其转换成中间码。
   *
   * - `<!--inline[path]-->` to embed resource content
   * - `<img|embed|audio|video|link|object ... (data-)?src="path"/>` to locate resource
   * - `<img|embed|audio|video|link|object ... (data-)?src="path?__inline"/>` to embed resource content
   * - `<script|style ... src="path"></script|style>` to locate js|css resource
   * - `<script|style ... src="path?__inline"></script|style>` to embed js|css resource
   * - `<script|style ...>...</script|style>` to analyse as js|css
   *
   * @param {String} content html 内容。
   * @param {Callback} callback 正则替换回调函数，如果不想替换，请传入 null.
   * @param {File} file js 内容所在文件。
   * @memberOf hack.compile
   */
  function extHtml(content, callback, file) {
    var reg = /(<script(?:(?=\s)[\s\S]*?["'\s\w\/\-]>|>))([\s\S]*?)(?=<\/script\s*>|$)|(<style(?:(?=\s)[\s\S]*?["'\s\w\/\-]>|>))([\s\S]*?)(?=<\/style\s*>|$)|<(img|embed|audio|video|link|object|source)\s+[\s\S]*?["'\s\w\/\-](?:>|$)|<!--inline\[([^\]]+)\]-->|(<!(?:--)?\[[^>]+>)|<!--(?!\[|>)([\s\S]*?)(-->|$)|\bstyle\s*=\s*("(?:[^\\"\r\n\f]|\\[\s\S])+"|'(?:[^\\'\n\r\f]|\\[\s\S])+')/ig;
    callback = callback || function(m, $1, $2, $3, $4, $5, $6, $7, $8, $9, $10) {
      if ($1) { //<script>
        var embed = '';
        $1 = $1.replace(/(\s(?:data-)?src\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value) {
          if (isInline(hack.util.query(value))) {
            embed += map.embed.wrap(value);
            return '';
          } else {
            return prefix + map.uri.wrap(value);
          }
        });
        if (embed) {
          //embed file
          m = $1 + embed;
        } else {
          m = xLang($1, $2, file, rType.test($1) ? (RegExp.$3 === 'javascript' ? 'js' : 'html') : 'js');
        }
      } else if ($3) { //<style>
        m = xLang($3, $4, file, 'css');
      } else if ($5) { //<img|embed|audio|video|link|object|source>
        var tag = $5.toLowerCase();
        if (tag === 'link') {
          var inline = '',
            isCssLink = false,
            isImportLink = false;
          var result = m.match(/\srel\s*=\s*('[^']+'|"[^"]+"|[^\s\/>]+)/i);
          if (result && result[1]) {
            var rel = result[1].replace(/^['"]|['"]$/g, '').toLowerCase();
            isCssLink = rel === 'stylesheet';
            isImportLink = rel === 'import';
          }
          m = m.replace(/(\s(?:data-)?href\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(_, prefix, value) {
            if ((isCssLink || isImportLink) && isInline(hack.util.query(value))) {
              if (isCssLink) {
                inline += '<style' + m.substring(5).replace(/\/(?=>$)/, '').replace(/\s+(?:charset|href|data-href|hreflang|rel|rev|sizes|target)\s*=\s*(?:'[^']+'|"[^"]+"|[^\s\/>]+)/ig, '');
              }
              inline += map.embed.wrap(value);
              if (isCssLink) {
                inline += '</style>';
              }
              return '';
            } else {
              return prefix + map.uri.wrap(value);
            }
          });
          m = inline || m;
        } else if (tag === 'object') {
          m = m.replace(/(\sdata\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value) {
            return prefix + map.uri.wrap(value);
          });
        } else {
          m = m.replace(/(\s(?:(?:data-)?src(?:set)?|poster)\s*=\s*)('[^']+'|"[^"]+"|[^\s\/>]+)/ig, function(m, prefix, value) {
            var key = isInline(hack.util.query(value)) ? 'embed' : 'uri';
            if (prefix.indexOf('srcset') != -1) {
              //support srcset
              var info = hack.util.stringQuote(value);
              var srcset = [];
              info.rest.split(',').forEach(function(item) {
                var p;
                item = item.trim();
                if ((p = item.indexOf(' ')) == -1) {
                  srcset.push(item);
                  return;
                }
                srcset.push(map['uri'].wrap(item.substr(0, p)) + item.substr(p));
              });
              return prefix + info.quote + srcset.join(', ') + info.quote;
            }
            return prefix + map[key].wrap(value);
          });
        }
      } else if ($6) {
        m = map.embed.wrap($6);
      } else if ($8) {
        m = '<!--' + analyseComment($8) + $9;
      } else if ($10) {
        var quote = $10[0];
        m = 'style=' + quote + map.inlineStyle.wrap($10.substring(1, $10.length - 1)) + quote;
      }
      return m;
    };
    content = content.replace(reg, callback);
  
    var info = {
      file: file,
      content: content
    };
  
    hack.emit('standard:html', info);
  
    return info.content;
  }
  
  /**
   * 处理type类型为 `x-**` 的block标签。
   *
   * ```css
   * <head>
   *   <style type="x-scss">
   *    &commat;import "compass/css3";
   *
   *    #border-radius {
   *      &commat;include border-radius(25px);
   *    }
   *   </style>
   * </head>
   * ```
   * @param  {String} tag        标签
   * @param  {String} content    the content of file
   * @param  {File} file         hack.file instance
   * @param  {String} defaultExt what is ?
   * @return {String}
   * @function
   * @memberOf hack.compile
   */
  function xLang(tag, content, file, defaultExt) {
    var ext = defaultExt;
  
    if (file.pipeEmbed === false) {
      switch (ext) {
        case 'html':
          content = extHtml(content, null, file);
          break;
  
        case 'js':
          content = extJs(content, null, file);
          break;
  
        case 'css':
          content = extCss(content, null, file);
          break;
      }
  
      return tag + content;
    } else {
      var isXLang = false;
  
      var m = rType.exec(tag);
      if (m) {
        var lang = m[3].toLowerCase();
  
        switch (lang) {
          case 'javascript':
            ext = 'js';
            break;
  
          case 'css':
            ext = 'css';
            break;
  
          default:
            if (lang.substring(0, 2) === 'x-') {
              ext = lang.substring(2);
              isXLang = true;
            }
            break;
        }
      }
  
      if (isXLang) {
        var mime = _.getMimeType(ext);
        mime && (mime !== 'application/x-' + ext) && (tag = tag.replace(rType, function(all, quote) {
          return ' type=' + quote + mime + quote;
        }));
      }
    }
  
    return tag + map.xlang.wrap(content, ext);
  }
  
  /**
   * 单文件编译入口，与 {@link hack.compile} 不同的是，此方法内部不进行缓存判断。
   * @param  {File} file 文件对象
   * @memberOf hack.compile
   * @name process
   * @function
   */
  function process(file, context) {
    hack.emit('process:start', file);
    pipe(file, 'parser');
    pipe(file, 'preprocessor');
    pipe(file, 'standard');
    postStandard(file, context);
    pipe(file, 'postprocessor');
    pipe(file, 'optimizer');
    hack.emit('process:end', file);
  }
  
  /**
   * 让文件像管道一样经过某个流程处理。注意，跟 stream 的 pipe 不同，此方法不支持异步，而是同步的处理。
   * @memberOf hack.compile
   * @inner
   * @name pipe
   * @function
   * @param {File} file 文件对象
   * @param {String} type 类型
   * @param {Boolean} [keep] 是否保留文件内容。
   */
  function pipe(file, type, keep) {
    var processors = [];
    var prop = file[type];
  
    if (type === 'standard' && 'undefined' === typeof prop) {
      processors.push('builtin');
    }
  
    if (prop) {
      var typeOf = typeof prop;
      if (typeOf === 'string') {
        prop = prop.trim().split(/\s*,\s*/);
      } else if (!Array.isArray(prop)) {
        prop = [prop];
      }
  
      processors = processors.concat(prop);
    }
  
    hack.emit('compile:' + type, file);
  
    if (processors.length) {
  
      // 过滤掉同名的插件, 没必要重复操作。
      processors = processors.filter(function(item, idx, arr) {
        item = item.__name || item;
  
        return idx === _.findIndex(arr, function(target) {
          target = target.__name || target;
  
          return target === item;
        });
      });
  
      var callback = function(processor, settings, key) {
        settings.filename = file.realpath;
        var content = file.getContent();
        try {
          hack.log.debug('pipe [' + key + '] start');
          var result = processor(content, file, settings);
          hack.log.debug('pipe [' + key + '] end');
          if (keep) {
            file.setContent(content);
          } else if (typeof result === 'undefined') {
            hack.log.warning('invalid content return of pipe [' + key + ']');
          } else {
            file.setContent(result);
          }
        } catch (e) {
          if (typeof e === 'string') {
            e = new Error(e);
          }
  
          //log error
          hack.log.debug('pipe [' + key + '] fail');
          var msg = key + ': ' + String(e.message || e.msg || e).trim() + ' [' + (e.filename || file.realpath);
          if (e.hasOwnProperty('line')) {
            msg += ':' + e.line;
            if (e.hasOwnProperty('col')) {
              msg += ':' + e.col;
            } else if (e.hasOwnProperty('column')) {
              msg += ':' + e.column;
            }
          }
          msg += ']';
          e.message = msg;
          error(e);
        }
      };
  
      processors.forEach(function(processor, index) {
        var typeOf = typeof processor,
          key, options;
  
        if (typeOf === 'object' && processor.__name) {
          options = processor;
          processor = processor.__name;
          typeOf = typeof processor;
        }
  
        if (typeOf === 'string') {
          key = type + '.' + processor;
          processor = (type === 'standard' && processor === 'builtin') ? builtinStandard : hack.require(type, processor);
        } else {
          key = type + '.' + index;
        }
  
        if (typeof processor === 'function') {
          var settings = {};
          _.assign(settings, processor.defaultOptions || processor.options || {});
          _.assign(settings, hack.media().get('settings.' + key, {}));
          _.assign(settings, options || {});
  
          // 删除隐藏配置
          delete settings.__name;
          delete settings.__plugin;
          delete settings.__pos;
          delete settings.__isPlugin;
  
          callback(processor, settings, key);
        } else {
          hack.log.warning('invalid processor [modules.' + key + ']');
        }
      });
    }
  }
  
  var lockedMap = {};
  
  /*
   * error收集&输出
   * @param  {String} msg 输出的
   */
  function error(msg) {
    //for watching, unable to exit
    lockedMap = {};
    hack.log.error(msg);
  }
  
  /*
   * 检查依赖是否存在闭环
   * @param  {String} from
   * @param  {String} to
   * @return {Boolean}
   */
  function lockedCheck(from, to) {
    from = hack.file.wrap(from).realpath;
    to = hack.file.wrap(to).realpath;
    if (from === to) {
      return true;
    } else if (lockedMap[to]) {
      var prev = from;
      var msg = [];
  
      do {
        msg.unshift(prev);
        prev = lockedMap[prev];
      } while (prev);
  
      prev && msg.unshift(prev);
      msg.push(to);
      return msg;
    }
    return false;
  }
  
  /*
   * 设置 lockedMap 的值，用来 check.
   */
  function lock(from, to) {
    from = hack.file.wrap(from).realpath;
    to = hack.file.wrap(to).realpath;
  
    lockedMap[to] = from;
  }
  
  /*
   * 删除的对应的 lockedMap 的值
   * @param  {Object} file
   */
  function unlock(to) {
    to = hack.file.wrap(to).realpath;
    delete lockedMap[to];
  }
  
  /*
   * 添加依赖
   * @param {Object} a
   * @param {Object} b
   */
  function addDeps(a, b) {
    if (a && a.cache && b) {
      if (b.cache) {
        a.cache.mergeDeps(b.cache);
      }
      a.cache.addDeps(b.realpath || b);
    }
  }
  
  function addMissingDeps(file, value) {
    if (file && file.cache && value) {
      if (value[0] === '"' || value[0] === "'") {
        value = value.substring(1, value.length - 1);
      }
  
      var filepath = _.isAbsolute(value) ? value : path.join(file.dirname, value), value;
  
      file.cache.addMissingDeps(filepath, value);
    }
  }
  
  /**
   * 内置的标准化处理函数，外部可以覆写此过程。
   *
   * - 对 html 文件进行 {@link hack.compile.extHtml} 处理。
   * - 对 js 文件进行 {@link hack.compile.extjs} 处理。
   * - 对 css 文件进行 {@link hack.compile.extCss} 处理。
   *
   * @param  {String} content 文件内容
   * @param  {File} file    文件对象
   * @param  {Object} conf    标准化配置项
   * @memberOf hack.compile
   * @inner
   */
  function builtinStandard(content, file, conf) {
    if (typeof content === 'string') {
      hack.log.debug('builtin standard for [%s] start', file.realpath);
      var type;
      if (conf.type && conf.type !== 'auto') {
        type = conf.type;
      } else {
        type = file.isHtmlLike ? 'html' : (file.isJsLike ? 'js' : (file.isCssLike ? 'css' : ''));
      }
  
      switch (type) {
        case 'html':
          content = extHtml(content, null, file);
          break;
  
        case 'js':
          content = extJs(content, null, file);
          break;
  
        case 'css':
          content = extCss(content, null, file);
          break;
  
        default:
          // unrecognized.
          break;
      }
      hack.log.debug('builtin standard for [%s] end', file.realpath);
    }
    return content;
  }
  
  /**
   * 将中间码还原成源码。
   *
   * 中间码说明：（待补充）
   *
   * @inner
   * @memberOf hack.compile
   * @param  {file} file 文件对象
   */
  function postStandard(file, context) {
    hack.emit('standard:restore:start', file);
    var content = file.getContent();
  
    if (typeof content !== 'string') {
      return;
    }
  
    hack.log.debug('postStandard start');
  
    var reg = map.reg;
    // 因为处理过程中可能新生成中间码，所以要拉个判断。
    while (reg.test(content)) {
      reg.lastIndex = 0; // 重置 regexp
      content = content.replace(reg, function(all, type, depth, value, extra) {
        var ret = '',
          info, id;
        try {
          switch (type) {
            case 'id':
              info = hack.project.lookup(value, file);
              ret = info.quote + info.id + info.quote;
  
              if (info.file && info.file.isFile()) {
                file.addLink(info.file.subpath);
              }
              break;
            case 'moduleId':
              info = hack.project.lookup(value, file);
              ret = info.quote + info.moduleId + info.quote;
  
              if (info.file && info.file.isFile()) {
                file.addLink(info.file.subpath);
              }
              break;
            case 'hash':
              info = hack.project.lookup(value, file);
              if (info.file && info.file.isFile()) {
  
                file.addLink(info.file.subpath);
  
                var locked = lockedCheck(file, info.file);
                if (!locked) {
                  lock(file, info.file);
                  exports(info.file, context);
                  unlock(info.file);
                  addDeps(file, info.file);
                }
  
                var md5 = info.file.getHash();
                ret = info.quote + md5 + info.quote;
              } else {
                ret = value;
                addMissingDeps(file, value);
              }
              break;
            case 'require':
            case 'jsRequire':
              info = hack.project.lookup(value, file);
              file.addRequire(info.id);
  
              if (type === 'jsRequire' && info.moduleId) {
                ret = info.quote + info.moduleId + info.quote;
              } else {
                ret = info.quote + info.id + info.quote;
              }
  
              if (info.file && info.file.isFile()) {
                file.addLink(info.file.subpath);
              }
              break;
  
            case 'async':
            case 'jsAsync':
              info = hack.project.lookup(value, file);
              file.addAsyncRequire(info.id);
  
              if (type === 'jsAsync' && info.moduleId) {
                ret = info.quote + info.moduleId + info.quote;
              } else {
                ret = info.quote + info.id + info.quote;
              }
  
              if (info.file && info.file.isFile()) {
                file.addLink(info.file.subpath);
              }
              break;
            case 'uri':
              info = hack.project.lookup(value, file);
              if (info.file && info.file.isFile()) {
  
                file.addLink(info.file.subpath);
  
                if (info.file.useHash) {
                  var locked = lockedCheck(file, info.file);
                  if (!locked) {
                    lock(file, info.file);
                    exports(info.file, context);
                    unlock(info.file);
                    addDeps(file, info.file);
                  }
                }
                var query = (info.file.query && info.query) ? '&' + info.query.substring(1) : info.query;
                var url = info.file.getUrl();
                var hash = info.hash || info.file.hash;
                ret = info.quote + url + query + hash + info.quote;
              } else {
                ret = value;
                addMissingDeps(file, value);
              }
              break;
            case 'dep':
              if (file.cache) {
                info = hack.project.lookup(value, file);
                addDeps(file, info.file);
              } else {
                hack.log.warning('unable to add deps to file [' + path + ']');
                addMissingDeps(file, value);
              }
              break;
            case 'embed':
            case 'jsEmbed':
              info = hack.project.lookup(value, file);
              var f;
              if (info.file) {
                f = info.file;
              } else if (hack.util.isAbsolute(info.rest)) {
                f = hack.file(info.rest);
              }
              if (f && f.isFile()) {
                file.addLink(f.subpath);
                var locked = lockedCheck(file, info.file);
                if (!locked) {
                  lock(file, f);
                  f.isInline = true;
                  exports(f, context);
                  unlock(f);
                  addDeps(file, f);
                  copyInfo(f, file);
  
                  if (f.isText()) {
                    ret = f.getContent();
                    if (type === 'jsEmbed' && !f.isJsLike && !f.isJsonLike) {
                      ret = JSON.stringify(ret);
                    }
                  } else {
                    ret = info.quote + f.getBase64() + info.quote;
                  }
                } else {
                  var msg = 'unable to embed file[' + file.realpath + '] into itself.';
  
                  if (locked.splice) {
                    msg = 'circular embed `' + locked.join('` -> `') + '`.';
                  }
  
                  error(msg);
                }
              } else {
                hack.log.error('unable to embed non-existent file %s', value);
              }
              break;
            case 'xlang':
              ret = partial(value, file, extra);
              break;
  
            case 'inlineStyle':
              ret = partial('inline-style-placeholder {' + value + '}', file, {
                ext: 'css',
                xLang: ':inline-style'
              });
              ret = ret.replace(/inline-style-placeholder\s?\{([\s\S]*)\}/, '$1');
              break;
  
            case 'sourceMap':
              info = hack.project.lookup(value, file, true);
              if (info.file && info.file.isFile()) {
  
                file.addLink(info.file.subpath);
  
                if (info.file.useHash) {
                  var locked = lockedCheck(file, info.file);
                  if (!locked) {
                    lock(file, info.file);
                    exports(info.file, context);
                    unlock(info.file);
                    addDeps(file, info.file);
                  }
                }
                var query = (info.file.query && info.query) ? '&' + info.query.substring(1) : info.query;
                var url = info.file.getUrl();
                var hash = info.hash || info.file.hash;
                ret = info.quote + url + query + hash + info.quote;
  
                file.extras = file.extras || {};
                file.extras.derived = file.extras.derived || [];
                file.extras.derived.push(info.file);
              } else {
                ret = value;
              }
              break;
  
              // 用来存信息的，内容会被移除。
            case 'info':
              ret = '';
              break;
            default:
              if (!map[type]) {
                hack.log.error('unsupported hack language tag [%s]', type);
              }
          }
  
          // trigger event.
          var message = {
            ret: ret,
            value: value,
            file: file,
            info: info,
            type: type
          };
          hack.emit('standard:restore', message);
          hack.emit('standard:restore:' + type, message);
          ret = message.ret;
        } catch (e) {
          lockedMap = {};
          e.message = e.message + ' in [' + file.subpath + ']';
          throw e;
        }
        return ret;
      });
    }
    file.setContent(content);
    hack.emit('standard:restore:end', file);
    hack.log.debug('postStandard end');
  }
  
  /**
   * 编译代码片段。用于在 html 中内嵌其他异构语言。
   * @param {String} content 代码片段。
   * @param {File} host 代码片段所在的文件，用于片段中对其他资源的查找。
   * @param {Object} info 文件信息。
   * @memberOf hack.compile
   * @example
   * var file = hack.file(root, 'static/_nav.tmpl');
   * var content = file.getContent();
   *
   * // tmpl 文件本身是 html 文件，但是会被解析成 js 文件供 js 使用。正常情况下他只有 js 语言能力。但是：
   * content = hack.compile.partial(content, file, {
   *    ext: 'html' // set isHtmlLike
   * });
   *
   * file.setConent(content);
   *
   * // 继续走之后的 js parser 流程。
   */
  function partial(content, host, info) {
  
    // 默认 html 内嵌的 js, css 内容，都会独立走一遍单文件编译流程。
    // 可以通过 pipeEmbed 属性设置成 `false` 来关闭。
    if (host.pipeEmbed === false) {
      return content;
    }
  
    if (content.trim()) {
      info = typeof info === 'string' ? {
        ext: info
      } : (info || {});
      var ext = info.ext || host.ext;
      ext[0] === '.' && (ext = ext.substring(1));
      info.ext = '.' + ext;
      info.xLang = info.xLang || (':' + ext);
  
      var f = hack.file(host.realpath, info);
      f.cache = host.cache;
      f.isPartial = true;
      f.isInline = true;
      f.setContent(content);
      process(f);
      copyInfo(f, host);
      content = f.getContent();
    }
  
    return content;
  }
  
  function copyInfo(src, dst) {
    var addFn = {
      'requires': 'addRequire',
      'asyncs': 'addAsyncRequire',
      'links': 'addLink'
    };
  
    Object.keys(addFn).forEach(function(key) {
      src[key].forEach(function(item) {
        dst[addFn[key]](item);
      });
    });
  }
  
  function resourceMapFile(file) {
    var content = file.getContent();
    var reg = /\b__RESOURCE_MAP__\b/g;
    // 如果写文档时包含了此字符，不应该替换，所以强制判断一下
    if (content.match(reg) && typeof file._isResourceMap == 'undefined') {
      file._isResourceMap = true; // special file
      file.useCache = false; // disable cache
      return true;
    }
    return false;
  }
  
  /*
   * adjust file
   * @param file
   */
  function adjust(file) {
    if (file.isText()) {
      resourceMapFile(file);
    }
  }
  
  exports.process = process;
  exports.extJs = extJs;
  exports.extCss = extCss;
  exports.extHtml = extHtml;
  exports.xLang = xLang;
  exports.partial = partial;
  exports.isInline = isInline;
  exports.analyseComment = analyseComment;
  