1'use strict' 2 3var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$') 4var builtins = require('builtins') 5var blacklist = [ 6 'node_modules', 7 'favicon.ico', 8] 9 10function validate (name) { 11 var warnings = [] 12 var errors = [] 13 14 if (name === null) { 15 errors.push('name cannot be null') 16 return done(warnings, errors) 17 } 18 19 if (name === undefined) { 20 errors.push('name cannot be undefined') 21 return done(warnings, errors) 22 } 23 24 if (typeof name !== 'string') { 25 errors.push('name must be a string') 26 return done(warnings, errors) 27 } 28 29 if (!name.length) { 30 errors.push('name length must be greater than zero') 31 } 32 33 if (name.match(/^\./)) { 34 errors.push('name cannot start with a period') 35 } 36 37 if (name.match(/^_/)) { 38 errors.push('name cannot start with an underscore') 39 } 40 41 if (name.trim() !== name) { 42 errors.push('name cannot contain leading or trailing spaces') 43 } 44 45 // No funny business 46 blacklist.forEach(function (blacklistedName) { 47 if (name.toLowerCase() === blacklistedName) { 48 errors.push(blacklistedName + ' is a blacklisted name') 49 } 50 }) 51 52 // Generate warnings for stuff that used to be allowed 53 54 // core module names like http, events, util, etc 55 builtins({ version: '*' }).forEach(function (builtin) { 56 if (name.toLowerCase() === builtin) { 57 warnings.push(builtin + ' is a core module name') 58 } 59 }) 60 61 if (name.length > 214) { 62 warnings.push('name can no longer contain more than 214 characters') 63 } 64 65 // mIxeD CaSe nAMEs 66 if (name.toLowerCase() !== name) { 67 warnings.push('name can no longer contain capital letters') 68 } 69 70 if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) { 71 warnings.push('name can no longer contain special characters ("~\'!()*")') 72 } 73 74 if (encodeURIComponent(name) !== name) { 75 // Maybe it's a scoped package name, like @user/package 76 var nameMatch = name.match(scopedPackagePattern) 77 if (nameMatch) { 78 var user = nameMatch[1] 79 var pkg = nameMatch[2] 80 if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) { 81 return done(warnings, errors) 82 } 83 } 84 85 errors.push('name can only contain URL-friendly characters') 86 } 87 88 return done(warnings, errors) 89} 90 91var done = function (warnings, errors) { 92 var result = { 93 validForNewPackages: errors.length === 0 && warnings.length === 0, 94 validForOldPackages: errors.length === 0, 95 warnings: warnings, 96 errors: errors, 97 } 98 if (!result.warnings.length) { 99 delete result.warnings 100 } 101 if (!result.errors.length) { 102 delete result.errors 103 } 104 return result 105} 106 107module.exports = validate 108