Skip to content

Commit

Permalink
Merge branch 'enum/labels'
Browse files Browse the repository at this point in the history
  • Loading branch information
CarsonF committed Sep 26, 2024
2 parents 4dd89f1 + dd3ce3e commit 85556cf
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 12 deletions.
4 changes: 3 additions & 1 deletion packages/nest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"@nestjs/common": "^10",
"@nestjs/core": "^10",
"@nestjs/graphql": "^12",
"@seedcompany/common": ">0.3 <1"
"@seedcompany/common": ">0.3 <1",
"change-case": "^5.4.4",
"title-case": "^4.3.1"
},
"peerDependencies": {
"@nestjs/common": "^10",
Expand Down
55 changes: 44 additions & 11 deletions packages/nest/src/make-enum.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { registerEnumType } from '@nestjs/graphql';
import { cleanJoin, nonEnumerable, setHas } from '@seedcompany/common';
import { cleanJoin, mapKeys, nonEnumerable } from '@seedcompany/common';
import { noCase, splitSeparateNumbers } from 'change-case';
import { titleCase } from 'title-case';
import { inspect, InspectOptionsStylized } from 'util';

export type EnumType<Enum> = Enum extends MadeEnum<infer Values, any, any>
Expand All @@ -8,12 +10,20 @@ export type EnumType<Enum> = Enum extends MadeEnum<infer Values, any, any>

export type MadeEnum<
Values extends string,
Extra = unknown,
ValueDeclaration = EnumValueDeclarationShape,
> = {
readonly [Value in Values & string]: Value;
} & Readonly<Extra> &
EnumHelpers<Values, ValueDeclaration>;
Extra = unknown,
> = EnumHelpers<
Values,
ValueDeclaration & ImplicitValueDeclarationShape<Values>
> &
Readonly<Extra> &
// Allow direct access to the values if they're strict.
// For generic `string` we don't allow this.
// This allows strict values to be compatible with generic values.
// MadeEnum<string> = MadeEnum<X>
(string extends Values
? unknown // ignore addition
: { readonly [Value in Values & string]: Value });

interface EnumOptions<
ValueDeclaration extends EnumValueDeclarationShape,
Expand Down Expand Up @@ -48,9 +58,12 @@ interface EnumOptions<
* This is given the built enum (without any extras), to prevent circular references.
*/
readonly extra?: (
enumObject: MadeEnum<
// MadeEnum without Extras & ImplicitValueDeclarationShape
enumObject: {
readonly [Value in ValuesOfDeclarations<ValueDeclaration> &
string]: Value;
} & EnumHelpers<
ValuesOfDeclarations<ValueDeclaration>,
unknown,
NormalizedValueDeclaration<ValueDeclaration>
>,
) => Extra;
Expand All @@ -66,8 +79,8 @@ export const makeEnum = <
input: EnumOptions<ValueDeclaration, Extra>,
): MadeEnum<
ValuesOfDeclarations<ValueDeclaration>,
[Extra] extends [never] ? unknown : Extra,
NormalizedValueDeclaration<ValueDeclaration>
NormalizedValueDeclaration<ValueDeclaration>,
[Extra] extends [never] ? unknown : Extra
> => {
const {
name,
Expand All @@ -81,6 +94,7 @@ export const makeEnum = <
(value: EnumValueDeclarationShape): EnumValueDeclarationObjectShape =>
typeof value === 'string' ? { value } : value,
);
const entryMap = mapKeys.fromList(entries, (e) => e.value).asMap;

const object = Object.fromEntries(entries.map((v) => [v.value, v.value]));

Expand All @@ -91,7 +105,14 @@ export const makeEnum = <
entries,
[Symbol.iterator]: () => values.values(),
// @ts-expect-error Ignoring generics for implementation.
has: (value: string) => setHas(values, value),
has: (value: string) => entryMap.has(value),
entry: (value: string) => {
const entry = entryMap.get(value);
if (!entry) {
throw new Error(`${name ?? 'Enum'} does not have member: "${value}"`);
}
return entry;
},
[inspect.custom]: (
depth: number,
options: InspectOptionsStylized,
Expand Down Expand Up @@ -136,6 +157,13 @@ export const makeEnum = <
registerEnumType(object, { name, description, valuesMap });
}

for (const entry of entries) {
// @ts-expect-error ignoring immutable here.
entry.label ??= titleCase(
noCase(entry.value, { split: splitSeparateNumbers }),
).replace(/ and /g, ' & ');
}

return object as any;
};

Expand All @@ -162,6 +190,10 @@ interface EnumValueDeclarationObjectShape<Value extends string = string> {
readonly deprecationReason?: string;
}

type ImplicitValueDeclarationShape<Value extends string> = Required<
Pick<EnumValueDeclarationObjectShape<Value>, 'value' | 'label'>
>;

type ValuesOfDeclarations<ValueDeclaration extends EnumValueDeclarationShape> =
ValueDeclaration extends string
? ValueDeclaration
Expand Down Expand Up @@ -191,6 +223,7 @@ type NormalizedValueDeclaration<Declaration extends EnumValueDeclarationShape> =
interface EnumHelpers<Values extends string, ValueDeclaration> {
readonly values: ReadonlySet<Values>;
readonly entries: ReadonlyArray<Readonly<ValueDeclaration>>;
readonly entry: <V extends Values>(value: V) => Readonly<ValueDeclaration>;
readonly has: <In extends string>(
value: In & {},
) => value is In & Values & {};
Expand Down
16 changes: 16 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4066,9 +4066,11 @@ __metadata:
"@nestjs/graphql": "npm:^12"
"@nestjs/testing": "npm:^10"
"@seedcompany/common": "npm:>0.3 <1"
change-case: "npm:^5.4.4"
graphql: "npm:^16.9.0"
reflect-metadata: "npm:^0.1.12"
rxjs: "npm:^7.8.1"
title-case: "npm:^4.3.1"
peerDependencies:
"@nestjs/common": ^10
"@nestjs/core": ^10
Expand Down Expand Up @@ -6253,6 +6255,13 @@ __metadata:
languageName: node
linkType: hard

"change-case@npm:^5.4.4":
version: 5.4.4
resolution: "change-case@npm:5.4.4"
checksum: 10c0/2a9c2b9c9ad6ab2491105aaf506db1a9acaf543a18967798dcce20926c6a173aa63266cb6189f3086e3c14bf7ae1f8ea4f96ecc466fcd582310efa00372f3734
languageName: node
linkType: hard

"char-regex@npm:^1.0.2":
version: 1.0.2
resolution: "char-regex@npm:1.0.2"
Expand Down Expand Up @@ -14552,6 +14561,13 @@ __metadata:
languageName: node
linkType: hard

"title-case@npm:^4.3.1":
version: 4.3.2
resolution: "title-case@npm:4.3.2"
checksum: 10c0/f040c5b0586e3a4ac5881e59ac060ebfa56dae611b0d513ad211fa7f92597d418395fa902fe9d7ee49f98e557e88421e274680b22a3b04dd1ce1c577225444d3
languageName: node
linkType: hard

"tmp@npm:^0.0.33":
version: 0.0.33
resolution: "tmp@npm:0.0.33"
Expand Down

0 comments on commit 85556cf

Please sign in to comment.