Skip to content

Commit 69b182a

Browse files
authored
[v4] [icons] fix: remove dependency on type-fest (#5010)
1 parent f6b23cc commit 69b182a

14 files changed

+385
-10
lines changed

packages/icons/.eslintrc.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../.eslintrc.js",
3+
"ignorePatterns": ["src/type-utils/*"]
4+
}

packages/icons/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@
3838
"dependencies": {
3939
"change-case": "^4.1.2",
4040
"classnames": "^2.2",
41-
"tslib": "~1.13.0",
42-
"type-fest": "^1.4.0"
41+
"tslib": "~1.13.0"
4342
},
4443
"devDependencies": {
4544
"@blueprintjs/node-build-scripts": "^2.0.0-beta.1",

packages/icons/src/iconNames.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
/* eslint-disable camelcase */
1818

1919
import { pascalCase, snakeCase } from "change-case";
20-
import type { PascalCase, ScreamingSnakeCase } from "type-fest";
2120

22-
// icon sets are identical aside from SVG paths, so we just import the info for the 16px set
2321
import { BlueprintIcons_16, BlueprintIcons_16Id as IconName } from "./generated/16px/blueprint-icons-16";
22+
import type { PascalCase, ScreamingSnakeCase } from "./type-utils";
23+
24+
// icon sets are identical aside from SVG paths, so we just import the info for the 16px set
2425

2526
export type { IconName };
2627

packages/icons/src/iconSvgPaths.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
*/
1616

1717
import { pascalCase } from "change-case";
18-
import type { PascalCase } from "type-fest";
1918

2019
import type { IconName } from "./iconNames";
20+
import type { PascalCase } from "./type-utils";
2121

2222
/* eslint-disable @typescript-eslint/no-var-requires */
2323
export const IconSvgPaths16 = require("./generated/16px/paths") as Record<PascalCase<IconName>, string[]>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { WordSeparators } from "./sourceUtilities";
2+
import { Split } from "./utilities";
3+
4+
/**
5+
Step by step takes the first item in an array literal, formats it and adds it to a string literal, and then recursively appends the remainder.
6+
7+
Only to be used by `CamelCaseStringArray<>`.
8+
9+
@see CamelCaseStringArray
10+
*/
11+
type InnerCamelCaseStringArray<Parts extends any[], PreviousPart> = Parts extends [
12+
`${infer FirstPart}`,
13+
...infer RemainingParts
14+
]
15+
? FirstPart extends undefined
16+
? ""
17+
: FirstPart extends ""
18+
? InnerCamelCaseStringArray<RemainingParts, PreviousPart>
19+
: `${PreviousPart extends "" ? FirstPart : Capitalize<FirstPart>}${InnerCamelCaseStringArray<
20+
RemainingParts,
21+
FirstPart
22+
>}`
23+
: "";
24+
25+
/**
26+
Starts fusing the output of `Split<>`, an array literal of strings, into a camel-cased string literal.
27+
28+
It's separate from `InnerCamelCaseStringArray<>` to keep a clean API outwards to the rest of the code.
29+
30+
@see Split
31+
*/
32+
type CamelCaseStringArray<Parts extends string[]> = Parts extends [`${infer FirstPart}`, ...infer RemainingParts]
33+
? Uncapitalize<`${FirstPart}${InnerCamelCaseStringArray<RemainingParts, FirstPart>}`>
34+
: never;
35+
36+
/**
37+
Convert a string literal to camel-case.
38+
39+
This can be useful when, for example, converting some kebab-cased command-line flags or a snake-cased database result.
40+
41+
@example
42+
```
43+
import {CamelCase} from 'type-fest';
44+
45+
// Simple
46+
47+
const someVariable: CamelCase<'foo-bar'> = 'fooBar';
48+
49+
// Advanced
50+
51+
type CamelCasedProperties<T> = {
52+
[K in keyof T as CamelCase<K>]: T[K]
53+
};
54+
55+
interface RawOptions {
56+
'dry-run': boolean;
57+
'full_family_name': string;
58+
foo: number;
59+
BAR: string;
60+
QUZ_QUX: number;
61+
'OTHER-FIELD': boolean;
62+
}
63+
64+
const dbResult: CamelCasedProperties<ModelProps> = {
65+
dryRun: true,
66+
fullFamilyName: 'bar.js',
67+
foo: 123,
68+
bar: 'foo',
69+
quzQux: 6,
70+
otherField: false
71+
};
72+
```
73+
74+
@category Template Literals
75+
*/
76+
export type CamelCase<K> = K extends string
77+
? K extends Uppercase<K>
78+
? CamelCaseStringArray<Split<Lowercase<K>, WordSeparators>>
79+
: CamelCaseStringArray<Split<K, WordSeparators>>
80+
: K;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { UpperCaseCharacters, WordSeparators } from "./sourceUtilities";
2+
3+
/**
4+
Unlike a simpler split, this one includes the delimiter splitted on in the resulting array literal. This is to enable splitting on, for example, upper-case characters.
5+
6+
@category Template Literals
7+
*/
8+
export type SplitIncludingDelimiters<Source extends string, Delimiter extends string> = Source extends ""
9+
? []
10+
: Source extends `${infer FirstPart}${Delimiter}${infer SecondPart}`
11+
? Source extends `${FirstPart}${infer UsedDelimiter}${SecondPart}`
12+
? UsedDelimiter extends Delimiter
13+
? Source extends `${infer FirstPart}${UsedDelimiter}${infer SecondPart}`
14+
? [
15+
...SplitIncludingDelimiters<FirstPart, Delimiter>,
16+
UsedDelimiter,
17+
...SplitIncludingDelimiters<SecondPart, Delimiter>
18+
]
19+
: never
20+
: never
21+
: never
22+
: [Source];
23+
24+
/**
25+
Format a specific part of the splitted string literal that `StringArrayToDelimiterCase<>` fuses together, ensuring desired casing.
26+
27+
@see StringArrayToDelimiterCase
28+
*/
29+
type StringPartToDelimiterCase<
30+
StringPart extends string,
31+
UsedWordSeparators extends string,
32+
UsedUpperCaseCharacters extends string,
33+
Delimiter extends string
34+
> = StringPart extends UsedWordSeparators
35+
? Delimiter
36+
: StringPart extends UsedUpperCaseCharacters
37+
? `${Delimiter}${Lowercase<StringPart>}`
38+
: StringPart;
39+
40+
/**
41+
Takes the result of a splitted string literal and recursively concatenates it together into the desired casing.
42+
43+
It receives `UsedWordSeparators` and `UsedUpperCaseCharacters` as input to ensure it's fully encapsulated.
44+
45+
@see SplitIncludingDelimiters
46+
*/
47+
type StringArrayToDelimiterCase<
48+
Parts extends any[],
49+
UsedWordSeparators extends string,
50+
UsedUpperCaseCharacters extends string,
51+
Delimiter extends string
52+
> = Parts extends [`${infer FirstPart}`, ...infer RemainingParts]
53+
? `${StringPartToDelimiterCase<
54+
FirstPart,
55+
UsedWordSeparators,
56+
UsedUpperCaseCharacters,
57+
Delimiter
58+
>}${StringArrayToDelimiterCase<RemainingParts, UsedWordSeparators, UsedUpperCaseCharacters, Delimiter>}`
59+
: "";
60+
61+
/**
62+
Convert a string literal to a custom string delimiter casing.
63+
64+
This can be useful when, for example, converting a camel-cased object property to an oddly cased one.
65+
66+
@see KebabCase
67+
@see SnakeCase
68+
69+
@example
70+
```
71+
import {DelimiterCase} from 'type-fest';
72+
73+
// Simple
74+
75+
const someVariable: DelimiterCase<'fooBar', '#'> = 'foo#bar';
76+
77+
// Advanced
78+
79+
type OddlyCasedProperties<T> = {
80+
[K in keyof T as DelimiterCase<K, '#'>]: T[K]
81+
};
82+
83+
interface SomeOptions {
84+
dryRun: boolean;
85+
includeFile: string;
86+
foo: number;
87+
}
88+
89+
const rawCliOptions: OddlyCasedProperties<SomeOptions> = {
90+
'dry#run': true,
91+
'include#file': 'bar.js',
92+
foo: 123
93+
};
94+
```
95+
96+
@category Template Literals
97+
*/
98+
export type DelimiterCase<Value, Delimiter extends string> = Value extends string
99+
? StringArrayToDelimiterCase<
100+
SplitIncludingDelimiters<Value, WordSeparators | UpperCaseCharacters>,
101+
WordSeparators,
102+
UpperCaseCharacters,
103+
Delimiter
104+
>
105+
: Value;
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
Returns a boolean for whether given two types are equal.
3+
4+
@link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
5+
*/
6+
type IsEqual<T, U> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? true : false;
7+
8+
/**
9+
Returns a boolean for whether the given array includes the given item.
10+
11+
This can be useful if another type wants to make a decision based on whether the array includes that item.
12+
13+
@example
14+
```
15+
import {Includes} from 'type-fest';
16+
17+
type hasRed<array extends any[]> = Includes<array, 'red'>;
18+
```
19+
20+
@category Utilities
21+
*/
22+
export type Includes<Value extends any[], Item> = IsEqual<Value[0], Item> extends true
23+
? true
24+
: Value extends [Value[0], ...infer rest]
25+
? Includes<rest, Item>
26+
: false;
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2021 Palantir Technologies, Inc. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/** @fileoverview Mapped type utils copied from the "type-fest" package */
18+
19+
export { PascalCase } from "./pascal-case";
20+
export { ScreamingSnakeCase } from "./screaming-snake-case";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { CamelCase } from "./camel-case";
2+
3+
/**
4+
Converts a string literal to pascal-case.
5+
6+
@example
7+
```
8+
import {PascalCase} from 'type-fest';
9+
10+
// Simple
11+
12+
const someVariable: PascalCase<'foo-bar'> = 'FooBar';
13+
14+
// Advanced
15+
16+
type PascalCaseProps<T> = {
17+
[K in keyof T as PascalCase<K>]: T[K]
18+
};
19+
20+
interface RawOptions {
21+
'dry-run': boolean;
22+
'full_family_name': string;
23+
foo: number;
24+
}
25+
26+
const dbResult: CamelCasedProperties<ModelProps> = {
27+
DryRun: true,
28+
FullFamilyName: 'bar.js',
29+
Foo: 123
30+
};
31+
```
32+
33+
@category Template Literals
34+
*/
35+
export type PascalCase<Value> = CamelCase<Value> extends string ? Capitalize<CamelCase<Value>> : CamelCase<Value>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { SplitIncludingDelimiters } from "./delimiter-case";
2+
import { SnakeCase } from "./snake-case";
3+
import { Includes } from "./includes";
4+
5+
/**
6+
Returns a boolean for whether the string is screaming snake case.
7+
*/
8+
type IsScreamingSnakeCase<Value extends string> = Value extends Uppercase<Value>
9+
? Includes<SplitIncludingDelimiters<Lowercase<Value>, "_">, "_"> extends true
10+
? true
11+
: false
12+
: false;
13+
14+
/**
15+
Convert a string literal to screaming-snake-case.
16+
17+
This can be useful when, for example, converting a camel-cased object property to a screaming-snake-cased SQL column name.
18+
19+
@example
20+
```
21+
import {ScreamingSnakeCase} from 'type-fest';
22+
23+
const someVariable: ScreamingSnakeCase<'fooBar'> = 'FOO_BAR';
24+
```
25+
26+
@category Template Literals
27+
*/
28+
export type ScreamingSnakeCase<Value> = Value extends string
29+
? IsScreamingSnakeCase<Value> extends true
30+
? Value
31+
: Uppercase<SnakeCase<Value>>
32+
: Value;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { DelimiterCase } from "./delimiter-case";
2+
3+
/**
4+
Convert a string literal to snake-case.
5+
6+
This can be useful when, for example, converting a camel-cased object property to a snake-cased SQL column name.
7+
8+
@example
9+
```
10+
import {SnakeCase} from 'type-fest';
11+
12+
// Simple
13+
14+
const someVariable: SnakeCase<'fooBar'> = 'foo_bar';
15+
16+
// Advanced
17+
18+
type SnakeCasedProperties<T> = {
19+
[K in keyof T as SnakeCase<K>]: T[K]
20+
};
21+
22+
interface ModelProps {
23+
isHappy: boolean;
24+
fullFamilyName: string;
25+
foo: number;
26+
}
27+
28+
const dbResult: SnakeCasedProperties<ModelProps> = {
29+
'is_happy': true,
30+
'full_family_name': 'Carla Smith',
31+
foo: 123
32+
};
33+
```
34+
35+
@category Template Literals
36+
*/
37+
export type SnakeCase<Value> = DelimiterCase<Value, "_">;

0 commit comments

Comments
 (0)