Handle scoped package names in typing installer

Fixes #32075
This commit is contained in:
Sheetal Nandi
2019-07-16 10:14:06 -07:00
parent 664671cf49
commit 49ba408e4f
4 changed files with 114 additions and 56 deletions

View File

@@ -289,9 +289,8 @@ namespace ts.JsTyping {
}
export const enum PackageNameValidationResult {
export const enum NameValidationResult {
Ok,
ScopedPackagesNotSupported,
EmptyName,
NameTooLong,
NameStartsWithDot,
@@ -301,49 +300,77 @@ namespace ts.JsTyping {
const maxPackageNameLength = 214;
export interface ScopedPackageNameValidationResult {
name: string;
isScopeName: boolean;
result: NameValidationResult;
}
export type PackageNameValidationResult = NameValidationResult | ScopedPackageNameValidationResult;
/**
* Validates package name using rules defined at https://docs.npmjs.com/files/package.json
*/
export function validatePackageName(packageName: string): PackageNameValidationResult {
return validatePackageNameWorker(packageName, /*supportScopedPackage*/ true);
}
function validatePackageNameWorker(packageName: string, supportScopedPackage: false): NameValidationResult;
function validatePackageNameWorker(packageName: string, supportScopedPackage: true): PackageNameValidationResult;
function validatePackageNameWorker(packageName: string, supportScopedPackage: boolean): PackageNameValidationResult {
if (!packageName) {
return PackageNameValidationResult.EmptyName;
return NameValidationResult.EmptyName;
}
if (packageName.length > maxPackageNameLength) {
return PackageNameValidationResult.NameTooLong;
return NameValidationResult.NameTooLong;
}
if (packageName.charCodeAt(0) === CharacterCodes.dot) {
return PackageNameValidationResult.NameStartsWithDot;
return NameValidationResult.NameStartsWithDot;
}
if (packageName.charCodeAt(0) === CharacterCodes._) {
return PackageNameValidationResult.NameStartsWithUnderscore;
return NameValidationResult.NameStartsWithUnderscore;
}
// check if name is scope package like: starts with @ and has one '/' in the middle
// scoped packages are not currently supported
// TODO: when support will be added we'll need to split and check both scope and package name
if (/^@[^/]+\/[^/]+$/.test(packageName)) {
return PackageNameValidationResult.ScopedPackagesNotSupported;
if (supportScopedPackage) {
const matches = /^@([^/]+)\/([^/]+)$/.exec(packageName);
if (matches) {
const scopeResult = validatePackageNameWorker(matches[1], /*supportScopedPackage*/ false);
if (scopeResult !== NameValidationResult.Ok) {
return { name: matches[1], isScopeName: true, result: scopeResult };
}
const packageResult = validatePackageNameWorker(matches[2], /*supportScopedPackage*/ false);
if (packageResult !== NameValidationResult.Ok) {
return { name: matches[2], isScopeName: false, result: packageResult };
}
return NameValidationResult.Ok;
}
}
if (encodeURIComponent(packageName) !== packageName) {
return PackageNameValidationResult.NameContainsNonURISafeCharacters;
return NameValidationResult.NameContainsNonURISafeCharacters;
}
return PackageNameValidationResult.Ok;
return NameValidationResult.Ok;
}
export function renderPackageNameValidationFailure(result: PackageNameValidationResult, typing: string): string {
return typeof result === "object" ?
renderPackageNameValidationFailureWorker(typing, result.result, result.name, result.isScopeName) :
renderPackageNameValidationFailureWorker(typing, result, typing, /*isScopeName*/ false);
}
function renderPackageNameValidationFailureWorker(typing: string, result: NameValidationResult, name: string, isScopeName: boolean): string {
const kind = isScopeName ? "Scope" : "Package";
switch (result) {
case PackageNameValidationResult.EmptyName:
return `Package name '${typing}' cannot be empty`;
case PackageNameValidationResult.NameTooLong:
return `Package name '${typing}' should be less than ${maxPackageNameLength} characters`;
case PackageNameValidationResult.NameStartsWithDot:
return `Package name '${typing}' cannot start with '.'`;
case PackageNameValidationResult.NameStartsWithUnderscore:
return `Package name '${typing}' cannot start with '_'`;
case PackageNameValidationResult.ScopedPackagesNotSupported:
return `Package '${typing}' is scoped and currently is not supported`;
case PackageNameValidationResult.NameContainsNonURISafeCharacters:
return `Package name '${typing}' contains non URI safe characters`;
case PackageNameValidationResult.Ok:
case NameValidationResult.EmptyName:
return `'${typing}':: ${kind} name '${name}' cannot be empty`;
case NameValidationResult.NameTooLong:
return `'${typing}':: ${kind} name '${name}' should be less than ${maxPackageNameLength} characters`;
case NameValidationResult.NameStartsWithDot:
return `'${typing}':: ${kind} name '${name}' cannot start with '.'`;
case NameValidationResult.NameStartsWithUnderscore:
return `'${typing}':: ${kind} name '${name}' cannot start with '_'`;
case NameValidationResult.NameContainsNonURISafeCharacters:
return `'${typing}':: ${kind} name '${name}' contains non URI safe characters`;
case NameValidationResult.Ok:
return Debug.fail(); // Shouldn't have called this.
default:
throw Debug.assertNever(result);