All Downloads are FREE. Search and download functionalities are using the official Maven repository.

static.js.vendor.openlayers3.tasks.generate-externs.js Maven / Gradle / Ivy

The newest version!
var fs = require('fs');
var path = require('path');

var async = require('async');
var fse = require('fs-extra');
var nomnom = require('nomnom');

var generateInfo = require('./generate-info');

var googRegEx = /^goog\..*$/;

/**
 * Read the symbols from info file.
 * @param {funciton(Error, Array., Array.)} callback Called
 *     with the patterns and symbols (or any error).
 */
function getInfo(callback) {
  generateInfo(function(err) {
    if (err) {
      callback(new Error('Trouble generating info: ' + err.message));
      return;
    }
    var info = require('../build/info.json');
    callback(null, info.typedefs, info.symbols, info.externs, info.base);
  });
}


/**
 * Generate externs code given a list symbols.
 * @param {Array.} typedefs List of typedefs.
 * @param {Array.} symbols List of symbols.
 * @param {Array.} externs List of externs.
 * @param {Array.} base List of base.
 * @param {string|undefined} namespace Target object for exported symbols.
 * @return {string} Export code.
 */
function generateExterns(typedefs, symbols, externs, base) {
  var lines = [];
  var processedSymbols = {};
  var constructors = {};

  function addNamespaces(name) {
    var parts = name.split('.');
    parts.pop();
    var namespace = [];
    parts.forEach(function(part) {
      namespace.push(part);
      var partialNamespace = namespace.join('.');
      if (!(partialNamespace in processedSymbols ||
          partialNamespace in constructors)) {
        lines.push('/**');
        lines.push(' * @type {Object}');
        lines.push(' */');
        lines.push(nameToJS(partialNamespace) + ';');
        lines.push('\n');
      }
    });
  }

  function nameToJS(name) {
    processedSymbols[name] = true;
    if (name.indexOf('.') === -1) {
      name = 'var ' + name;
    }
    return name;
  }

  function noGoogTypes(typesWithGoog) {
    var typesWithoutGoog = [];
    typesWithGoog.forEach(function(type) {
      typesWithoutGoog.push(type.replace(googRegEx, '*'));
    });
    return typesWithoutGoog;
  }

  function processSymbol(symbol) {
    addNamespaces(symbol.name.split('#')[0]);

    var name = symbol.name;
    if (name.indexOf('#') > 0) {
      name = symbol.name.replace('#', '.prototype.');
      var constructor = symbol.name.split('#')[0];
      if (!(constructor in constructors)) {
        constructors[constructor] = true;
        lines.push('/**');
        lines.push(' * @constructor');
        lines.push(' */');
        lines.push(nameToJS(constructor) + ' = function() {};');
        lines.push('\n');
      }
    }

    lines.push('/**');
    if (symbol.kind === 'class') {
      constructors[name] = true;
      lines.push(' * @constructor');
      if (symbol.extends && !googRegEx.test(symbol.extends)) {
        lines.push(' * @extends {' + symbol.extends + '}');
      }
    }
    if (symbol.types) {
      lines.push(' * @type {' + noGoogTypes(symbol.types).join('|') + '}');
    }
    var args = [];
    if (symbol.params) {
      symbol.params.forEach(function(param) {
        args.push(param.name);
        lines.push(' * @param {' +
            (param.variable ? '...' : '') +
            noGoogTypes(param.types).join('|') +
            (param.optional ? '=' : '') + (param.nullable ? '!' : '') +
            '} ' + param.name);
      });
    }
    if (symbol.returns) {
      lines.push(' * @return {' +
          (symbol.returns.nullable ? '!' : '') +
          noGoogTypes(symbol.returns.types).join('|') + '}');
    }
    if (symbol.template) {
      lines.push(' * @template ' + symbol.template);
    }
    lines.push(' */');
    if (symbol.kind === 'function' || symbol.kind === 'class') {
      lines.push(nameToJS(name) + ' = function(' + args.join(', ') + ') {};');
    } else {
      lines.push(nameToJS(name) + ';');
    }
    lines.push('\n');
  }

  externs.forEach(processSymbol);

  base.forEach(processSymbol);

  symbols.forEach(processSymbol);

  typedefs.forEach(function(typedef) {
    addNamespaces(typedef.name);
    lines.push('/**');
    lines.push(' * @typedef {' + noGoogTypes(typedef.types).join('|') + '}');
    lines.push(' */');
    lines.push(nameToJS(typedef.name) + ';');
    lines.push('\n');
  });

  return lines.join('\n');
}


/**
 * Generate the exports code.
 *
 * @param {function(Error, string)} callback Called with the exports code or any
 *     error generating it.
 */
function main(callback) {
  async.waterfall([
    getInfo,
    function(typedefs, symbols, externs, base, done) {
      var code, err;
      try {
        code = generateExterns(typedefs, symbols, externs, base);
      } catch (e) {
        err = e;
      }
      done(err, code);
    }
  ], callback);
}


/**
 * If running this module directly, read the config file, call the main
 * function, and write the output file.
 */
if (require.main === module) {
  var options = nomnom.options({
    output: {
      position: 0,
      required: true,
      help: 'Output path for the generated externs file.'
    }
  }).parse();

  async.waterfall([
    main,
    fse.outputFile.bind(fse, options.output)
  ], function(err) {
    if (err) {
      process.stderr.write(err.message + '\n');
      process.exit(1);
    } else {
      process.exit(0);
    }
  });
}


/**
 * Export main function.
 */
module.exports = main;