diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4895aea..e53b609 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,6 +19,7 @@ jobs: ecma-pnpm: true - run: pnpm install - run: task check + - run: task test - run: task build build-docs: diff --git a/Taskfile.yml b/Taskfile.yml index dd0165c..3493979 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -17,6 +17,8 @@ tasks: - task: ecma:fix build: - task: ecma:lib-build + test: + - task: ecma:test doc: - task: ecma:doc-build diff --git a/package.json b/package.json index e4399d4..0eb54da 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pistonite/celera", - "version": "0.2.0", + "version": "0.2.1", "type": "module", "private": true, "description": "In-house UI framework", diff --git a/src/style/gale.ts b/src/style/gale.ts index 3a1b52c..e6665e9 100644 --- a/src/style/gale.ts +++ b/src/style/gale.ts @@ -188,22 +188,65 @@ export type GaleKeys = T | GaleBuiltinKey; /** Hook to be called inside a component to get the `m` function. See {@link gale} */ export type GaleHook = () => GaleFn; /** The `m` function that turns a style string into class names */ -export type GaleFn = (classes: GaleString) => string; +export type GaleFn = (classes: GaleString) => string; /** Type-safe, space-separated style idents */ -export type GaleString = K extends T - ? K - : K extends `${infer U} ${infer N}` - ? U extends T - ? GaleString extends N - ? `${U} ${GaleString}` - : Prettify & { After: U }> - : { InvalidStyleIdent: U } - : { InvalidStyleIdent: K }; +export type GaleString = 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 extends N + ? `${U} ${GaleString}` + : Prettify, U>> + : { InvalidStyleIdent: U } + : { InvalidStyleIdent: K }; +type ExtendAfter = T extends { After: string } ? T : T & { After: U }; type Prettify = { [K in keyof T]: T[K]; } & {}; +if (import.meta.vitest) { + const { test, expectTypeOf } = import.meta.vitest; + type Validate = GaleString; + test("GaleString", () => { + expectTypeOf>().toExtend(); + expectTypeOf>().toExtend(); + expectTypeOf>().toEqualTypeOf<{ InvalidStyleIdent: "biz" }>(); + + expectTypeOf>().toExtend(); + expectTypeOf>().toEqualTypeOf<{ + DuplicateStyleIdent: "foo"; + After: "foo"; + }>(); + expectTypeOf>().toEqualTypeOf<{ + InvalidStyleIdent: "biz"; + After: "foo"; + }>(); + expectTypeOf>().toEqualTypeOf<{ InvalidStyleIdent: "biz" }>(); + expectTypeOf>().toEqualTypeOf<{ + InvalidStyleIdent: "biz"; + After: "bar"; + }>(); + expectTypeOf>().toEqualTypeOf<{ + InvalidStyleIdent: "biz"; + After: "foo"; + }>(); + expectTypeOf>().toEqualTypeOf<{ + InvalidStyleIdent: "biz"; + After: "foo"; + }>(); + expectTypeOf>().toEqualTypeOf<{ InvalidStyleIdent: "biz" }>(); + expectTypeOf>().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": {