zanata/fileMapping.js

'use strict';

/**
 * The file mapping definition
 * @exports zanata/fileMapping
 *
 * @requires util
 * @requires minimatch
 * @requires glob
 */
const util = require('util');
const minimatch = require('minimatch');
const glob = require('glob');

/**
 * @class
 * @classdesc The class to handle the mapping from the project type to the file name rule.
 */
class FileMappingRule {

  /**
   *
   *
   * @param {object[]|object} args - the Translation files mapping rules object represented by `xml2json` rules tags in zanata.xml
   * @param {object} args.rule
   * @param {string} args.rule.$t
   * @param {string} args.rule.pattern
   *
   * @see {@link http://docs.zanata.org/en/release/client/configuration/#translation-files-mapping-rules|Translation files mapping rules}
   */
  constructor(args) {
    this.defaultMapping = {
      'gettext': '{path}/{locale_with_underscore}.{extension}',
      'podir': '{locale}/{path}/{filename}.{extension}',
      'properties': '{path}/{filename}_{locale_with_underscore}.{extension}',
      'utf8properties': '{path}/{filename}_{locale_with_underscore}.{extension}',
      'xliff': '{path}/{filename}_{locale_with_underscore}.{extension}',
      'xml': '{path}/{filename}_{locale_with_underscore}.{extension}',
      'file': '{locale}/{path}/{filename}.{extension}'
    };
    this.customMapping = (args instanceof Array) ? args : [args];
    this.customMapping = this.customMapping.filter((v) => v != undefined);
  }

  __applyMapping(tmpl, pattern) {
    Object.keys(pattern).forEach(function(k) {
      let restr = util.format('{%s}', k);
      let re = new RegExp(restr, 'g');
      tmpl = tmpl.replace(re, pattern[k]);
    });

    return tmpl;
  }

  __getMapping(type, pattern) {
    if (this.defaultMapping[type] == undefined) {
      throw new Error('Unknown mapping type: ' + type);
    }
    return this.__applyMapping(this.defaultMapping[type], pattern);
  }

  /**
   * Set the mapping rule
   *
   * @param {string} type - the project type
   * @param {string} pattern - the file name pattern
   */
  setMapping(type, pattern) {
    this.defaultMapping[type] = pattern;
  }

  /**
   * Obtain the file path
   *
   * @param {string} type - the project type
   * @param {object} params
   * @param {string} params.source - the source document filename
   * @param {string} [params.locale] - the locale name
   * @param {string} [params.path]
   * @param {string} [params.filename]
   * @param {string} [params.extension]
   *
   * @throws {Error} Unknown mapping type
   */
  getPath(type, params) {
    let self = this;
    let src = !params || !params.source ? null : params.source;
    if (params && params.locale)
      params.locale_with_underscore = params.locale.replace(/-/g, '_');
    let ret = self.__getMapping(type, params);
    if (self.customMapping.length > 0 && !src) {
      console.warn("No source document filename is given in params. ignoring customMapping");
    } else {
      self.customMapping.forEach(function(o) {
        let r = o.rule;
        if (!Array.isArray(r))
          r = [r];
        for (let i = 0; i < r.length; i++) {
          if (!r[i].pattern || minimatch(params.source, r[i].pattern)) {
            ret = self.__applyMapping(r[i]['$t'], params);
            break;
          }
        }
      });
    }

    return ret;
  }

  /**
   * Obtain the absolute file path
   *
   * @param {string} type - the project type
   * @param {object} params
   * @param {string} params.source - the source document filename
   * @param {string} [params.locale] - the locale name
   * @param {string} [params.path]
   * @param {string} [params.filename]
   * @param {string} [params.extension]
   *
   * @throws {Error} Unknown mapping type
   */
  getRealPath(type, params) {
    let p = this.getPath(type, params).replace(/^\.\//, '');
    return glob.sync(p, {nocase: true, nodir: true})[0];
  }

}

exports.FileMappingRule = FileMappingRule;