Skip to content

Commit

Permalink
feat: add flatCase and trainCase (#68)
Browse files Browse the repository at this point in the history
  • Loading branch information
itpropro authored Jan 10, 2024
1 parent 5024603 commit d2c281f
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 0 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,26 @@ snakeCase("foo-barBaz");
// foo_bar_baz
```

### `flatCase`

Splits string and joins by flatcase convention:

```ts
flatCase("foo-barBaz");
// foobarbaz
```

### `trainCase(str, opts?: { normalize })`

Split string and joins by Train-Case (a.k.a. HTTP-Header-Case) convention:

```ts
trainCase("FooBARb");
// Foo-Ba-Rb
```

**Notice:** If an uppercase letter is followed by other uppercase letters (like `WWWAuthenticate`), they are preserved (=> `WWW-Authenticate`). You can use `{ normalize: true }` for strictly only having the first letter uppercased.

### `upperFirst(str)`

Converts first character to upper case:
Expand Down
25 changes: 25 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type {
SnakeCase,
SplitByCase,
CaseOptions,
TrainCase,
FlatCase,
} from "./types";

const NUMBER_CHAR_RE = /\d/;
Expand Down Expand Up @@ -144,4 +146,27 @@ export function snakeCase<T extends string | readonly string[]>(str?: T) {
return kebabCase(str || "", "_") as SnakeCase<T>;
}

export function flatCase(): "";
export function flatCase<T extends string | readonly string[]>(
str: T,
): FlatCase<T>;
export function flatCase<T extends string | readonly string[]>(str?: T) {
return kebabCase(str || "", "") as FlatCase<T>;
}

export function trainCase(): "";
export function trainCase<
T extends string | readonly string[],
UserCaseOptions extends CaseOptions = CaseOptions,
>(str: T, opts?: UserCaseOptions): TrainCase<T, UserCaseOptions["normalize"]>;
export function trainCase<
T extends string | readonly string[],
UserCaseOptions extends CaseOptions = CaseOptions,
>(str?: T, opts?: UserCaseOptions) {
return (Array.isArray(str) ? str : splitByCase(str as string))
.filter(Boolean)
.map((p) => upperFirst(opts?.normalize ? p.toLowerCase() : p))
.join("-") as TrainCase<T, UserCaseOptions["normalize"]>;
}

export * from "./types";
20 changes: 20 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,23 @@ export type SnakeCase<T extends string | readonly string[]> = JoinByCase<
T,
"_"
>;

export type TrainCase<
T,
Normalize extends boolean | undefined = false,
> = string extends T
? string
: string[] extends T
? string
: T extends string
? SplitByCase<T> extends readonly string[]
? CapitalizedWords<SplitByCase<T>, "-">
: never
: T extends readonly string[]
? CapitalizedWords<T, "-", Normalize>
: never;

export type FlatCase<
T extends string | readonly string[],
Joiner extends string = "",
> = JoinByCase<T, Joiner>;
41 changes: 41 additions & 0 deletions test/scule.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
upperFirst,
lowerFirst,
snakeCase,
trainCase,
flatCase,
} from "../src";

describe("splitByCase", () => {
Expand Down Expand Up @@ -103,3 +105,42 @@ describe("lowerFirst", () => {
expect(lowerFirst(input)).toMatchObject(expected);
});
});

describe("trainCase", () => {
test.each([
["", ""],
["f", "F"],
["foo", "Foo"],
["foo-bAr", "Foo-B-Ar"],
["AcceptCH", "Accept-CH"],
["foo_bar-baz/qux", "Foo-Bar-Baz-Qux"],
["FOO_BAR", "FOO-BAR"],
["foo--bar-Baz", "Foo-Bar-Baz"],
["WWW-authenticate", "WWW-Authenticate"],
["WWWAuthenticate", "WWW-Authenticate"],
])("%s => %s", (input, expected) => {
expect(trainCase(input)).toMatchObject(expected);
});

test.each([
["AcceptCH", "Accept-Ch"],
["FOO_BAR", "Foo-Bar"],
["WWW-authenticate", "Www-Authenticate"],
])("%s => %s", (input, expected) => {
expect(trainCase(input, { normalize: true })).toMatchObject(expected);
});
});

describe("flatCase", () => {
test.each([
["", ""],
["foo", "foo"],
["foo-bAr", "foobar"],
["FooBARb", "foobarb"],
["foo_bar-baz/qux", "foobarbazqux"],
["FOO_BAR", "foobar"],
["foo--bar-Baz", "foobarbaz"],
])("%s => %s", (input, expected) => {
expect(flatCase(input)).toMatchObject(expected);
});
});

0 comments on commit d2c281f

Please sign in to comment.