var semver = require("semver") var validateLicense = require('validate-npm-package-license'); var hostedGitInfo = require("hosted-git-info") var isBuiltinModule = require("is-builtin-module") var depTypes = ["dependencies","devDependencies","optionalDependencies"] var extractDescription = require("./extract_description") var url = require("url") var typos = require("./typos") 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) { this.warn("missingProtocolHomepage") 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 ) { 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] } }) }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 19553 | swellard | Move and rename clients | ||
//guest/perforce_software/helix-web-services/main/source/clients/2016.1.0/javascript/node_modules/normalize-package-data/lib/fixer.js | |||||
#1 | 18810 | tjuricek |
First-pass at JavaScript client SDK. JavaScript requires Node with Gulp to "browserfy" the library. It's the easiest way I found to use the swagger-js project; bundle up a wrapping method. There is no JavaScript reference guide. The swagger-js doesn't really document what they do very well, actually. Overall I'm not particularly impressed by swagger-js, it was hard to even figure out what the right method syntax was. We may want to invest time in doing it better. This required setting CORS response headers, which are currently defaulted to a fairly insecure setting. |