|
| 1 | +'use strict' |
| 2 | + |
| 3 | +var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$') |
| 4 | +var builtins = require('builtins') |
| 5 | +var blacklist = [ |
| 6 | + 'node_modules', |
| 7 | + 'favicon.ico', |
| 8 | +] |
| 9 | + |
| 10 | +function 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 | + |
| 91 | +var 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 | + |
| 107 | +module.exports = validate |
0 commit comments