Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move @babel/helper-validator-option to ts #12410

Merged
merged 3 commits into from Dec 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions lib/babel-packages.js.flow
Expand Up @@ -133,3 +133,27 @@ declare module "@babel/helper-validator-identifier" {
declare function isIdentifierChar(code: number): boolean;
declare function isIdentifierName(name: string): boolean;
}

declare module "@babel/helper-validator-option" {
declare class OptionValidator {
descriptor: string;
constructor(descriptor: string): OptionValidator;
validateTopLevelOptions(options: Object, TopLevelOptionShape: Object): void;
validateBooleanOption<T>(
name: string,
value?: boolean,
defaultValue?: T
): boolean | T;
validateStringOption<T>(
name: string,
value?: string,
defaultValue?: T
): string | T;
invariant(condition: boolean, message: string): void;
formatMessage(message: string): string;
}
declare function findSuggestion(
str: string,
arr: $ReadonlyArray<string>
): string;
}
@@ -1,5 +1,3 @@
// @flow

const { min } = Math;

// a minimal leven distance implementation
Expand All @@ -9,7 +7,7 @@ const { min } = Math;
// that have less than 20 ASCII characters

// https://rosettacode.org/wiki/Levenshtein_distance#ES5
function levenshtein(a, b) {
function levenshtein(a: string, b: string): number {
let t = [],
u = [],
i,
Expand Down Expand Up @@ -44,7 +42,7 @@ function levenshtein(a, b) {
* @param {string[]} arr
* @returns {string}
*/
export function findSuggestion(str: string, arr: string[]): string {
export function findSuggestion(str: string, arr: readonly string[]): string {
const distances = arr.map<number>(el => levenshtein(el, str));
return arr[distances.indexOf(min(...distances))];
}
@@ -1,4 +1,3 @@
// @flow
import { findSuggestion } from "./find-suggestion.js";

export class OptionValidator {
Expand Down Expand Up @@ -30,13 +29,13 @@ export class OptionValidator {

// note: we do not consider rewrite them to high order functions
// until we have to support `validateNumberOption`.
validateBooleanOption(
validateBooleanOption<T>(
name: string,
value?: boolean,
defaultValue?: boolean,
): boolean | void {
defaultValue?: T,
): boolean | T {
if (value === undefined) {
value = defaultValue;
return defaultValue;
} else {
this.invariant(
typeof value === "boolean",
Expand All @@ -46,13 +45,13 @@ export class OptionValidator {
return value;
}

validateStringOption(
validateStringOption<T>(
name: string,
value?: string,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, value could be anything here I think (otherwise we wouldn't need this helper).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally value should be of type that extends string. So if we accidentally pass number value to validateStringOption, the type checker should throw.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't it be anything, since it's whatever the user gave us as input? We cannot statically know the type of plugin options, so an user could pass a number to a plugin, the plugin will pass a number to this helper, and then this helper will throw.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also offer our type interface to programmatic users:

{ a: any, b: any }

is technically correct (which is the type user inputs) but not much of value for programmatic usage (API caller) and/or schema check. The validator here is a runtime enforcement of type checking -- so the user input does match our assumption of types, so it makes sense that if the type checking is good, it is also sound in runtime.

defaultValue?: string,
): string | void {
defaultValue?: T,
): string | T {
if (value === undefined) {
value = defaultValue;
return defaultValue;
} else {
this.invariant(
typeof value === "string",
Expand Down