Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jobs:
ecma-pnpm: true
- run: pnpm install
- run: task check
- run: task test
- run: task build

build-docs:
Expand Down
2 changes: 2 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ tasks:
- task: ecma:fix
build:
- task: ecma:lib-build
test:
- task: ecma:test

doc:
- task: ecma:doc-build
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pistonite/celera",
"version": "0.2.0",
"version": "0.2.1",
"type": "module",
"private": true,
"description": "In-house UI framework",
Expand Down
63 changes: 53 additions & 10 deletions src/style/gale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,22 +188,65 @@ export type GaleKeys<T extends string> = T | GaleBuiltinKey;
/** Hook to be called inside a component to get the `m` function. See {@link gale} */
export type GaleHook<T extends string> = () => GaleFn<T>;
/** The `m` function that turns a style string into class names */
export type GaleFn<T extends string> = <K extends string>(classes: GaleString<K, T>) => string;
export type GaleFn<T extends string> = <K extends string>(classes: GaleString<K, T, []>) => string;

/** Type-safe, space-separated style idents */
export type GaleString<K extends string, T extends string> = K extends T
? K
: K extends `${infer U} ${infer N}`
? U extends T
? GaleString<N, T> extends N
? `${U} ${GaleString<N, T>}`
: Prettify<GaleString<N, T> & { After: U }>
: { InvalidStyleIdent: U }
: { InvalidStyleIdent: K };
export type GaleString<K extends string, T extends string, S extends string[]> = K extends S[number]
? { DuplicateStyleIdent: K }
: K extends T
? K
: K extends `${infer U} ${infer N}`
? U extends S[number]
? { DuplicateStyleIdent: U }
: U extends T
? GaleString<N, T, [...S, U]> extends N
? `${U} ${GaleString<N, T, [...S, U]>}`
: Prettify<ExtendAfter<GaleString<N, T, [...S, U]>, U>>
: { InvalidStyleIdent: U }
: { InvalidStyleIdent: K };
type ExtendAfter<T, U> = T extends { After: string } ? T : T & { After: U };
type Prettify<T> = {
[K in keyof T]: T[K];
} & {};

if (import.meta.vitest) {
const { test, expectTypeOf } = import.meta.vitest;
type Validate<K extends string> = GaleString<K, "foo" | "bar" | `long-${number}`, []>;
test("GaleString", () => {
expectTypeOf<Validate<"bar">>().toExtend<string>();
expectTypeOf<Validate<"foo">>().toExtend<string>();
expectTypeOf<Validate<"biz">>().toEqualTypeOf<{ InvalidStyleIdent: "biz" }>();

expectTypeOf<Validate<"foo bar">>().toExtend<string>();
expectTypeOf<Validate<"foo foo">>().toEqualTypeOf<{
DuplicateStyleIdent: "foo";
After: "foo";
}>();
expectTypeOf<Validate<"foo biz">>().toEqualTypeOf<{
InvalidStyleIdent: "biz";
After: "foo";
}>();
expectTypeOf<Validate<"biz foo">>().toEqualTypeOf<{ InvalidStyleIdent: "biz" }>();
expectTypeOf<Validate<"foo bar biz">>().toEqualTypeOf<{
InvalidStyleIdent: "biz";
After: "bar";
}>();
expectTypeOf<Validate<"foo biz biz">>().toEqualTypeOf<{
InvalidStyleIdent: "biz";
After: "foo";
}>();
expectTypeOf<Validate<"foo biz bar">>().toEqualTypeOf<{
InvalidStyleIdent: "biz";
After: "foo";
}>();
expectTypeOf<Validate<"biz biz bar">>().toEqualTypeOf<{ InvalidStyleIdent: "biz" }>();
expectTypeOf<Validate<"biz biz">>().toEqualTypeOf<{ InvalidStyleIdent: "biz" }>();
expectTypeOf<
Validate<"foo bar long-1 long-2 long-3 long-4 long-5 big long-6 long-7 long-8 long-9">
>().toEqualTypeOf<{ InvalidStyleIdent: "big"; After: "long-5" }>();
});
}

/** Built-in styles for {@link gale} */
export const GALE_BUILTIN_STYLES = {
"wh-100v": {
Expand Down
Loading