99 lines
3.0 KiB
Go
99 lines
3.0 KiB
Go
package ata
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type NameValidationResult int
|
|
|
|
const (
|
|
NameOk NameValidationResult = iota
|
|
EmptyName
|
|
NameTooLong
|
|
NameStartsWithDot
|
|
NameStartsWithUnderscore
|
|
NameContainsNonURISafeCharacters
|
|
)
|
|
|
|
const maxPackageNameLength = 214
|
|
|
|
/**
|
|
* Validates package name using rules defined at https://docs.npmjs.com/files/package.json
|
|
*
|
|
* @internal
|
|
*/
|
|
func ValidatePackageName(packageName string) (result NameValidationResult, name string, isScopeName bool) {
|
|
return validatePackageNameWorker(packageName /*supportScopedPackage*/, true)
|
|
}
|
|
|
|
func validatePackageNameWorker(packageName string, supportScopedPackage bool) (result NameValidationResult, name string, isScopeName bool) {
|
|
packageNameLen := len(packageName)
|
|
if packageNameLen == 0 {
|
|
return EmptyName, "", false
|
|
}
|
|
if packageNameLen > maxPackageNameLength {
|
|
return NameTooLong, "", false
|
|
}
|
|
firstChar, _ := utf8.DecodeRuneInString(packageName)
|
|
if firstChar == '.' {
|
|
return NameStartsWithDot, "", false
|
|
}
|
|
if firstChar == '_' {
|
|
return NameStartsWithUnderscore, "", false
|
|
}
|
|
// check if name is scope package like: starts with @ and has one '/' in the middle
|
|
// scoped packages are not currently supported
|
|
if supportScopedPackage {
|
|
if withoutScope, found := strings.CutPrefix(packageName, "@"); found {
|
|
scope, scopedPackageName, found := strings.Cut(withoutScope, "/")
|
|
if found && len(scope) > 0 && len(scopedPackageName) > 0 && !strings.Contains(scopedPackageName, "/") {
|
|
scopeResult, _, _ := validatePackageNameWorker(scope /*supportScopedPackage*/, false)
|
|
if scopeResult != NameOk {
|
|
return scopeResult, scope, true
|
|
}
|
|
packageResult, _, _ := validatePackageNameWorker(scopedPackageName /*supportScopedPackage*/, false)
|
|
if packageResult != NameOk {
|
|
return packageResult, scopedPackageName, false
|
|
}
|
|
return NameOk, "", false
|
|
}
|
|
}
|
|
}
|
|
if url.QueryEscape(packageName) != packageName {
|
|
return NameContainsNonURISafeCharacters, "", false
|
|
}
|
|
return NameOk, "", false
|
|
}
|
|
|
|
/** @internal */
|
|
func renderPackageNameValidationFailure(typing string, result NameValidationResult, name string, isScopeName bool) string {
|
|
var kind string
|
|
if isScopeName {
|
|
kind = "Scope"
|
|
} else {
|
|
kind = "Package"
|
|
}
|
|
if name == "" {
|
|
name = typing
|
|
}
|
|
switch result {
|
|
case EmptyName:
|
|
return fmt.Sprintf("'%s':: %s name '%s' cannot be empty", typing, kind, name)
|
|
case NameTooLong:
|
|
return fmt.Sprintf("'%s':: %s name '%s' should be less than %d characters", typing, kind, name, maxPackageNameLength)
|
|
case NameStartsWithDot:
|
|
return fmt.Sprintf("'%s':: %s name '%s' cannot start with '.'", typing, kind, name)
|
|
case NameStartsWithUnderscore:
|
|
return fmt.Sprintf("'%s':: %s name '%s' cannot start with '_'", typing, kind, name)
|
|
case NameContainsNonURISafeCharacters:
|
|
return fmt.Sprintf("'%s':: %s name '%s' contains non URI safe characters", typing, kind, name)
|
|
case NameOk:
|
|
panic("Unexpected Ok result")
|
|
default:
|
|
panic("Unknown package name validation result")
|
|
}
|
|
}
|