| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418 | var semver = require("semver")var validateLicense = require('validate-npm-package-license');var hostedGitInfo = require("hosted-git-info")var isBuiltinModule = require("resolve").isCorevar depTypes = ["dependencies","devDependencies","optionalDependencies"]var extractDescription = require("./extract_description")var url = require("url")var typos = require("./typos.json")var fixer = module.exports = {  // default warning function  warn: function() {},  fixRepositoryField: function(data) {    if (data.repositories) {      this.warn("repositories");      data.repository = data.repositories[0]    }    if (!data.repository) return this.warn("missingRepository")    if (typeof data.repository === "string") {      data.repository = {        type: "git",        url: data.repository      }    }    var r = data.repository.url || ""    if (r) {      var hosted = hostedGitInfo.fromUrl(r)      if (hosted) {        r = data.repository.url          = hosted.getDefaultRepresentation() == "shortcut" ? hosted.https() : hosted.toString()      }    }    if (r.match(/github.com\/[^\/]+\/[^\/]+\.git\.git$/)) {      this.warn("brokenGitUrl", r)    }  }, fixTypos: function(data) {    Object.keys(typos.topLevel).forEach(function (d) {      if (data.hasOwnProperty(d)) {        this.warn("typo", d, typos.topLevel[d])      }    }, this)  }, fixScriptsField: function(data) {    if (!data.scripts) return    if (typeof data.scripts !== "object") {      this.warn("nonObjectScripts")      delete data.scripts      return    }    Object.keys(data.scripts).forEach(function (k) {      if (typeof data.scripts[k] !== "string") {        this.warn("nonStringScript")        delete data.scripts[k]      } else if (typos.script[k] && !data.scripts[typos.script[k]]) {        this.warn("typo", k, typos.script[k], "scripts")      }    }, this)  }, fixFilesField: function(data) {    var files = data.files    if (files && !Array.isArray(files)) {      this.warn("nonArrayFiles")      delete data.files    } else if (data.files) {      data.files = data.files.filter(function(file) {        if (!file || typeof file !== "string") {          this.warn("invalidFilename", file)          return false        } else {          return true        }      }, this)    }  }, fixBinField: function(data) {    if (!data.bin) return;    if (typeof data.bin === "string") {      var b = {}      var match      if (match = data.name.match(/^@[^/]+[/](.*)$/)) {        b[match[1]] = data.bin      } else {        b[data.name] = data.bin      }      data.bin = b    }  }, fixManField: function(data) {    if (!data.man) return;    if (typeof data.man === "string") {      data.man = [ data.man ]    }  }, fixBundleDependenciesField: function(data) {    var bdd = "bundledDependencies"    var bd = "bundleDependencies"    if (data[bdd] && !data[bd]) {      data[bd] = data[bdd]      delete data[bdd]    }    if (data[bd] && !Array.isArray(data[bd])) {      this.warn("nonArrayBundleDependencies")      delete data[bd]    } else if (data[bd]) {      data[bd] = data[bd].filter(function(bd) {        if (!bd || typeof bd !== 'string') {          this.warn("nonStringBundleDependency", bd)          return false        } else {          if (!data.dependencies) {            data.dependencies = {}          }          if (!data.dependencies.hasOwnProperty(bd)) {            this.warn("nonDependencyBundleDependency", bd)            data.dependencies[bd] = "*"          }          return true        }      }, this)    }  }, fixDependencies: function(data, strict) {    var loose = !strict    objectifyDeps(data, this.warn)    addOptionalDepsToDeps(data, this.warn)    this.fixBundleDependenciesField(data)    ;['dependencies','devDependencies'].forEach(function(deps) {      if (!(deps in data)) return      if (!data[deps] || typeof data[deps] !== "object") {        this.warn("nonObjectDependencies", deps)        delete data[deps]        return      }      Object.keys(data[deps]).forEach(function (d) {        var r = data[deps][d]        if (typeof r !== 'string') {          this.warn("nonStringDependency", d, JSON.stringify(r))          delete data[deps][d]        }        var hosted = hostedGitInfo.fromUrl(data[deps][d])        if (hosted) data[deps][d] = hosted.toString()      }, this)    }, this)  }, fixModulesField: function (data) {    if (data.modules) {      this.warn("deprecatedModules")      delete data.modules    }  }, fixKeywordsField: function (data) {    if (typeof data.keywords === "string") {      data.keywords = data.keywords.split(/,\s+/)    }    if (data.keywords && !Array.isArray(data.keywords)) {      delete data.keywords      this.warn("nonArrayKeywords")    } else if (data.keywords) {      data.keywords = data.keywords.filter(function(kw) {        if (typeof kw !== "string" || !kw) {          this.warn("nonStringKeyword");          return false        } else {          return true        }      }, this)    }  }, fixVersionField: function(data, strict) {    // allow "loose" semver 1.0 versions in non-strict mode    // enforce strict semver 2.0 compliance in strict mode    var loose = !strict    if (!data.version) {      data.version = ""      return true    }    if (!semver.valid(data.version, loose)) {      throw new Error('Invalid version: "'+ data.version + '"')    }    data.version = semver.clean(data.version, loose)    return true  }, fixPeople: function(data) {    modifyPeople(data, unParsePerson)    modifyPeople(data, parsePerson)  }, fixNameField: function(data, options) {    if (typeof options === "boolean") options = {strict: options}    else if (typeof options === "undefined") options = {}    var strict = options.strict    if (!data.name && !strict) {      data.name = ""      return    }    if (typeof data.name !== "string") {      throw new Error("name field must be a string.")    }    if (!strict)      data.name = data.name.trim()    ensureValidName(data.name, strict, options.allowLegacyCase)    if (isBuiltinModule(data.name))      this.warn("conflictingName", data.name)  }, fixDescriptionField: function (data) {    if (data.description && typeof data.description !== 'string') {      this.warn("nonStringDescription")      delete data.description    }    if (data.readme && !data.description)      data.description = extractDescription(data.readme)      if(data.description === undefined) delete data.description;    if (!data.description) this.warn("missingDescription")  }, fixReadmeField: function (data) {    if (!data.readme) {      this.warn("missingReadme")      data.readme = "ERROR: No README data found!"    }  }, fixBugsField: function(data) {    if (!data.bugs && data.repository && data.repository.url) {      var hosted = hostedGitInfo.fromUrl(data.repository.url)      if(hosted && hosted.bugs()) {        data.bugs = {url: hosted.bugs()}      }    }    else if(data.bugs) {      var emailRe = /^.+@.*\..+$/      if(typeof data.bugs == "string") {        if(emailRe.test(data.bugs))          data.bugs = {email:data.bugs}        else if(url.parse(data.bugs).protocol)          data.bugs = {url: data.bugs}        else          this.warn("nonEmailUrlBugsString")      }      else {        bugsTypos(data.bugs, this.warn)        var oldBugs = data.bugs        data.bugs = {}        if(oldBugs.url) {          if(typeof(oldBugs.url) == "string" && url.parse(oldBugs.url).protocol)            data.bugs.url = oldBugs.url          else            this.warn("nonUrlBugsUrlField")        }        if(oldBugs.email) {          if(typeof(oldBugs.email) == "string" && emailRe.test(oldBugs.email))            data.bugs.email = oldBugs.email          else            this.warn("nonEmailBugsEmailField")        }      }      if(!data.bugs.email && !data.bugs.url) {        delete data.bugs        this.warn("emptyNormalizedBugs")      }    }  }, fixHomepageField: function(data) {    if (!data.homepage && data.repository && data.repository.url) {      var hosted = hostedGitInfo.fromUrl(data.repository.url)      if (hosted && hosted.docs()) data.homepage = hosted.docs()    }    if (!data.homepage) return    if(typeof data.homepage !== "string") {      this.warn("nonUrlHomepage")      return delete data.homepage    }    if(!url.parse(data.homepage).protocol) {      data.homepage = "http://" + data.homepage    }  }, fixLicenseField: function(data) {    if (!data.license) {      return this.warn("missingLicense")    } else{      if (        typeof(data.license) !== 'string' ||        data.license.length < 1 ||        data.license.trim() === ''      ) {        this.warn("invalidLicense")      } else {        if (!validateLicense(data.license).validForNewPackages)          this.warn("invalidLicense")      }    }  }}function isValidScopedPackageName(spec) {  if (spec.charAt(0) !== '@') return false  var rest = spec.slice(1).split('/')  if (rest.length !== 2) return false  return rest[0] && rest[1] &&    rest[0] === encodeURIComponent(rest[0]) &&    rest[1] === encodeURIComponent(rest[1])}function isCorrectlyEncodedName(spec) {  return !spec.match(/[\/@\s\+%:]/) &&    spec === encodeURIComponent(spec)}function ensureValidName (name, strict, allowLegacyCase) {  if (name.charAt(0) === "." ||      !(isValidScopedPackageName(name) || isCorrectlyEncodedName(name)) ||      (strict && (!allowLegacyCase) && name !== name.toLowerCase()) ||      name.toLowerCase() === "node_modules" ||      name.toLowerCase() === "favicon.ico") {        throw new Error("Invalid name: " + JSON.stringify(name))  }}function modifyPeople (data, fn) {  if (data.author) data.author = fn(data.author)  ;["maintainers", "contributors"].forEach(function (set) {    if (!Array.isArray(data[set])) return;    data[set] = data[set].map(fn)  })  return data}function unParsePerson (person) {  if (typeof person === "string") return person  var name = person.name || ""  var u = person.url || person.web  var url = u ? (" ("+u+")") : ""  var e = person.email || person.mail  var email = e ? (" <"+e+">") : ""  return name+email+url}function parsePerson (person) {  if (typeof person !== "string") return person  var name = person.match(/^([^\(<]+)/)  var url = person.match(/\(([^\)]+)\)/)  var email = person.match(/<([^>]+)>/)  var obj = {}  if (name && name[0].trim()) obj.name = name[0].trim()  if (email) obj.email = email[1];  if (url) obj.url = url[1];  return obj}function addOptionalDepsToDeps (data, warn) {  var o = data.optionalDependencies  if (!o) return;  var d = data.dependencies || {}  Object.keys(o).forEach(function (k) {    d[k] = o[k]  })  data.dependencies = d}function depObjectify (deps, type, warn) {  if (!deps) return {}  if (typeof deps === "string") {    deps = deps.trim().split(/[\n\r\s\t ,]+/)  }  if (!Array.isArray(deps)) return deps  warn("deprecatedArrayDependencies", type)  var o = {}  deps.filter(function (d) {    return typeof d === "string"  }).forEach(function(d) {    d = d.trim().split(/(:?[@\s><=])/)    var dn = d.shift()    var dv = d.join("")    dv = dv.trim()    dv = dv.replace(/^@/, "")    o[dn] = dv  })  return o}function objectifyDeps (data, warn) {  depTypes.forEach(function (type) {    if (!data[type]) return;    data[type] = depObjectify(data[type], type, warn)  })}function bugsTypos(bugs, warn) {  if (!bugs) return  Object.keys(bugs).forEach(function (k) {    if (typos.bugs[k]) {      warn("typo", k, typos.bugs[k], "bugs")      bugs[typos.bugs[k]] = bugs[k]      delete bugs[k]    }  })}
 |