function generateExterns(names, inputPath) {
  // Load and parse the code, with comments attached to the nodes.
  const code = fs.readFileSync(inputPath, 'utf-8');
  const program = esprima.parse(code, {attachComment: true});
  assert.equal(program.type, 'Program');

  const body = program.body;
  const provides = program.body.filter(isProvideNode)
      .map((node) => getArgumentFromCallNode(0, node));
  const requires = program.body.filter(isRequireNode)
      .map((node) => getArgumentFromCallNode(0, node));

  // Get all exported nodes and all classes, in order.
  const rawExterns = program.body.map((node) => {
    if (isExportNode(node)) {
      // Explicitly-exported nodes are handled here.
      return createExternFromExportNode(names, node);
    } else if (isPartiallyExportedClassAssignmentNode(node)) {
      // Some classes are not exported, but contain exported members.  These
      // need to have externs generated, too.

      // But wait!  The latest compiler won't actually export those correctly!
      // TODO: File a bug against the Closure Compiler.
      // In the mean time, log these now and throw an error at the end to make
      // sure we are generating usable releases.  This tends to affect our
      // plugin registration APIs, and apps should definitely be able to use
      // those!
      if (!partiallyExportedClassesDetected) {
        partiallyExportedClassesDetected = true;
        console.log('The Closure Compiler does not handle partially-exported ' +
            'classes correctly!  The following classes need to be exported:');
      }

      const name = getIdentifierString(node.expression.left);
      console.log(' * ' + name);

      return createExternFromPartiallyExportedClassAssignmentNode(names, node);
    } else {
      // Ignore anything else, and don't generate any externs.
      return '';
    }
  });

  const externs = rawExterns.join('');

  return {
    path: inputPath,
    provides: provides,
    requires: requires,
    externs: externs,
  };
}
@param {!Set<string>} names A set of the names of exported nodes.
@param {string} inputPath
@return {{
  path: string,
  provides: !Array<string>,
  requires: !Array<string>,
  externs: string,
}}