dLobatog/npm2rpm

View on GitHub
lib/spec_file_generator.js

Summary

Maintainability
A
3 hrs
Test Coverage
const fs = require('fs');
const Handlebars = require('handlebars');
const semver = require('semver');
const npmUrl = require('../lib/npm_helpers.js').npmUrl;
const getCacheFilename = require('../lib/npm_helpers.js').getCacheFilename;
const getRpmPackageName = require('../lib/npm_helpers.js').getRpmPackageName;
module.exports = generateSpecFile;
module.exports.dependenciesToRequires = dependenciesToRequires;

// SpecFile creation

function sorted(items) {
  const result = []
  for (item in items) {
    result.push(item);
  }
  result.sort();
  return result;
}

function dependenciesToSources(name, deps) {
  const sources = deps.map((dependency, index) => {
    return `Source${index}: ${npmUrl(dependency['name'], dependency['version'])}`;
  });
  sources.push(`Source${deps.length}: ${getCacheFilename(getRpmPackageName(name), '%{version}')}`);
  return sources;
}

function sortDependencies(deps) {
  const result = deps.map((dependency) => {
    return {'name': dependency[0], 'version': dependency[1]}
  });
  result.sort((a, b) => {
    if (a['name'] === b['name']) {
      return a['version'].localeCompare(b['version']);
    } else {
      return a['name'].localeCompare(b['name']);
    }
  });
  return result;
}

function dependenciesToRequires(deps, scl) {
  var dependencies = [];
  var sortedDeps = sortedDeps = sorted(deps);
  var sclPrefix = '';

  if (scl) {
    sclPrefix = '%{scl_prefix}';
  }

  for (var i = 0; i < sortedDeps.length; i++) {
    const dep = sortedDeps[i];
    const version = deps[dep];
    const versionRange = semver.validRange(version);


    // if the version string is not valid, validRange returns null, otherwise
    // it parses a semver string into a disjunctive normal form of version
    // constraints, and joins that result into a single string, using '||' as
    // OR and ' ' as AND.

    var versionDNF = [];
    if (versionRange !== null) {
      versionDNF = versionRange.split('||').map((s) => s.split(' '));
    }

    // If the version range contains more than on OR clause (e.g., 1.2.3 || 3.0.0),
    // it cannot be expressed with simple dependencies, so just output a name with
    // no version. If the version range contains no clauses, validRange returned
    // null and there is no version for us to include.
    if (versionDNF.length !== 1) {
      dependencies.push(sclPrefix + 'npm(' + dep + ')');
    } else {
      // At this point each constraint can be one of three things:
      //  - '*', meaning there is no version constraint
      //  - just a version number, e.g. 1.2.3, meaning the requirement is equal to that
      //  - a constraint (<,>,>=,<=) and a version number, which can be used as-is
      versionDNF[0].forEach((constraint) => {
        var m;
        if (constraint === '*') {
          dependencies.push(sclPrefix + 'npm(' + dep + ')');
        } else if ((m = constraint.match(/^([<>]=?)(.*?)$/)) !== null) {
          dependencies.push(sclPrefix + 'npm(' + dep + ') ' + m[1] + ' ' + m[2]);
        } else {
          dependencies.push(sclPrefix + 'npm(' + dep + ') = ' + constraint);
        }
      });
    }
  }

  return dependencies;
}

function getCopyFiles(files) {
  const ignoreFiles = [
    'Gemfile',
    'Gruntfile.js',
    'Rakefile',
    'bower.json',
    'circle.yml',
    'codecov.yml',
    'composer.json',
    'deploy_key.enc',
    'gulpfile.js',
    'karma.conf.js',
    'npm-shrinkwrap.json',
    'package-lock.json',
    'release.sh',
    'yarn.lock'
  ].concat(getLicenceFiles(files), getDocFiles(files));
  const ignorePattern = /^\.|\.gemspec$/;
  return files.filter(file => ignoreFiles.indexOf(file) === -1 && !ignorePattern.test(file));
}

function getDocFiles(files) {
  const licenseFiles = getLicenceFiles(files);
  const docPattern = /\.txt$|\.md$|authors|readme|contributing|docs/i;
  return files.filter(file => licenseFiles.indexOf(file) === -1 && docPattern.test(file));
}

function getLicenceFiles(files) {
  return files.filter(file => /license|copying/i.test(file));
}

function getSummary(description) {
  return description !== undefined ? description.split('.')[0].replace(/^\s+|\s+$/g, '') : 'FIXME';
}

function getProjectUrl(npm_module) {
  var projecturl = '';

  if (npm_module.homepage) {
    projecturl = npm_module.homepage;
  } else if (npm_module.repository) {
    projecturl = npm_module.repository.url;
  } else {
    projecturl = "https://www.npmjs.com/package/" + npm_module.name;
  }

  return projecturl;
}

function getLicense(npm_module) {
  if (npm_module.license) {
    return npm_module.license;
  } else if (npm_module.licenses) {
    return npm_module.licenses[0].type;
  } else {
    return 'FIXME';
  }
}

function generateSpecFile(npm_module, files, dependencies, release, template_name, scl) {
  for (binary in npm_module.bin) {
    npm_module.bin[binary] = npm_module.bin[binary].replace(/^\.\//, '');
  }

  const replacements = {
    NAME: npm_module.name,
    RPM_PACKAGE_NAME: getRpmPackageName(npm_module.name),
    PACKAGE_NAME_HAS_SLASH: npm_module.name.indexOf('/') >= 0,
    VERSION: npm_module.version,
    RELEASE: release,
    LICENSEFILES: getLicenceFiles(files),
    LICENSETYPE: getLicense(npm_module),
    SUMMARY: getSummary(npm_module.description),
    PROJECTURL: getProjectUrl(npm_module),
    DESCRIPTION: npm_module.description,
    BINARIES: npm_module.bin,
    COPYFILES: getCopyFiles(files),
    DOCFILES: getDocFiles(files)
  }

  if (dependencies.length !== 0) {
    dependencies = sortDependencies(dependencies);
    replacements['DEPENDENCIES'] = [];
    replacements['PROVIDES'] = dependencies;
    replacements['SOURCES'] = dependenciesToSources(npm_module.name, dependencies);
  } else {
    replacements['DEPENDENCIES'] = dependenciesToRequires(npm_module['dependencies'], scl);
    replacements['PROVIDES'] = [];
    replacements['SOURCES'] = ['Source0: ' + npmUrl(npm_module.name, '%{version}')];
  }

  const spec_file = fs.readFileSync(template_name, { encoding: 'utf8' });
  const template = Handlebars.compile(spec_file);
  return template(replacements);
}