code: function analyze (entry, options) {
    options = options || {}
  
    let result = {}
  
    // support array
    if (!Array.isArray(entry)) {
      entry = [entry]
    }
  
    entry.forEach(file => {
      let content
  
      // normalize file
      if (typeof file === 'string') {
        content = fs.readFileSync(file, 'utf8')
      } else {
        content = file.content
        file = file.file
      }
  
      let filter = options.filter
      let depResolve = options.depResolve
      let depth = options.depth
  
      analyzeFile(file, content, filter, depResolve, depth, result)
    })
  
    return result
  }
docs: analyze dependencies of a file
  
  ```
  return {
  <file>: {
  deps: [{               // all deps
  parent: currFile,    // parent file path
  type: type,          // current file type (js/css)
  raw: dep,            // raw dependency name (require('./xxx') => './xxx')
  name: null,          // formated dependency name ('~@alife/xxx' => '@alife/xxx')
  module: null,        // module name (only external module)
  file: null,          // resolved file name (only relative file)
  }, ...],
  relatives: [ ... ],    // relative deps' absolute path
  modules: [ ... ],      // all external modules name
  },
  ...
  }
  ```
  
  @param  {Mixed} entry the analyze entry (or entries)
  - {Array} multiple files, the item of Array should be String or Object
  - {String} single file path
  - {Object} single file path and content
  - file: {String} file path
  - content: {String} file content
  @param  {Object} options analyze options
  - filter: {Function} filter deps, will be invoked with `filter(dep, currFile)`
  - depResolve: {Function} custom resolve for relative file, will be invoked with `resolve(dep, currFile, defaultResolve)`
  - depth: {Number} set max depth to analyze (default is Infinity)
  @return {Object}