123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- var isValidSemver = require('semver/functions/valid')
- var cleanSemver = require('semver/functions/clean')
- var validateLicense = require('validate-npm-package-license')
- var hostedGitInfo = require('hosted-git-info')
- var isBuiltinModule = require('is-core-module')
- var depTypes = ['dependencies', 'devDependencies', 'optionalDependencies']
- var extractDescription = require('./extract_description')
- var url = require('url')
- var typos = require('./typos.json')
- 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 (Object.prototype.hasOwnProperty.call(data, 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 (Object.prototype.hasOwnProperty.call(data.dependencies, bd)) {
- this.warn('nonDependencyBundleDependency', bd)
- data.dependencies[bd] = '*'
- }
- return true
- }
- }, this)
- }
- },
- fixDependencies: function (data, 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 (!isValidSemver(data.version, loose)) {
- throw new Error('Invalid version: "' + data.version + '"')
- }
- data.version = cleanSemver(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}
- /* eslint-disable-next-line node/no-deprecated-api */
- } 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) {
- /* eslint-disable-next-line node/no-deprecated-api */
- 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
- }
- /* eslint-disable-next-line node/no-deprecated-api */
- if (!url.parse(data.homepage).protocol) {
- data.homepage = 'http://' + data.homepage
- }
- },
- fixLicenseField: function (data) {
- const license = data.license || data.licence
- if (!license) {
- return this.warn('missingLicense')
- }
- if (
- typeof (license) !== 'string' ||
- license.length < 1 ||
- license.trim() === ''
- ) {
- return this.warn('invalidLicense')
- }
- if (!validateLicense(license).validForNewPackages) {
- return 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]
- }
- })
- }
|