[
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  'env': {\n    'browser': false,\n    'commonjs': true,\n    'node': true,\n    'es6': true\n  },\n  'parserOptions': {\n    'ecmaVersion': 8\n  },\n  'extends': 'eslint:recommended',\n  'rules': {\n    'indent': [\n      'error',\n      2,\n      {\n        'FunctionDeclaration': {\n          'parameters': 'first'\n        },\n        'FunctionExpression': {\n          'parameters': 'first'\n        },\n        'CallExpression': {\n          'arguments': 'first'\n        }\n      }\n    ],\n    'linebreak-style': [\n      'error',\n      'unix'\n    ],\n    'quotes': [\n      'error',\n      'single'\n    ],\n    'semi': [\n      'error',\n      'always'\n    ],\n    'max-len': [\n      'error',\n      80,\n      2\n    ]\n  }\n};\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules/\nnpm-debug.log\n.nyc_output/\ncoverage/\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nlanguage: node_js\nnode_js:\n  - \"stable\"\n"
  },
  {
    "path": "README.md",
    "content": "# CommonJS Tree Shaker\n[![NPM version](https://badge.fury.io/js/common-shake.svg)](http://badge.fury.io/js/common-shake)\n[![Build Status](https://secure.travis-ci.org/indutny/common-shake.svg)](http://travis-ci.org/indutny/common-shake)\n\nSee [webpack-common-shake][0] for [webpack][1] plugin.\n\n## Usage\n\n```js\nconst acorn = require('acorn');\nconst Analyzer = require('common-shake').Analyzer;\n\nconst a = new Analyzer();\n\na.run(acorn.parse(`\n  'use strict';\n  const lib = require('./a.js');\n  exports.a = lib.a;\n`, { locations: true }), 'index.js');\n\na.run(acorn.parse(`\n  'use strict';\n  exports.a = 42;\n`, { locations: true }), 'a.js');\n\na.resolve('index.js', './a.js', 'a.js');\nconsole.log(a.isSuccess(), a.bailouts);\n// true false\n\nconsole.log(a.getModule('index.js').getInfo());\n// { bailouts: false, declarations: [ 'a' ], uses: [] }\n\nconsole.log(a.getModule('a.js').getInfo());\n// { bailouts: false, declarations: [ 'a' ], uses: [ 'a' ] }\n\nconst module = a.getModule('a.js');\na.getDeclarations().forEach((decl) => {\n  console.log(module.isUsed(decl.name) ? 'used' : 'not used');\n  console.log(decl.name, decl.ast);\n});\n\n// If you want to mark all exported values of module as used:\na.getModule('root').forceExport();\n```\n\n## Graphviz\n\nFor debugging and inspection purposes a graph in [dot][2] format may be\ngenerated from the modules hierarchy using following API:\n\n```js\nconst Graph = require('common-shake').Graph;\nconst graph = new Graph('/path/to/working/dir');\n\nconsole.log(graph.generate(analyzer.getModules()));\n```\n\n## LICENSE\n\nThis software is licensed under the MIT License.\n\nCopyright Fedor Indutny, 2017.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to permit\npersons to whom the Software is furnished to do so, subject to the\nfollowing conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\nNO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\nOTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\nUSE OR OTHER DEALINGS IN THE SOFTWARE.\n\n[0]: https://github.com/indutny/webpack-common-shake\n[1]: https://webpack.github.io/\n[2]: http://www.graphviz.org/content/dot-language\n"
  },
  {
    "path": "lib/shake/analyzer.js",
    "content": "'use strict';\n\nconst escope = require('escope');\nconst debug = require('debug')('common-shake:analyzer');\n\nconst shake = require('../shake');\nconst walk = shake.walk;\nconst Module = shake.Module;\n\nfunction Analyzer() {\n  // All `Module` instances by resource\n  this.modules = new Map();\n\n  // All unresolved `Module` instances by parent resource + path\n  this.unresolved = new Map();\n\n  // Uses of required module. Map from ast node to `Module` instance\n  this.moduleUses = null;\n\n  // Uses of `exports` in module. A collection of AST nodes.\n  this.exportsUses = null;\n\n  // Uses of `require` in module. A collection of AST nodes.\n  this.requireUses = null;\n\n  // Any global bailouts\n  this.bailouts = false;\n}\nmodule.exports = Analyzer;\n\n// Public API\n\nAnalyzer.prototype.run = function run(ast, resource) {\n  this.requireUses = new Set();\n  this.exportsUses = new Set();\n  this.moduleUses = new Map();\n\n  const current = this.getModule(resource);\n\n  this.gather(ast, current);\n  this.sift(ast, current);\n\n  this.moduleUses = null;\n  this.exportsUses = null;\n  this.requireUses = null;\n\n  return current;\n};\n\nAnalyzer.prototype.resolve = function resolve(issuer, name, to) {\n  debug('resolve %j:%j => %j', issuer, name, to);\n\n  const unresolved = this.getUnresolvedModule(issuer, name);\n  const resolved = this.getModule(to);\n\n  // Already resolved\n  if (unresolved === resolved)\n    return;\n\n  resolved.mergeFrom(unresolved);\n  resolved.addIssuer(this.getModule(issuer));\n  this.unresolved.get(issuer).set(name, to);\n};\n\nAnalyzer.prototype.getModule = function getModule(resource) {\n  let module;\n  if (this.modules.has(resource)) {\n    module = this.modules.get(resource);\n  } else {\n    module = new Module(resource);\n    this.modules.set(resource, module);\n  }\n  return module;\n};\n\nAnalyzer.prototype.getModules = function getModules() {\n  return Array.from(this.modules.values());\n};\n\nAnalyzer.prototype.isSuccess = function isSuccess() {\n  return this.bailouts === false;\n};\n\n// Private API\n\nAnalyzer.prototype.gather = function gather(ast, current) {\n  const manager = escope.analyze(ast, {\n    ecmaVersion: 6,\n    sourceType: 'module',\n    optimistic: true,\n    ignoreEval: true,\n    impliedStrict: true\n  });\n\n  const scope = manager.acquireAll(ast);\n\n  const declarations = [];\n\n  const queue = scope.slice();\n  while (queue.length !== 0) {\n    const scope = queue.shift();\n\n    for (let i = 0; i < scope.childScopes.length; i++)\n      queue.push(scope.childScopes[i]);\n\n    // Skip variables declared in dynamic scopes\n    if (scope.dynamic)\n      continue;\n\n    for (let i = 0; i < scope.variables.length; i++)\n      declarations.push(scope.variables[i]);\n  }\n\n  // Just to avoid double-bailouts\n  const seenDefs = new Set();\n  for (let i = 0; i < declarations.length; i++) {\n    const decl = declarations[i];\n\n    const defs = decl.defs.filter(def => this.isRequireDef(def, decl));\n    if (defs.length === 0)\n      continue;\n\n    if (decl.defs.length !== 1) {\n      defs.forEach((def) => {\n        if (seenDefs.has(def.node))\n          return;\n        seenDefs.add(def.node);\n\n        const name = def.node.init.arguments[0].value;\n        const module = this.getUnresolvedModule(current.resource, name);\n\n        module.bailout('`require` variable override', def.node.loc,\n                       current.resource);\n      });\n      continue;\n    }\n\n    const node = defs[0].node;\n    if (seenDefs.has(node))\n      continue;\n    seenDefs.add(node);\n\n    const name = shake.evaluateConst(node.init.arguments[0]);\n    const module = this.getUnresolvedModule(current.resource, name);\n\n    // Destructuring\n    if (node.id.type === 'ObjectPattern') {\n      this.gatherDestructured(module, node.id, current);\n      continue;\n    }\n\n    if (node.id.type !== 'Identifier') {\n      module.bailout('`require` used in unknown way', node.loc,\n                     current.resource);\n      continue;\n    }\n\n    for (let i = 0; i < decl.references.length; i++) {\n      const ref = decl.references[i];\n      if (ref.identifier !== node.id)\n        this.moduleUses.set(ref.identifier, module);\n    }\n  }\n};\n\nAnalyzer.prototype.gatherDestructured = function gatherDestructured(module,\n                                                                    id,\n                                                                    current) {\n  for (let i = 0; i < id.properties.length; i++) {\n    const prop = id.properties[i];\n\n    if (prop.key.type !== (prop.computed ? 'Literal' : 'Identifier')) {\n      module.bailout('Dynamic properties in `require` destructuring', id.loc,\n                     current.resource);\n      continue;\n    }\n\n    const key = prop.key.name || prop.key.value;\n    module.use(key, current, false);\n  }\n};\n\nAnalyzer.prototype.isRequireDef = function isRequireDef(def, decl) {\n  if (def.type !== 'Variable')\n    return false;\n\n  const node = def.node;\n  if (node.type !== 'VariableDeclarator')\n    return false;\n\n  if (node.id.type === 'Identifier') {\n    if (node.id.name === 'exports') {\n      this.markOverriddenUses(this.exportsUses, decl.references);\n      return false;\n    } else if (node.id.name === 'require') {\n      this.markOverriddenUses(this.requireUses, decl.references);\n      return false;\n    }\n  }\n\n  const init = node.init;\n  if (!init || init.type !== 'CallExpression')\n    return false;\n\n  if (init.callee.type !== 'Identifier' || init.callee.name !== 'require')\n    return false;\n\n  const args = init.arguments;\n  if (args.length < 1)\n    return false;\n\n  try {\n    shake.evaluateConst(args[0]);\n  } catch (e) {\n    return false;\n  }\n\n  // Overridden `require`\n  if (this.requireUses.has(init.callee))\n    return false;\n  this.requireUses.add(init.callee);\n\n  return true;\n};\n\nAnalyzer.prototype.markOverriddenUses = function markOverriddenUses(set, refs) {\n  for (let i = 0; i < refs.length; i++)\n    set.add(refs[i].identifier);\n};\n\nAnalyzer.prototype.isExports = function isExports(node) {\n  // `exports`\n  if (node.type === 'Identifier' && node.name === 'exports')\n    return true;\n\n  // `module.exports`\n  if (node.type !== 'MemberExpression')\n    return false;\n\n  const isStatic =\n      node.property.type === (node.computed ? 'Literal' : 'Identifier');\n  if (!isStatic)\n    return false;\n\n  const key = node.property.name || node.property.value;\n  return key === 'exports';\n};\n\nAnalyzer.prototype.sift = function sift(ast, current) {\n  walk(ast, {\n    AssignmentExpression: node => this.siftAssignment(node, current),\n    MemberExpression: node => this.siftMember(node, current, false),\n    Identifier: node => this.siftRequireUse(node, current),\n    CallExpression: node => this.siftCall(node, current),\n    NewExpression: node => this.siftNew(node, current),\n    UnaryExpression: node => this.siftUnaryExpression(node),\n  });\n\n  this.moduleUses.forEach((module, use) => {\n    module.bailout('Escaping value or unknown use', use.loc, current.resource);\n  });\n};\n\nAnalyzer.prototype.siftAssignment = function siftAssignment(node, current) {\n  if (node.left.type === 'Identifier') {\n    if (node.left.name === 'exports') {\n      if (this.exportsUses.has(node.left))\n        return;\n      this.exportsUses.add(node.left);\n\n      current.bailout('`exports` assignment', node.loc);\n      return;\n    }\n    if (node.left.name === 'require') {\n      if (this.requireUses.has(node.left))\n        return;\n\n      this.requireUses.add(node.left);\n      current.bailout('`require` assignment', node.loc);\n      return;\n    }\n  }\n\n  if (node.left.type !== 'MemberExpression')\n    return;\n\n  const member = node.left;\n\n  if (this.moduleUses.has(member.object)) {\n    const module = this.moduleUses.get(member.object);\n    this.moduleUses.delete(member.object);\n    module.bailout('Module property assignment', node.loc, current.resource);\n    return;\n  }\n\n  const isExports = this.isExports(member.object);\n  if (!isExports && member.object.type !== 'Identifier')\n    return;\n\n  const object = isExports ? 'exports' : member.object.name;\n  if (object !== 'exports' && object !== 'module')\n    return;\n\n  if (member.property.type !== (member.computed ? 'Literal' : 'Identifier')) {\n    if (object === 'exports') {\n      if (this.exportsUses.has(member.object))\n        return;\n      this.exportsUses.add(member.object);\n\n      current.bailout('Dynamic CommonJS export', member.loc);\n    } else {\n      current.bailout('Dynamic `module` use', member.loc);\n    }\n    return;\n  }\n\n  const name = member.property.name || member.property.value;\n\n  if (object === 'module') {\n    if (name !== 'exports')\n      return;\n\n    this.siftModuleExports(node, current);\n    return;\n  }\n\n  if (this.exportsUses.has(member.object))\n    return;\n  this.exportsUses.add(member.object);\n\n  // `exports.a = imported.b`\n  if (node.right.type === 'MemberExpression')\n    this.siftMember(node.right, current, name);\n\n  const decl = {\n    type: 'exports',\n    name,\n    ast: node\n  };\n\n  if (!current.declare(decl)) {\n    current.bailout('Simultaneous assignment to both `exports` and ' +\n                    '`module.exports`', node.loc);\n  }\n};\n\nAnalyzer.prototype.siftModuleExports = function siftModuleExports(node,\n                                                                  current) {\n  if (node.right.type !== 'ObjectExpression') {\n    current.bailout('`module.exports` assignment', node.loc, null, 'info');\n    return;\n  }\n\n  // `module.exports = {}`\n  const props = node.right.properties;\n  const pairs = [];\n  for (let i = 0; i < props.length; i++) {\n    const prop = props[i];\n\n    if (prop.computed ||\n        (prop.key.type !== 'Literal' && prop.key.type !== 'Identifier')) {\n      current.bailout('Dynamic `module.exports` property', prop.loc, null);\n      continue;\n    }\n\n    const key = prop.key.name || prop.key.value;\n\n    // `module.exports = { a: imported.b }`\n    if (prop.kind === 'init' && prop.value.type === 'MemberExpression')\n      this.siftMember(prop.value, current, key);\n\n    pairs.push({\n      type: 'module.exports',\n      name: key,\n      ast: prop\n    });\n  }\n\n  if (!current.multiDeclare(pairs)) {\n    current.bailout('Simultaneous assignment to both `exports` and ' +\n                    '`module.exports`', node.loc);\n  }\n};\n\nAnalyzer.prototype.siftMember = function siftMember(node, current, recursive) {\n  let module;\n  if (this.isExports(node.object)) {\n    // Do not track assignments twice\n    if (this.exportsUses.has(node.object))\n      return;\n    this.exportsUses.add(node.object);\n\n    module = current;\n  } else if (node.object.type === 'Identifier' &&\n             node.object.name === 'require') {\n    // It is ok to use `require` properties\n    this.requireUses.add(node.object);\n    return;\n  } else if (this.moduleUses.has(node.object)) {\n    module = this.moduleUses.get(node.object);\n    this.moduleUses.delete(node.object);\n  } else if (node.object.type === 'CallExpression') {\n    module = this.siftRequireCall(node.object, current);\n    if (!module)\n      return;\n  } else {\n    return;\n  }\n\n  if (node.property.type !== (node.computed ? 'Literal' : 'Identifier')) {\n    if (module === current) {\n      module.bailout('Dynamic CommonJS use', node.loc);\n    } else {\n      const reason = 'Dynamic CommonJS import';\n      module.bailout(reason, node.loc, current.resource);\n    }\n    return;\n  }\n\n  // TODO(indutny): build dependency tree for self-uses. They should not retain\n  // themselves or others if unused.\n  const prop = node.property.name || node.property.value;\n  module.use(prop, current, recursive);\n\n  // For recursive imports\n  return { module, property: prop };\n};\n\nAnalyzer.prototype.siftCall = function siftCall(node, current) {\n  // `lib()`\n  if (this.moduleUses.has(node.callee)) {\n    const module = this.moduleUses.get(node.callee);\n    this.moduleUses.delete(node.callee);\n    module.bailout('Imported library call', node.loc, current.resource, 'info');\n    return;\n  }\n\n  const module = this.siftRequireCall(node, current);\n  if (!module)\n    return;\n\n  // TODO(indutny): support `var lib; lib = require('...')`\n  module.bailout('Escaping `require` call', node.loc, current.resource);\n};\n\nAnalyzer.prototype.siftNew = function siftNew(node, current) {\n  // `new lib()`\n  if (!this.moduleUses.has(node.callee))\n    return;\n\n  const module = this.moduleUses.get(node.callee);\n  this.moduleUses.delete(node.callee);\n  module.bailout('Imported library new call', node.loc, current.resource,\n                 'info');\n};\n\nAnalyzer.prototype.siftUnaryExpression = function siftUnaryExpression(node) {\n  if (node.operator !== 'typeof')\n    return false;\n\n  // Mark `typeof require` as a valid use of `require`\n  const argument = node.argument;\n  if (argument.type !== 'Identifier' || argument.name !== 'require')\n    return false;\n\n  if (this.requireUses.has(argument))\n    return false;\n  this.requireUses.add(argument);\n};\n\nAnalyzer.prototype.siftRequireCall = function siftRequireCall(node, current) {\n  const callee = node.callee;\n  if (callee.type !== 'Identifier' || callee.name !== 'require')\n    return false;\n\n  // Valid `require` use\n  if (this.requireUses.has(callee))\n    return false;\n  this.requireUses.add(callee);\n\n  const args = node.arguments;\n  if (args.length < 1)\n    return false;\n\n  let arg;\n  let fail = false;\n  try {\n    arg = shake.evaluateConst(args[0]);\n  } catch (e) {\n    fail = true;\n  }\n\n  if (fail || typeof arg !== 'string') {\n    const msg = 'Dynamic argument of `require`';\n    current.bailout(msg, node.loc);\n    this.bailout(msg, node.loc, current.resource);\n    return false;\n  }\n\n  // TODO(indutny): support `require('./lib')()`\n  return this.getUnresolvedModule(current.resource, arg);\n};\n\nAnalyzer.prototype.siftRequireUse = function siftRequireUse(node, current) {\n  if (node.type !== 'Identifier' || node.name !== 'require')\n    return;\n\n  if (this.requireUses.has(node))\n    return;\n  this.requireUses.add(node);\n\n  current.bailout('Invalid use of `require`', node.loc);\n  this.bailout('Invalid use of `require`', node.loc, current.resource);\n};\n\nAnalyzer.prototype.getUnresolvedModule = function getUnresolvedModule(issuer,\n                                                                      name) {\n  let issuerMap;\n  if (this.unresolved.has(issuer)) {\n    issuerMap = this.unresolved.get(issuer);\n  } else {\n    issuerMap = new Map();\n    this.unresolved.set(issuer, issuerMap);\n  }\n\n  let module;\n  if (issuerMap.has(name)) {\n    module = issuerMap.get(name);\n  } else {\n    module = new Module(name);\n    issuerMap.set(name, module);\n  }\n\n  // Already resolved\n  if (typeof module === 'string')\n    return this.getModule(module);\n\n  return module;\n};\n\nAnalyzer.prototype.bailout = function bailout(reason, loc, source) {\n  if (this.bailouts)\n    this.bailouts.push({ reason, loc, source });\n  else\n    this.bailouts = [ { reason, loc, source } ];\n};\n"
  },
  {
    "path": "lib/shake/eval.js",
    "content": "'use strict';\n\nfunction evaluateBinary(node) {\n  const op = node.operator;\n\n  const left = evaluateConst(node.left);\n  const right = evaluateConst(node.right);\n\n  if (op === '+')\n    return left + right;\n\n  throw new Error(`Unsupported binary operation: \"${op}\"`);\n}\n\nfunction evaluateConst(node) {\n  if (node.type === 'Literal')\n    return node.value;\n\n  if (node.type === 'BinaryExpression')\n    return evaluateBinary(node);\n\n  throw new Error(`Unsupported node type: \"${node.type}\"`);\n}\nmodule.exports = evaluateConst;\n"
  },
  {
    "path": "lib/shake/graph.js",
    "content": "'use strict';\n\nconst path = require('path');\n\nfunction Graph(dir) {\n  this.dir = dir || '.';\n  this.relativeCache = new Map();\n}\nmodule.exports = Graph;\n\nGraph.prototype.generate = function generate(modules) {\n  const seen = new Set();\n  const queue = modules.slice();\n\n  let out = 'digraph {\\n';\n  out += '  ranksep=1.2;\\n';\n  while (queue.length !== 0) {\n    const module = queue.shift();\n\n    if (seen.has(module))\n      continue;\n    seen.add(module);\n\n    out += this.generateModule(module);\n  }\n  out += '}\\n';\n  return out;\n};\n\nGraph.prototype.relative = function relative(file) {\n  file = file || '';\n  if (this.relativeCache.has(file))\n    return this.relativeCache.get(file);\n\n  const relative = path.relative(this.dir, file);\n  this.relativeCache.set(file, relative);\n  return relative;\n};\n\nGraph.prototype.escape = function escape(str) {\n  return `\"${str.replace(/\"/g, '\\\\\"')}\"`;\n};\n\nGraph.prototype.declarationId = function declarationId(module, name) {\n  return this.escape(`{${this.relative(module.resource)}}[${name}]`);\n};\n\nGraph.prototype.moduleId = function moduleId(module) {\n  return this.escape(`{${this.relative(module.resource)}}/require`);\n};\n\nGraph.prototype.generateModule = function generateModule(module) {\n  const resource = this.escape('cluster://' + this.relative(module.resource));\n  const label = this.escape(this.relative(module.resource));\n\n  let out = '';\n\n  const color = module.bailouts === false ? 'black' : 'red';\n  let cluster = `  subgraph ${resource} {\\n`;\n  cluster += `    label=${label};\\n`;\n  cluster += `    color=${color};\\n`;\n  cluster += `    ${this.moduleId(module)} [label=require shape=diamond];\\n`;\n\n  const issuersSeen = new Set();\n  const declarationsSeen = new Set();\n\n  const declare = (name) => {\n    const id = this.declarationId(module, name);\n    if (declarationsSeen.has(name))\n      return id;\n\n    declarationsSeen.add(name);\n\n    const color = module.bailouts === false ?\n      module.isUsed(name) ? 'black' : 'blue' :\n      'red';\n\n    const shortId = this.escape(`${name}`);\n    cluster += `    ${id} [label=${shortId} color=${color}];\\n`;\n\n    return id;\n  };\n\n  // Add all declarations\n  module.declarations.forEach((declaration) => {\n    declare(declaration.name);\n  });\n\n  // Add uses\n  module.uses.forEach((issuers, name) => {\n    issuers.forEach((issuer) => {\n      issuersSeen.add(issuer);\n\n      out += `  ${this.moduleId(issuer)} -> ${declare(name)};\\n`;\n    });\n  });\n\n  // Add dynamic issuer edges (without particular uses)\n  module.issuers.forEach((issuer) => {\n    if (issuersSeen.has(issuer))\n      return;\n    issuersSeen.add(issuer);\n\n    out += `  ${this.moduleId(issuer)} -> ${declare('[*]')};\\n`;\n  });\n\n  cluster += '  }\\n';\n\n  return cluster + out;\n};\n"
  },
  {
    "path": "lib/shake/module.js",
    "content": "'use strict';\n\nconst debug = require('debug')('common-shake:module');\n\nfunction Module(resource) {\n  this.resource = resource;\n  this.bailouts = false;\n  this.issuers = new Set();\n  this.uses = new Map();\n  this.declarations = [];\n\n  this.pendingUses = [];\n  this.computing = false;\n  this.forced = false;\n}\nmodule.exports = Module;\n\n// Public API\n\nModule.prototype.forceExport = function forceExport() {\n  this.forced = true;\n};\n\nModule.prototype.isUsed = function isUsed(name) {\n  this.compute();\n\n  if (this.bailouts || this.forced)\n    return true;\n\n  // Detect loops\n  if (this.computing) {\n    const pending = this.pendingUses.some(use => use.property === name);\n    if (pending)\n      return true;\n  }\n\n  return this.uses.has(name);\n};\n\nModule.prototype.getInfo = function getInfo() {\n  this.compute();\n\n  return {\n    bailouts: this.bailouts,\n    declarations: this.declarations.map(decl => decl.name),\n    uses: Array.from(this.uses.keys())\n  };\n};\n\nModule.prototype.getDeclarations = function getDeclarations() {\n  return this.declarations.slice();\n};\n\n// Private API\n\nModule.prototype.bailout = function bailout(reason, loc, source, level) {\n  const bail = {\n    reason,\n    loc,\n    source: source || null,\n    level: level || 'warning'\n  };\n  if (this.bailouts)\n    this.bailouts.push(bail);\n  else\n    this.bailouts = [ bail ];\n  this.sealed = false;\n};\n\nModule.prototype.use = function use(property, from, recursive) {\n  if (recursive !== false) {\n    debug('pending use this=%j property=%j from=%j recursive=%j',\n          this.resource, property, from.resource, recursive);\n    this.pendingUses.push({ property, from, recursive });\n    return;\n  }\n\n  debug('use this=%j property=%j from=%j recursive=%j',\n        this.resource, property, from.resource, recursive);\n\n  if (this.uses.has(property))\n    this.uses.get(property).add(from);\n  else\n    this.uses.set(property, new Set([ from ]));\n};\n\nModule.prototype.seal = function seal() {\n  this.sealed = true;\n};\n\nModule.prototype.declare = function declare(property) {\n  this.declarations.push(property);\n\n  return !this.sealed;\n};\n\nModule.prototype.multiDeclare = function multiDeclare(declarations) {\n  const success = this.declarations.length === 0 && !this.sealed;\n  this.seal();\n  for (let i = 0; i < declarations.length; i++)\n    this.declarations.push(declarations[i]);\n  return success;\n};\n\nModule.prototype.mergeFrom = function mergeFrom(unresolved) {\n  if (unresolved.bailouts) {\n    unresolved.bailouts.forEach((b) => {\n      this.bailout(b.reason, b.loc, b.source, b.level);\n    });\n  }\n\n  unresolved.uses.forEach((from, property) => {\n    from.forEach(resource => this.use(property, resource, false));\n  });\n  unresolved.declarations.forEach(declaration => this.declare(declaration));\n  this.pendingUses = this.pendingUses.concat(unresolved.pendingUses);\n\n  unresolved.clear();\n};\n\nModule.prototype.addIssuer = function addIssuer(issuer) {\n  this.issuers.add(issuer);\n};\n\nModule.prototype.clear = function clear() {\n  this.uses = null;\n  this.declarations = null;\n  this.pendingUses = null;\n};\n\nModule.prototype.compute = function compute() {\n  // Already computed or cleared\n  if (this.pendingUses === null)\n    return;\n\n  if (this.computing)\n    return;\n  this.computing = true;\n  debug('compute this=%j pending=%d', this.resource, this.pendingUses.length);\n\n  // Do several passes until it will stabilize\n  // TODO(indutny): what is complexity of this? Exponential?\n  let before;\n  do {\n    before = this.pendingUses.length;\n\n    // NOTE: it is important to overwrite this, since recursive lookups will\n    // get to it.\n    this.pendingUses = this.pendingUses.filter((use) => {\n      return use.from.isUsed(use.recursive);\n    });\n    debug('compute pass this=%j before=%d after=%d',\n          this.resource, before, this.pendingUses.length);\n  } while (this.pendingUses.length !== before);\n\n  this.pendingUses.forEach(use => this.use(use.property, use.from, false));\n\n  this.pendingUses = null;\n  this.computing = false;\n};\n"
  },
  {
    "path": "lib/shake/walk.js",
    "content": "'use strict';\n\nconst walk = require('acorn/dist/walk');\n\nconst BASE = Object.assign({\n  // acorn-dynamic-import support\n  Import: () => {}\n}, walk.base);\n\n// Pre-order walker\nmodule.exports = (node, visitors) => {\n  const state = null;\n  const override = false;\n  !function c(node, st, override) {\n    var type = override || node.type, found = visitors[type];\n    if (found) found(node, st);\n    BASE[type](node, st, c);\n  }(node, state, override);\n};\n"
  },
  {
    "path": "lib/shake.js",
    "content": "'use strict';\n\nexports.walk = require('./shake/walk');\nexports.evaluateConst = require('./shake/eval');\n\nexports.Module = require('./shake/module');\nexports.Analyzer = require('./shake/analyzer');\nexports.Graph = require('./shake/graph');\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"common-shake\",\n  \"version\": \"2.1.0\",\n  \"description\": \"CommonJS Tree Shake\",\n  \"main\": \"lib/shake.js\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+ssh://git@github.com/indutny/common-shake.git\"\n  },\n  \"scripts\": {\n    \"lint\": \"eslint lib/*.js lib/**/*.js test/*.js\",\n    \"test\": \"nyc --reporter=html mocha --reporter=spec test/*-test.js && npm run lint\"\n  },\n  \"files\": [\n    \"lib\"\n  ],\n  \"keywords\": [\n    \"commonjs\",\n    \"tree\",\n    \"shake\"\n  ],\n  \"author\": \"Fedor Indutny <fedor@indutny.com> (http://darksi.de/)\",\n  \"license\": \"MIT\",\n  \"dependencies\": {\n    \"acorn\": \"^5.1.1\",\n    \"debug\": \"^2.6.8\",\n    \"escope\": \"^3.6.0\"\n  },\n  \"devDependencies\": {\n    \"acorn-dynamic-import\": \"^2.0.2\",\n    \"assert-text\": \"^1.1.2\",\n    \"eslint\": \"^4.1.1\",\n    \"mocha\": \"^3.4.2\",\n    \"nyc\": \"^11.0.3\"\n  }\n}\n"
  },
  {
    "path": "test/analyzer-test.js",
    "content": "'use strict';\n/* globals describe it beforeEach afterEach */\n\nconst assert = require('assert');\nconst fixtures = require('./fixtures');\nconst parse = fixtures.parse;\n\nconst shake = require('../');\nconst Analyzer = shake.Analyzer;\n\nconst EMPTY = {\n  bailouts: false,\n  uses: [],\n  declarations: []\n};\n\nfunction simplifyDecl(decl) {\n  return {\n    type: decl.type,\n    name: decl.name,\n    ast: decl.ast.type\n  };\n}\n\ndescribe('Analyzer', () => {\n  let analyzer;\n\n  beforeEach(() => {\n    analyzer = new Analyzer();\n  });\n\n  afterEach(() => {\n    analyzer = null;\n  });\n\n  it('should find all exported values', () => {\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n\n      !function() {\n        module.exports.c = 3;\n      }();\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), {\n      bailouts: false,\n      uses: [],\n      declarations: [ 'a', 'b', 'c' ]\n    });\n\n    const decls = analyzer.getModule('root').getDeclarations();\n\n    assert.deepEqual(decls.map(simplifyDecl), [\n      { type: 'exports', name: 'a', ast: 'AssignmentExpression' },\n      { type: 'exports', name: 'b', ast: 'AssignmentExpression' },\n      { type: 'exports', name: 'c', ast: 'AssignmentExpression' }\n    ]);\n  });\n\n  it('should find all imported values', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n\n      lib.a();\n      lib.b();\n      require('./a').c();\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n      exports.c = 3;\n      exports.d = 4;\n    `), 'a');\n\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n    assert.deepEqual(analyzer.getModule('a').getInfo(), {\n      bailouts: false,\n      uses: [ 'a', 'b', 'c' ],\n      declarations: [ 'a', 'b', 'c', 'd' ]\n    });\n  });\n\n  it('should find all self-used values', () => {\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = () => {};\n\n      exports.c = () => {\n        return exports.b();\n      };\n\n      exports.d = () => {\n        return module.exports.c();\n      };\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), {\n      bailouts: false,\n      uses: [ 'b', 'c' ],\n      declarations: [ 'a', 'b', 'c', 'd' ]\n    });\n  });\n\n  it('should not count disguised `exports` use as export', () => {\n    analyzer.run(parse(`\n      function a() {\n        var exports = {};\n        exports.a = a;\n      }\n\n      exports.b = 1;\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), {\n      bailouts: false,\n      uses: [],\n      declarations: [ 'b' ]\n    });\n  });\n\n  it('should support object destructuring', () => {\n    analyzer.run(parse(`\n      const { a, b } = require('./a');\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n      exports.c = 3;\n    `), 'a');\n\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n    assert.deepEqual(analyzer.getModule('a').getInfo(), {\n      bailouts: false,\n      uses: [ 'a', 'b' ],\n      declarations: [ 'a', 'b', 'c' ]\n    });\n  });\n\n  it('should not support dynamic object destructuring', () => {\n    analyzer.run(parse(`\n      const prop = 'a';\n      const { [prop]: name } = require('./a');\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n      exports.c = 3;\n    `), 'a');\n\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 12, line: 3 },\n          end: { column: 28, line: 3 }\n        },\n        source: 'root',\n        reason: 'Dynamic properties in `require` destructuring',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should not support array destructuring', () => {\n    analyzer.run(parse(`\n      const [ a, b ] = require('./a');\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n      exports.c = 3;\n    `), 'a');\n\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 12, line: 2 },\n          end: { column: 37, line: 2 }\n        },\n        source: 'root',\n        reason: '`require` used in unknown way',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should not count disguised `require` use as import', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n\n      lib.a();\n      function a() {\n        const require = () => {};\n        const lib = require('./a');\n        lib.b();\n      }\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n    `), 'a');\n\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n    assert.deepEqual(analyzer.getModule('a').getInfo(), {\n      bailouts: false,\n      uses: [ 'a' ],\n      declarations: [ 'a', 'b' ]\n    });\n  });\n\n  it('should not count redefined variable as import', () => {\n    analyzer.run(parse(`\n      var lib = require('./a');\n\n      lib.a();\n\n      var lib = require('./b');\n      lib.b();\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n    `), 'a');\n\n    analyzer.resolve('root', './a', 'a');\n    analyzer.resolve('root', './b', 'b');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n    assert.deepEqual(analyzer.getModule('a').getInfo(), {\n      bailouts: [\n        {\n          loc: {\n            start: { column: 10, line: 2 },\n            end: { column: 30, line: 2 }\n          },\n          source: 'root',\n          reason: '`require` variable override',\n          level: 'warning'\n        }\n      ],\n      uses: [],\n      declarations: [ 'a', 'b' ]\n    });\n    assert.deepEqual(analyzer.getModule('b').getInfo(), {\n      bailouts: [\n        {\n          loc: {\n            start: { column: 10, line: 6 },\n            end: { column: 30, line: 6 }\n          },\n          source: 'root',\n          reason: '`require` variable override',\n          level: 'warning'\n        }\n      ],\n      uses: [],\n      declarations: []\n    });\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on assignment to `exports`', () => {\n    analyzer.run(parse(`\n      exports = {};\n      exports.a = 1;\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 2 },\n          end: { column: 18, line: 2 }\n        },\n        source: null,\n        reason: '`exports` assignment',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on assignment to `require`', () => {\n    analyzer.run(parse(`\n      require = () => {};\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 2 },\n          end: { column: 24, line: 2 }\n        },\n        source: null,\n        reason: '`require` assignment',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on dynamic `require`', () => {\n    analyzer.run(parse(`\n      const lib = require(Math.random());\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 18, line: 2 },\n          end: { column: 40, line: 2 }\n        },\n        source: null,\n        reason: 'Dynamic argument of `require`',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, [\n      {\n        loc: {\n          start: { column: 18, line: 2 },\n          end: { column: 40, line: 2 }\n        },\n        source: 'root',\n        reason: 'Dynamic argument of `require`'\n      }\n    ]);\n  });\n\n  it('should not bailout use of `require` properties', () => {\n    analyzer.run(parse(`\n      require.cache[a] = 1;\n    `), 'root');\n\n    assert(analyzer.isSuccess());\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n  });\n\n  it('should bailout on invalide use of `require`', () => {\n    analyzer.run(parse(`\n      escape(require);\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 13, line: 2 },\n          end: { column: 20, line: 2 }\n        },\n        source: null,\n        reason: 'Invalid use of `require`',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, [\n      {\n        loc: {\n          start: { column: 13, line: 2 },\n          end: { column: 20, line: 2 }\n        },\n        source: 'root',\n        reason: 'Invalid use of `require`'\n      }\n    ]);\n  });\n\n  it('should not bailout on `typeof require`', () => {\n    analyzer.run(parse(`\n      if (typeof require === 'function') {\n        console.log(\"ok\");\n      }\n    `), 'root');\n\n    assert.strictEqual(analyzer.getModule('root').getInfo().bailouts, false);\n  });\n\n  it('should bailout on assignment to `module.exports`', () => {\n    analyzer.run(parse(`\n      module.exports = () => {};\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 2 },\n          end: { column: 31, line: 2 }\n        },\n        source: null,\n        reason: '`module.exports` assignment',\n        level: 'info'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should not bailout on assignment to other `module` properties', () => {\n    analyzer.run(parse(`\n      module.lamports = () => {};\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, false);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should support object literal in `module.exports`', () => {\n    analyzer.run(parse(`\n      module.exports = {\n        a: 1,\n        \"b\": 2\n      };\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), {\n      bailouts: false,\n      uses: [],\n      declarations: [ 'a', 'b' ]\n    });\n\n    const decls = analyzer.getModule('root').getDeclarations();\n    assert.deepEqual(decls.map(simplifyDecl), [\n      { type: 'module.exports', name: 'a', ast: 'Property' },\n      { type: 'module.exports', name: 'b', ast: 'Property' }\n    ]);\n  });\n\n  it('should bailout on dynamic keys in `module.exports`', () => {\n    analyzer.run(parse(`\n      module.exports = {\n        [a]: 1,\n        \"b\": 2\n      };\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 8, line: 3 },\n          end: { column: 14, line: 3 }\n        },\n        source: null,\n        reason: 'Dynamic `module.exports` property',\n        level: 'warning'\n      }\n    ]);\n  });\n\n  it('should not support simultaneous `module.exports` and `exports`', () => {\n    analyzer.run(parse(`\n      exports.c = 1;\n      module.exports = {\n        a: 2,\n        b: 3\n      };\n    `), 'root');\n\n    analyzer.run(parse(`\n      module.exports = {\n        a: 2,\n        b: 3\n      };\n      exports.c = 1;\n    `), 'rev-root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), {\n      bailouts: [ {\n        loc: {\n          start: { column: 6, line: 3 },\n          end: { column: 7, line: 6 }\n        },\n        source: null,\n        reason: 'Simultaneous assignment to both `exports` and ' +\n                '`module.exports`',\n        level: 'warning'\n      } ],\n      uses: [],\n      declarations: [ 'c', 'a', 'b' ]\n    });\n\n    assert.deepEqual(analyzer.getModule('rev-root').getInfo(), {\n      bailouts: [ {\n        loc: {\n          start: { column: 6, line: 6 },\n          end: { column: 19, line: 6 }\n        },\n        source: null,\n        reason: 'Simultaneous assignment to both `exports` and ' +\n                '`module.exports`',\n        level: 'warning'\n      } ],\n      uses: [],\n      declarations: [ 'a', 'b', 'c' ]\n    });\n  });\n\n  it('should bailout on dynamic export', () => {\n    analyzer.run(parse(`\n      exports[Math.random()] = 1;\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 2 },\n          end: { column: 28, line: 2 }\n        },\n        source: null,\n        reason: 'Dynamic CommonJS export',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on dynamic `module` use', () => {\n    analyzer.run(parse(`\n      module[Math.random()] = 1;\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 2 },\n          end: { column: 27, line: 2 }\n        },\n        source: null,\n        reason: 'Dynamic `module` use',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on dynamic self-use', () => {\n    analyzer.run(parse(`\n      exports[Math.random()]();\n      module.exports[Math.random()]();\n    `), 'root');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 2 },\n          end: { column: 28, line: 2 }\n        },\n        source: null,\n        reason: 'Dynamic CommonJS use',\n        level: 'warning'\n      },\n      {\n        loc: {\n          start: { column: 6, line: 3 },\n          end: { column: 35, line: 3 }\n        },\n        source: null,\n        reason: 'Dynamic CommonJS use',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on dynamic import', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n\n      lib[Math.random()]();\n    `), 'root');\n\n    analyzer.run(parse(''), 'a');\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 4 },\n          end: { column: 24, line: 4 }\n        },\n        source: 'root',\n        reason: 'Dynamic CommonJS import',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on assignment to imported library', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n\n      lib.override = true;\n    `), 'root');\n\n    analyzer.run(parse(''), 'a');\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 4 },\n          end: { column: 25, line: 4 }\n        },\n        source: 'root',\n        reason: 'Module property assignment',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on escaping imported library', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n\n      send(lib);\n    `), 'root');\n\n    analyzer.run(parse(''), 'a');\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 11, line: 4 },\n          end: { column: 14, line: 4 }\n        },\n        source: 'root',\n        reason: 'Escaping value or unknown use',\n        level: 'warning'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on imported library call', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n\n      lib();\n    `), 'root');\n\n    analyzer.run(parse(''), 'a');\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 4 },\n          end: { column: 11, line: 4 }\n        },\n        source: 'root',\n        reason: 'Imported library call',\n        level: 'info'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on imported library new call', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n\n      new lib();\n    `), 'root');\n\n    analyzer.run(parse(''), 'a');\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 6, line: 4 },\n          end: { column: 15, line: 4 }\n        },\n        source: 'root',\n        reason: 'Imported library new call',\n        level: 'info'\n      }\n    ]);\n    assert.deepEqual(analyzer.bailouts, false);\n  });\n\n  it('should bailout on deferred require', () => {\n    analyzer.run(parse(`\n      var lib;\n      lib = require('./a');\n\n      lib.a();\n      lib.b();\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = 1;\n      exports.b = 2;\n      exports.c = 3;\n    `), 'a');\n\n    analyzer.resolve('root', './a', 'a');\n\n    assert.deepEqual(analyzer.getModule('root').getInfo(), EMPTY);\n    assert.deepEqual(analyzer.getModule('a').getInfo().bailouts, [\n      {\n        loc: {\n          start: { column: 12, line: 3 },\n          end: { column: 26, line: 3 }\n        },\n        source: 'root',\n        reason: 'Escaping `require` call',\n        level: 'warning'\n      }\n    ]);\n  });\n\n  it('should not bailout on const require argument', () => {\n    analyzer.run(parse(`\n      const lib = require('./a' + 'b');\n\n      lib.a();\n    `), 'root');\n\n    analyzer.run(parse('exports.a = 1;'), 'ab');\n    analyzer.resolve('root', './ab', 'ab');\n\n    assert.deepEqual(analyzer.getModule('ab').getInfo(), {\n      bailouts: false,\n      declarations: [ 'a' ],\n      uses: [ 'a' ]\n    });\n    assert(analyzer.isSuccess());\n  });\n\n  it('should not fail on dynamic import', () => {\n    assert.doesNotThrow(() => {\n      analyzer.run(parse('import(\"ohai\")'), 'root');\n    });\n  });\n\n  it('should not throw on double-resolve', () => {\n    assert.doesNotThrow(() => {\n      analyzer.resolve('root', './a', 'a');\n      analyzer.resolve('root', './a', 'a');\n      analyzer.resolve('root', './a', 'a');\n    });\n  });\n\n  it('should find recursive dependencies', () => {\n    analyzer.run(parse(`\n      const lib = require('./a');\n      const mlib = require('./ma');\n\n      exports.a = lib.a;\n      exports.b = mlib.a;\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.a = require('./b').a;\n      exports.c = require('./b').b;\n      exports.b = exports.c;\n    `), 'a');\n\n    analyzer.run(parse(`\n      module.exports = {\n        a: require('./mb').a,\n        b: require('./mb').b\n      };\n    `), 'ma');\n\n    analyzer.getModule('root').forceExport();\n\n    analyzer.resolve('root', './a', 'a');\n    analyzer.resolve('root', './ma', 'ma');\n    analyzer.resolve('a', './b', 'b');\n    analyzer.resolve('ma', './mb', 'mb');\n\n    assert.deepEqual(analyzer.getModule('a').getInfo(), {\n      bailouts: false,\n      uses: [ 'a' ],\n      declarations: [ 'a', 'c', 'b' ]\n    });\n\n    assert.deepEqual(analyzer.getModule('b').getInfo(), {\n      bailouts: false,\n      uses: [ 'a' ],\n      declarations: []\n    });\n\n    assert.deepEqual(analyzer.getModule('ma').getInfo(), {\n      bailouts: false,\n      uses: [ 'a' ],\n      declarations: [ 'a', 'b' ]\n    });\n\n    assert.deepEqual(analyzer.getModule('mb').getInfo(), {\n      bailouts: false,\n      uses: [ 'a' ],\n      declarations: []\n    });\n  });\n\n  it('should not choke on async/await', () => {\n    assert.doesNotThrow(() => {\n      analyzer.run(parse(`\n        'use strict';\n\n        const fn = async function() {\n          await other();\n        };\n      `), 'root');\n    });\n  });\n});\n"
  },
  {
    "path": "test/eval-test.js",
    "content": "'use strict';\n/* globals describe it */\n\nconst assert = require('assert');\nconst fixtures = require('./fixtures');\n\nconst shake = require('../');\nconst evaluateConst = shake.evaluateConst;\n\nconst parse = (source) => {\n  return fixtures.parse(source).body[0].expression;\n};\n\ndescribe('Evaluator', () => {\n  it('should evaluate number literal', () => {\n    assert.strictEqual(evaluateConst(parse('1')), 1);\n  });\n\n  it('should evaluate string literal', () => {\n    assert.strictEqual(evaluateConst(parse('\"1\"')), '1');\n  });\n\n  it('should evaluate binary addition', () => {\n    assert.strictEqual(evaluateConst(parse('\"1\" + \"2\"')), '12');\n  });\n\n  it('should throw on unknown binary operation', () => {\n    assert.throws(() => {\n      evaluateConst(parse('\"1\" / \"2\"'));\n    });\n  });\n\n  it('should throw on unknown node type', () => {\n    assert.throws(() => {\n      evaluateConst(parse('a()'));\n    });\n  });\n});\n"
  },
  {
    "path": "test/fixtures.js",
    "content": "'use strict';\n\nconst acorn = require('acorn-dynamic-import').default;\n\nexports.parse = (source) => {\n  return acorn.parse(source, {\n    locations: true,\n    sourceType: 'module',\n    ecmaVersion: 2017,\n    plugins: {\n      dynamicImport: true\n    }\n  });\n};\n"
  },
  {
    "path": "test/graph-test.js",
    "content": "'use strict';\n/* globals describe it beforeEach afterEach */\n\nconst assertText = require('assert-text');\nconst fixtures = require('./fixtures');\nconst parse = fixtures.parse;\n\nassertText.options.trim = true;\n\nconst shake = require('../');\nconst Analyzer = shake.Analyzer;\nconst Graph = shake.Graph;\n\ndescribe('Graph', () => {\n  let analyzer;\n  let graph;\n\n  beforeEach(() => {\n    analyzer = new Analyzer();\n    graph = new Graph(__dirname);\n  });\n\n  afterEach(() => {\n    analyzer = null;\n    graph = null;\n  });\n\n  it('should find all exported values', () => {\n    analyzer.run(parse(`\n      // Import all\n      require('./a')[K];\n\n      require('./b').bprop;\n\n      exports.prop = 1;\n    `), 'root');\n\n    analyzer.run(parse(`\n      exports.aprop = 1;\n    `), 'a');\n\n    analyzer.run(parse(`\n      exports.bprop = 1;\n    `), 'b');\n\n    analyzer.resolve('root', './a', 'a');\n    analyzer.resolve('root', './b', 'b');\n\n    const out = graph.generate(analyzer.getModules());\n    assertText.equal(out, `digraph {\n      ranksep=1.2;\n      subgraph \"cluster://../root\" {\n        label=\"../root\";\n        color=black;\n        \"{../root}/require\" [label=require shape=diamond];\n        \"{../root}[prop]\" [label=\"prop\" color=blue];\n      }\n      subgraph \"cluster://../a\" {\n        label=\"../a\";\n        color=red;\n        \"{../a}/require\" [label=require shape=diamond];\n        \"{../a}[aprop]\" [label=\"aprop\" color=red];\n        \"{../a}[[*]]\" [label=\"[*]\" color=red];\n      }\n      \"{../root}/require\" -> \"{../a}[[*]]\";\n      subgraph \"cluster://../b\" {\n        label=\"../b\";\n        color=black;\n        \"{../b}/require\" [label=require shape=diamond];\n        \"{../b}[bprop]\" [label=\"bprop\" color=black];\n      }\n      \"{../root}/require\" -> \"{../b}[bprop]\";\n    }`);\n  });\n});\n"
  }
]