| var semver = require("semver") | 
| var validateLicense = require('validate-npm-package-license'); | 
| var hostedGitInfo = require("hosted-git-info") | 
| var isBuiltinModule = require("resolve").isCore | 
| var 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] | 
|     } | 
|   }) | 
| } |