praxis-kit 0.1.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{build-runtime-CJ_nQEaZ.js → build-runtime-cNkW-D62.js} +331 -207
- package/dist/contract/index.d.ts +396 -14
- package/dist/contract/index.js +66 -1
- package/dist/eslint/index.js +11 -5
- package/dist/{index-BIBd_iPD.d.ts → index-zJDSXTxL.d.ts} +366 -67
- package/dist/lit/index.d.ts +267 -55
- package/dist/lit/index.js +275 -215
- package/dist/preact/index.d.ts +296 -52
- package/dist/preact/index.js +277 -210
- package/dist/react/index.d.ts +13 -4
- package/dist/react/index.js +22 -72
- package/dist/react/legacy.d.ts +9 -4
- package/dist/react/legacy.js +14 -2
- package/dist/solid/index.d.ts +253 -40
- package/dist/solid/index.js +271 -210
- package/dist/svelte/index.d.ts +203 -33
- package/dist/svelte/index.js +270 -207
- package/dist/tailwind/index.d.ts +12 -1
- package/dist/vite-plugin/index.js +289 -199
- package/dist/vue/index.d.ts +252 -41
- package/dist/vue/index.js +277 -210
- package/dist/web/index.d.ts +287 -53
- package/dist/web/index.js +275 -215
- package/package.json +5 -5
package/dist/lit/index.d.ts
CHANGED
|
@@ -32,13 +32,6 @@ type SubComponentMap = Readonly<AnyRecord>;
|
|
|
32
32
|
* editor hovers remain self-descriptive.
|
|
33
33
|
*/
|
|
34
34
|
type NoVariants = Readonly<EmptyRecord>;
|
|
35
|
-
/**
|
|
36
|
-
* Default `TPreset` type for components that declare no named presets.
|
|
37
|
-
*
|
|
38
|
-
* Structurally identical to `Readonly<EmptyRecord>`, but named separately so
|
|
39
|
-
* editor hovers remain self-descriptive.
|
|
40
|
-
*/
|
|
41
|
-
type NoPreset = Readonly<EmptyRecord>;
|
|
42
35
|
/**
|
|
43
36
|
* Fallback for `ExtractPluginProps<TPlugin>` when a plugin contributes no
|
|
44
37
|
* props, including the no-plugin case.
|
|
@@ -534,16 +527,6 @@ type StylingOptions<V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPre
|
|
|
534
527
|
type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
|
|
535
528
|
normalize(props: Readonly<Props & IntrinsicProps>): Props & IntrinsicProps;
|
|
536
529
|
}['normalize'];
|
|
537
|
-
/**
|
|
538
|
-
* The type-erased shape of {@link FactoryOptions} — every generic parameter widened to its bound.
|
|
539
|
-
*
|
|
540
|
-
* Use it for a value that must hold *any* factory config (a registry, a generic wrapper). It
|
|
541
|
-
* cannot check `styling.compounds` conditions against the real variant keys/values, because it
|
|
542
|
-
* has forgotten what they are — for that, annotate against `FactoryOptions<...>` with the concrete
|
|
543
|
-
* generics (or `satisfies FactoryOptions<'button', Props, typeof variants>`), which keeps an
|
|
544
|
-
* invalid compound condition a type error rather than a silent no-op.
|
|
545
|
-
*/
|
|
546
|
-
type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, RecipeMap<VariantMap>, AnyClassPluginFactory>;
|
|
547
530
|
/**
|
|
548
531
|
* The framework-neutral component-authoring config passed to `createContractComponent` in every
|
|
549
532
|
* adapter: default tag + name, own-prop defaults, a `normalize` transform, `styling` (variants,
|
|
@@ -553,7 +536,9 @@ type AnyFactoryOptions = FactoryOptions<ElementType, AnyRecord, VariantMap, Reci
|
|
|
553
536
|
* `satisfies FactoryOptions<TDefault, Props, typeof variants, ...>` on a config object narrows
|
|
554
537
|
* `styling.compounds` conditions to the real per-variant-key shape — including resolving a
|
|
555
538
|
* boolean-shaped axis (`{ true, false }`) to a real `boolean` — so a condition naming a variant or
|
|
556
|
-
* value that does not exist is a compile error. `
|
|
539
|
+
* value that does not exist is a compile error. Leaving `V` at the bare `VariantMap` instead
|
|
540
|
+
* (whether via `FactoryOptions`'s own default or an explicit erased instantiation) forgets that
|
|
541
|
+
* shape entirely, so the same invalid condition becomes a silent no-op instead.
|
|
557
542
|
*/
|
|
558
543
|
type FactoryOptions<TDefault extends ElementType = ElementType, Props extends AnyRecord = EmptyRecord, V extends Readonly<VariantMap> = Readonly<EmptyRecord>, TPreset extends RecipeMap<V> = Readonly<EmptyRecord>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> = {
|
|
559
544
|
/** The intrinsic tag the component renders by default. Overridable per instance via `as`. */
|
|
@@ -562,6 +547,33 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
|
|
|
562
547
|
readonly name?: string;
|
|
563
548
|
/** Values used for the component's own (non-variant) props when the consumer omits them. */
|
|
564
549
|
readonly defaults?: Partial<NoInfer<Props>>;
|
|
550
|
+
/**
|
|
551
|
+
* Optional, type-only declaration of this component's complete own-prop shape — present purely
|
|
552
|
+
* for type recovery, never read at runtime (see `declareProps` in `@praxis-kit/adapter-utils`).
|
|
553
|
+
*
|
|
554
|
+
* `defaults` alone can only prove a prop *has a default*, not that it's the complete prop model
|
|
555
|
+
* a component accepts — a `defaults: { size: 'md' }` component may still take `onClick`,
|
|
556
|
+
* `disabled`, and other props with no default at all, none of which a `defaults`-only recovery
|
|
557
|
+
* can see (see `ContractPropsFrom`'s own doc comment for the general shape of this problem).
|
|
558
|
+
* `props` closes that gap: when present, `ContractPropsOf<C>` / `ContractProps<typeof Component>`
|
|
559
|
+
* recover this declared type directly and exactly, in place of the necessarily-partial,
|
|
560
|
+
* literal-widened recovery `defaults` alone allows.
|
|
561
|
+
*
|
|
562
|
+
* Typed as `object | undefined`, not `NoInfer<Props> | undefined` like `defaults`/`onElement` —
|
|
563
|
+
* deliberately decoupled from this interface's own `Props` generic, unlike those two fields.
|
|
564
|
+
* `defaults`/`onElement` are tied to `Props` because real runtime code reads them against a
|
|
565
|
+
* concretely-resolved `Props` for a real call; `props` is never read at runtime at all (see
|
|
566
|
+
* above), so it has no such need, and tying it to `Props extends AnyRecord` would force every
|
|
567
|
+
* hand-declared prop `interface`/`type` an author passes through `declareProps<Props>()` to
|
|
568
|
+
* structurally satisfy `Record<string, unknown>` — a real TypeScript limitation (a named type
|
|
569
|
+
* without an index signature never satisfies that, even via plain assignment, only a fresh
|
|
570
|
+
* object literal does) that would make this field far more awkward to use for its one real job:
|
|
571
|
+
* carrying an author's own already-precise prop type through untouched. `ContractPropsFrom`
|
|
572
|
+
* (`contract-model.ts`) recovers the real value here structurally, straight off `O`'s own
|
|
573
|
+
* literal type — independent of this field's declared type, same as every other `Contract*From`
|
|
574
|
+
* derivation in that file.
|
|
575
|
+
*/
|
|
576
|
+
readonly props?: object | undefined;
|
|
565
577
|
/**
|
|
566
578
|
* A pure `(props) => props` transform run on every render, after `enforcement.props`'s
|
|
567
579
|
* normalizers see the same input. Use this for component-specific prop shaping — anything
|
|
@@ -616,9 +628,159 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
|
|
|
616
628
|
*
|
|
617
629
|
* Return a cleanup function to run when the instance unmounts.
|
|
618
630
|
*/
|
|
619
|
-
readonly onElement?:
|
|
631
|
+
readonly onElement?: {
|
|
632
|
+
onElement(element: ElementForTag<TDefault | TAllowed>, getProps: () => Readonly<Props>): void | (() => void);
|
|
633
|
+
}['onElement'];
|
|
620
634
|
};
|
|
621
635
|
//#endregion
|
|
636
|
+
//#region ../../lib/primitive/src/types/factory/contract-model.d.ts
|
|
637
|
+
/**
|
|
638
|
+
* The pipeline this file implements:
|
|
639
|
+
*
|
|
640
|
+
* ```text
|
|
641
|
+
* O (a raw contract literal)
|
|
642
|
+
* → Contract*From<O> one derivation per dimension, straight off O's own shape
|
|
643
|
+
* → ContractDimensions<O> the six derivations, assembled
|
|
644
|
+
* → ContractModel<...> the same six values, as a required-field carrier
|
|
645
|
+
* → ContractModelOf<C> resolves an already-`defineContract`-ed C's real model,
|
|
646
|
+
* or reconstructs one from a raw C via ContractModelFrom
|
|
647
|
+
* ```
|
|
648
|
+
*
|
|
649
|
+
* See `DECISIONS.md`'s `defineContract` entry for the TypeScript limitations that shaped the
|
|
650
|
+
* derivations below.
|
|
651
|
+
*/
|
|
652
|
+
/**
|
|
653
|
+
* Canonical type-level representation of a contract's dimensions — the contract-level counterpart
|
|
654
|
+
* to `PolymorphicGenerics`. Every field is required, so a field's presence is never ambiguous the
|
|
655
|
+
* way it is on an optional `FactoryOptions` field.
|
|
656
|
+
*/
|
|
657
|
+
interface ContractModel<TDefault extends ElementType = ElementType, Props extends AnyRecord = AnyRecord, V extends Readonly<VariantMap> = Readonly<VariantMap>, TPreset extends RecipeMap<VariantMap> = RecipeMap<VariantMap>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory, TAllowed extends ElementType = ElementType> {
|
|
658
|
+
readonly tag: TDefault;
|
|
659
|
+
readonly props: Props;
|
|
660
|
+
readonly variants: V;
|
|
661
|
+
readonly preset: TPreset;
|
|
662
|
+
readonly plugin: TPlugin;
|
|
663
|
+
readonly allowed: TAllowed;
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* The phantom-marker shape read back via `T extends HasContractModel<infer M> ? M : ...` — the
|
|
667
|
+
* contract-level counterpart to `lib/contract-props`'s `HasGenerics<G>`. Type-only: never assigned
|
|
668
|
+
* at runtime.
|
|
669
|
+
*
|
|
670
|
+
* `__model` is **required**, not optional like `HasGenerics<G>`'s `__generics?` — an optional field
|
|
671
|
+
* would make `C extends HasContractModel<infer M>` trivially succeed for any `C`, defeating the
|
|
672
|
+
* one thing this marker exists to answer: did `C` really go through `defineContract`?
|
|
673
|
+
*/
|
|
674
|
+
interface HasContractModel<M extends ContractModel = ContractModel> {
|
|
675
|
+
readonly __model: M;
|
|
676
|
+
}
|
|
677
|
+
type ContractTagFrom<O> = O extends {
|
|
678
|
+
tag: infer TDefault extends ElementType;
|
|
679
|
+
} ? TDefault : ElementType;
|
|
680
|
+
/** `'img'` → `string`, `1` → `number`, `true` → `boolean` — undoes `defineContract`'s `const O`
|
|
681
|
+
* literal narrowing so a `defaults` value doesn't become the only value a caller may pass. */
|
|
682
|
+
type WidenLiteral<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
|
683
|
+
type WidenShallow<T> = { [K in keyof T]: WidenLiteral<T[K]>; };
|
|
684
|
+
/** What the author explicitly declared, via `props: declareProps<Props>()`
|
|
685
|
+
* (`@praxis-kit/adapter-utils`) — used exactly as declared, no `Partial`/widening/`data-*`
|
|
686
|
+
* stripping (see `DefaultPropsFrom` for why those exist there but not here). */
|
|
687
|
+
type DeclaredPropsFrom<O> = O extends {
|
|
688
|
+
props: infer Props extends object | undefined;
|
|
689
|
+
} ? [NonNullable<Props>] extends [never] ? never : NonNullable<Props> & AnyRecord : never;
|
|
690
|
+
/** What can be inferred from `defaults` — partial at best, since a default only proves a prop
|
|
691
|
+
* *has* one, not that it's the complete prop set. `Partial`: a defaulted prop is optional to the
|
|
692
|
+
* caller by definition. `data-*` keys are dropped — every adapter has its own passthrough for
|
|
693
|
+
* those already (finding #43). */
|
|
694
|
+
type DefaultPropsFrom<O> = O extends {
|
|
695
|
+
defaults: infer Props extends AnyRecord;
|
|
696
|
+
} ? Partial<WidenShallow<Omit<Props, Extract<keyof Props, `data-${string}`>>>> : never;
|
|
697
|
+
/** Declared beats defaulted beats nothing. `[X] extends [never]`, not bare `X extends never` —
|
|
698
|
+
* tuple-wrapped so the check doesn't distribute if `X` is ever a union containing `never`. */
|
|
699
|
+
type ContractPropsFrom<O> = [DeclaredPropsFrom<O>] extends [never] ? [DefaultPropsFrom<O>] extends [never] ? EmptyRecord : DefaultPropsFrom<O> : DeclaredPropsFrom<O>;
|
|
700
|
+
type ContractVariantsFrom<O> = O extends {
|
|
701
|
+
styling: {
|
|
702
|
+
variants: infer V extends Readonly<VariantMap>;
|
|
703
|
+
};
|
|
704
|
+
} ? V : Readonly<EmptyRecord>;
|
|
705
|
+
type ContractPresetFrom<O> = O extends {
|
|
706
|
+
styling: {
|
|
707
|
+
presets: infer TPreset extends RecipeMap<VariantMap>;
|
|
708
|
+
};
|
|
709
|
+
} ? TPreset : Readonly<EmptyRecord>;
|
|
710
|
+
type ContractPluginFrom<O> = O extends {
|
|
711
|
+
styling: {
|
|
712
|
+
plugin: infer TPlugin extends AnyClassPluginFactory;
|
|
713
|
+
};
|
|
714
|
+
} ? TPlugin : AnyClassPluginFactory;
|
|
715
|
+
type ContractAllowedFrom<O> = O extends {
|
|
716
|
+
enforcement: {
|
|
717
|
+
allowedAs: readonly (infer TAllowed extends ElementType)[];
|
|
718
|
+
};
|
|
719
|
+
} ? TAllowed : ElementType;
|
|
720
|
+
/** The six derivations above, assembled — the direct input to `ContractModel`. */
|
|
721
|
+
type ContractDimensions<O> = {
|
|
722
|
+
readonly tag: ContractTagFrom<O>;
|
|
723
|
+
readonly props: ContractPropsFrom<O>;
|
|
724
|
+
readonly variants: ContractVariantsFrom<O>;
|
|
725
|
+
readonly preset: ContractPresetFrom<O>;
|
|
726
|
+
readonly plugin: ContractPluginFrom<O>;
|
|
727
|
+
readonly allowed: ContractAllowedFrom<O>;
|
|
728
|
+
};
|
|
729
|
+
/**
|
|
730
|
+
* Builds a `ContractModel` from a raw literal `O` — the fallback path `ContractModelOf<C>` uses
|
|
731
|
+
* when `C` never went through `defineContract` (no `__model` marker to read directly), and what
|
|
732
|
+
* `defineContract` itself attaches as that marker for a `C` that did.
|
|
733
|
+
*/
|
|
734
|
+
type ContractModelFrom<O> = ContractDimensions<O> extends (infer D extends ContractDimensions<O>) ? ContractModel<D['tag'], D['props'], D['variants'], D['preset'], D['plugin'], D['allowed']> : never;
|
|
735
|
+
/**
|
|
736
|
+
* Resolves "does `C` carry a real `ContractModel` already, or do we need to build one from its raw
|
|
737
|
+
* shape" — every `Contract*Of` accessor (`contract-of.ts`) is a one-line projection off this.
|
|
738
|
+
*/
|
|
739
|
+
type ContractModelOf<C> = C extends HasContractModel<infer M> ? M : ContractModelFrom<C>;
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region ../../lib/primitive/src/types/factory/contract-of.d.ts
|
|
742
|
+
/**
|
|
743
|
+
* `FactoryOptions`-level accessor family, mirroring `polymorphic-generics.ts`'s `*Of<T>`
|
|
744
|
+
* convention (`DefaultOf<G>`, `PropsOf<G>`, etc.) but applied one layer up, to a contract itself
|
|
745
|
+
* rather than to the `PolymorphicGenerics` an adapter derives from it.
|
|
746
|
+
*
|
|
747
|
+
* Named with a `Contract` prefix specifically to avoid colliding with `polymorphic-generics.ts`'s
|
|
748
|
+
* own `PropsOf`/`VariantsOf`/`RecipeOf`/`AllowedOf`/`DefaultOf` — both families are re-exported
|
|
749
|
+
* from `@praxis-kit/core`, so a name clash would be a real conflict, not a style nit.
|
|
750
|
+
*
|
|
751
|
+
* Each accessor is a trivial projection off `ContractModelOf<C>` (`contract-model.ts`) — the one
|
|
752
|
+
* place that resolves "does `C` carry a real `ContractModel` (via `defineContract`) or does one
|
|
753
|
+
* need reconstructing from `C`'s raw shape," so every accessor shares one implementation of that
|
|
754
|
+
* resolution rather than repeating it. See `contract-model.ts`'s own doc comments for the
|
|
755
|
+
* `ContractXFrom<O>` derivation each of these ultimately reads through, and `DECISIONS.md`'s
|
|
756
|
+
* `defineContract` entry for the design history behind the required-pattern-match technique.
|
|
757
|
+
*/
|
|
758
|
+
type ContractTagOf<C extends FactoryOptions> = ContractModelOf<C>['tag'];
|
|
759
|
+
/** See this file's own doc comment. Best-effort when `C` has no `ContractModel` marker — see
|
|
760
|
+
* `ContractPropsFrom`'s own doc comment for why. */
|
|
761
|
+
type ContractPropsOf<C extends FactoryOptions> = ContractModelOf<C>['props'];
|
|
762
|
+
/** See this file's own doc comment. */
|
|
763
|
+
type ContractVariantsOf<C extends FactoryOptions> = ContractModelOf<C>['variants'];
|
|
764
|
+
/** See this file's own doc comment. */
|
|
765
|
+
type ContractPresetOf<C extends FactoryOptions> = ContractModelOf<C>['preset'];
|
|
766
|
+
/** See this file's own doc comment. */
|
|
767
|
+
type ContractPluginOf<C extends FactoryOptions> = ContractModelOf<C>['plugin'];
|
|
768
|
+
//#endregion
|
|
769
|
+
//#region ../../lib/primitive/src/types/factory/contract-generics.d.ts
|
|
770
|
+
/**
|
|
771
|
+
* The canonical projection from a defined contract `C` to the `PolymorphicGenerics` shape every
|
|
772
|
+
* adapter's prop types are built from — the one place "G" gets computed, so it can no longer
|
|
773
|
+
* silently drift per adapter the way today's hand-assembled `PolymorphicGenerics<...>` instantiation
|
|
774
|
+
* at each `createContractComponent` call site can (see `ContractGenericsWithAllowedOf` below for
|
|
775
|
+
* the one confirmed instance of that drift).
|
|
776
|
+
*
|
|
777
|
+
* Folds the class-resolution plugin's own contributed props (`ExtractPluginProps<TPlugin>`) into
|
|
778
|
+
* `props` here, once, rather than leaving each adapter's `ContractProps` to re-derive that merge
|
|
779
|
+
* via its own distributive conditional type on every read — the root cause of the ~22-member
|
|
780
|
+
* layout-union bug PR #95 patched per-adapter (see `DECISIONS.md`'s `defineContract` entry).
|
|
781
|
+
*/
|
|
782
|
+
type ContractGenericsOf<C extends FactoryOptions> = PolymorphicGenerics<ContractTagOf<C>, MergeRecords<ContractPropsOf<C>, ExtractPluginProps<ContractPluginOf<C>>>, ContractVariantsOf<C>, ContractPresetOf<C>>;
|
|
783
|
+
//#endregion
|
|
622
784
|
//#region ../../lib/adapter-utils/src/types/filter-predicate.d.ts
|
|
623
785
|
/**
|
|
624
786
|
* Determines whether a prop should be stripped before forwarding to the
|
|
@@ -634,9 +796,6 @@ type FactoryOptions<TDefault extends ElementType = ElementType, Props extends An
|
|
|
634
796
|
*/
|
|
635
797
|
type FilterPredicate = (key: string, variantKeys: ReadonlySet<string>) => boolean;
|
|
636
798
|
//#endregion
|
|
637
|
-
//#region ../../lib/adapter-utils/src/runtime/define-component.d.ts
|
|
638
|
-
export declare function defineContractComponent<O extends FactoryOptions>(options: O): <R>(factory: (options: O) => R) => R;
|
|
639
|
-
//#endregion
|
|
640
799
|
//#region ../../adapters/lit/src/types/lit-options.d.ts
|
|
641
800
|
/**
|
|
642
801
|
* Options accepted by createContractComponent in the Lit adapter.
|
|
@@ -648,14 +807,17 @@ export declare function defineContractComponent<O extends FactoryOptions>(option
|
|
|
648
807
|
*
|
|
649
808
|
* Note: this adapter targets Light DOM composition only. Shadow DOM slot
|
|
650
809
|
* protocol is intentionally out of scope.
|
|
810
|
+
*
|
|
811
|
+
* Every generic parameter has a default (widened to that parameter's own *bound*, matching
|
|
812
|
+
* a wide-bound, type-erased philosophy, not `FactoryOptions`'s own narrower `EmptyRecord`-style
|
|
813
|
+
* defaults) so `LitFactoryOptions` can be used bare, as `createContractComponent`'s single
|
|
814
|
+
* `C extends LitFactoryOptions` constraint — see `ReactFactoryOptions`'s identical fix for the
|
|
815
|
+
* same reason.
|
|
651
816
|
*/
|
|
652
|
-
type LitFactoryOptions<TDefault extends ElementType = ElementType, TProps extends AnyRecord =
|
|
817
|
+
type LitFactoryOptions<TDefault extends ElementType = ElementType, TProps extends AnyRecord = AnyRecord, TVariants extends Readonly<VariantMap> = Readonly<VariantMap>, TPreset extends RecipeMap<TVariants> = RecipeMap<TVariants>, TPlugin extends AnyClassPluginFactory = AnyClassPluginFactory> = FactoryOptions<TDefault, TProps, TVariants, TPreset, TPlugin> & {
|
|
653
818
|
readonly filterProps?: FilterPredicate;
|
|
654
819
|
};
|
|
655
820
|
//#endregion
|
|
656
|
-
//#region ../../adapters/lit/src/types/generics.d.ts
|
|
657
|
-
type RuntimeG<TDefault extends ElementType, Props extends AnyRecord, Variants extends Readonly<VariantMap>, TPreset extends RecipeMap<Variants>> = PolymorphicGenerics<TDefault, Props, Variants, TPreset>;
|
|
658
|
-
//#endregion
|
|
659
821
|
//#region ../../adapters/lit/src/types/primitives.d.ts
|
|
660
822
|
type UnknownProps = AnyRecord;
|
|
661
823
|
/**
|
|
@@ -668,8 +830,12 @@ type UnknownProps = AnyRecord;
|
|
|
668
830
|
* `G` is a phantom marker only — see `__generics` below — and defaults to the
|
|
669
831
|
* widest `PolymorphicGenerics` so existing two-argument usages of this type
|
|
670
832
|
* (every call site inside this adapter) keep resolving exactly as before.
|
|
833
|
+
*
|
|
834
|
+
* `C` is a second phantom marker — see `__contract` below — added alongside `G`, not replacing
|
|
835
|
+
* it (Phase 3 of the `defineContract` refactor), defaulting to the widest `FactoryOptions` for the
|
|
836
|
+
* same reason.
|
|
671
837
|
*/
|
|
672
|
-
type LitContractComponent<TVariants extends Readonly<VariantMap> = NoVariants, TPluginProps extends AnyRecord = EmptyRecord, G extends PolymorphicGenerics = PolymorphicGenerics> = {
|
|
838
|
+
type LitContractComponent<TVariants extends Readonly<VariantMap> = NoVariants, TPluginProps extends AnyRecord = EmptyRecord, G extends PolymorphicGenerics = PolymorphicGenerics, C extends FactoryOptions = FactoryOptions> = {
|
|
673
839
|
new (): MergeRecords<LitElement & {
|
|
674
840
|
recipe: string | undefined;
|
|
675
841
|
praxisClass: string | undefined;
|
|
@@ -689,45 +855,70 @@ type LitContractComponent<TVariants extends Readonly<VariantMap> = NoVariants, T
|
|
|
689
855
|
* set, but kept consistent with the established shape regardless.
|
|
690
856
|
*/
|
|
691
857
|
readonly __generics?: G;
|
|
858
|
+
/**
|
|
859
|
+
* Type-only; never assigned at runtime — same rationale as `__generics` above and
|
|
860
|
+
* React's/Preact's `__contract` (see `HasContract<C>`, `@praxis-kit/contract-props`). Carries
|
|
861
|
+
* the *complete* contract this component was built from (`C`, the argument
|
|
862
|
+
* `createContractComponent<C extends LitFactoryOptions>` was actually called with), not just
|
|
863
|
+
* its `PolymorphicGenerics` projection. `GenericsOf`/`ContractProps` (./contract-props) recover
|
|
864
|
+
* `G` *through* this field now (`ContractGenericsOf<C>`), rather than from a separately-computed
|
|
865
|
+
* `G` that never carried plugin-contributed props — see that file's doc comment for the bug this
|
|
866
|
+
* closes.
|
|
867
|
+
*/
|
|
868
|
+
readonly __contract?: C;
|
|
692
869
|
};
|
|
693
870
|
//#endregion
|
|
694
|
-
//#region ../../lib/contract-props/src/has-
|
|
871
|
+
//#region ../../lib/contract-props/src/has-contract.d.ts
|
|
695
872
|
/**
|
|
696
|
-
* The phantom-marker shape read back via `T extends
|
|
697
|
-
*
|
|
873
|
+
* The phantom-marker shape read back via `T extends HasContract<infer C> ? C : never` — the
|
|
874
|
+
* contract-retention counterpart to `HasGenerics<G>` in this same package. Where `HasGenerics<G>`
|
|
875
|
+
* lets a built component recover the `PolymorphicGenerics` an adapter derived for it,
|
|
876
|
+
* `HasContract<C>` lets it recover the *complete, authoritative contract* (`C`, the argument
|
|
877
|
+
* `createContractComponent<C extends XFactoryOptions>` was actually called with) it was derived
|
|
878
|
+
* from — the two are deliberately separate markers, not one broadened to do both jobs: `G` is
|
|
879
|
+
* "what the adapter needs to implement the component," `C` is "what the component was configured
|
|
880
|
+
* with" (see `DECISIONS.md`'s `defineContract` entry). A component carries both.
|
|
698
881
|
*
|
|
699
|
-
* Type-only: never assigned at runtime
|
|
700
|
-
*
|
|
701
|
-
*
|
|
702
|
-
* assignable — confirmed directly against that adapter's own real component type (see
|
|
703
|
-
* `adapters/react/src/shared/types/polymorphic-props.test.ts`), not reproducible in an isolated
|
|
704
|
-
* minimal mock (see this file's own `has-generics.test.ts`), so treat "inline, not intersected"
|
|
705
|
-
* as an adapter-level implementation detail this package's marker shape must stay compatible
|
|
706
|
-
* with, not a property `HasGenerics<G>` itself enforces.
|
|
882
|
+
* Type-only: never assigned at runtime, same rationale as `HasGenerics<G>` (see that type's own
|
|
883
|
+
* doc comment) — a `createContractComponent` return value gets this shape via a type assertion,
|
|
884
|
+
* not a real property write.
|
|
707
885
|
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
710
|
-
*
|
|
886
|
+
* Unconstrained (no `C extends FactoryOptions` bound), matching `HasGenerics<G>`'s own choice —
|
|
887
|
+
* this package has no dependency on `@praxis-kit/core`/`@praxis-kit/primitive`, and adding one
|
|
888
|
+
* just to write a bound here isn't worth it: the accessor types that actually consume `C`
|
|
889
|
+
* (`ContractTagOf<C>`, etc., in `@praxis-kit/primitive`) already declare their own constraint.
|
|
890
|
+
*
|
|
891
|
+
* Do **not** "harden" this with a `unique symbol` or other nominal brand, for the same reason
|
|
892
|
+
* `HasGenerics<G>` doesn't: a real component's callable type needs to structurally satisfy this
|
|
893
|
+
* shape by declaring the same inline optional field, not by importing a nominal brand.
|
|
711
894
|
*/
|
|
712
|
-
interface
|
|
713
|
-
readonly
|
|
895
|
+
interface HasContract<C> {
|
|
896
|
+
readonly __contract?: C;
|
|
714
897
|
}
|
|
715
898
|
//#endregion
|
|
716
899
|
//#region ../../adapters/lit/src/types/contract-props.d.ts
|
|
717
900
|
/**
|
|
718
901
|
* Recovers a `LitContractComponent`'s `PolymorphicGenerics` descriptor from its own value type —
|
|
719
|
-
* the Lit analog of React's/Preact's `
|
|
720
|
-
* Needs
|
|
902
|
+
* the Lit analog of React's/Preact's `ContractGenericsOf`-via-`__contract` recovery.
|
|
903
|
+
* Needs a marker (unlike Svelte's `GenericsOf<T>`,
|
|
721
904
|
* `adapters/svelte/src/types/resolved-slot-props.ts`) because `createContractComponent` here
|
|
722
|
-
* returns `LitContractComponent<TVariants, TPluginProps, G>`, not `BuiltRuntime<G, TOptions>`
|
|
905
|
+
* returns `LitContractComponent<TVariants, TPluginProps, G, C>`, not `BuiltRuntime<G, TOptions>`
|
|
723
906
|
* directly — `TDefault`/`Props`/`TPreset` are genuinely erased from the return type, not merely
|
|
724
907
|
* hidden, so there is no ordinary type parameter left to `infer` them back out of.
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
*
|
|
728
|
-
*
|
|
908
|
+
*
|
|
909
|
+
* Re-pointed at `__contract` (Phase 4 of the `defineContract` refactor) rather than the
|
|
910
|
+
* separately-computed `__generics` field it used to read: `__generics`'s own `G` was assembled
|
|
911
|
+
* from `TDefault`/`Props`/`TVariants`/`TPreset` alone, with **no plugin-contributed props folded
|
|
912
|
+
* in** — a real, previously-open gap (`findings.md` #44 — Lit/Web's `ContractProps` silently
|
|
913
|
+
* missing plugin props entirely, not just unionized like React/Preact's old bug). `ContractGenericsOf<C>`
|
|
914
|
+
* folds `ExtractPluginProps<TPlugin>` into `props` once, at the canonical projection point, so this
|
|
915
|
+
* fix comes for free from routing through it — no separate merge needed here. `__generics` itself
|
|
916
|
+
* is untouched (still assembled, still readable) — this file just no longer reads it.
|
|
917
|
+
*
|
|
918
|
+
* Falls back to the widest `PolymorphicGenerics` for any non-praxis-kit value, the same "no
|
|
919
|
+
* marker, nothing to recover" case `HasContract<C>`'s own `never` branch covers for React/Preact.
|
|
729
920
|
*/
|
|
730
|
-
type GenericsOf<T extends
|
|
921
|
+
type GenericsOf<T extends HasContract<FactoryOptions>> = T extends HasContract<infer C extends FactoryOptions> ? ContractGenericsOf<C> extends (infer G extends PolymorphicGenerics) ? G : PolymorphicGenerics : PolymorphicGenerics;
|
|
731
922
|
/**
|
|
732
923
|
* A component's full prop contract — the attributes a caller can set on the custom element,
|
|
733
924
|
* recovered from outside the file that built it. Lit has exactly one render mode (no
|
|
@@ -742,16 +933,32 @@ type GenericsOf<T extends HasGenerics<PolymorphicGenerics>> = T extends HasGener
|
|
|
742
933
|
* makes that a type-level fact too, so `{ as: 'a' }` written against `ContractProps<T>` is a compile
|
|
743
934
|
* error, not a silently-ignored no-op a caller could believe was doing something.
|
|
744
935
|
*
|
|
936
|
+
* `data-*` attributes pass through (`DataAttributes` below). `_buildProps()` in
|
|
937
|
+
* `createContractComponent` scans every attribute set on the custom element into the pipeline, so
|
|
938
|
+
* `<praxis-button data-slot="…">` is real, forwarded input — a `data-*` a contract sets in
|
|
939
|
+
* `defaults` (`data-slot`, the near-universal styling hook) has to be a member of `ContractProps`
|
|
940
|
+
* for a consumer to type it. Other global/host attributes (`id`, `class`, `slot`, `aria-*`,
|
|
941
|
+
* `role`) are equally accepted on the element but are *not* part of this type: they are DOM
|
|
942
|
+
* globals a caller sets on the element directly, not part of a component's declared prop contract.
|
|
943
|
+
* `data-*` is the exception because contracts author it as an overridable prop.
|
|
944
|
+
*
|
|
745
945
|
* ```ts
|
|
746
946
|
* const Button = createContractComponent({ tag: 'button', name: 'Button', /* ... *\/ })
|
|
747
947
|
*
|
|
748
948
|
* type ButtonProps = ContractProps<typeof Button>
|
|
749
949
|
* ```
|
|
750
950
|
*/
|
|
751
|
-
type ContractProps<T extends
|
|
951
|
+
type ContractProps<T extends HasContract<FactoryOptions>> = Simplify<OmitIndexSignature<PropsOf<GenericsOf<T>>> & OmitIndexSignature<VariantProps<VariantsOf<GenericsOf<T>>>> & DataAttributes & {
|
|
752
952
|
as?: never;
|
|
753
953
|
recipe?: keyof RecipeOf<GenericsOf<T>>;
|
|
754
954
|
}>;
|
|
955
|
+
/**
|
|
956
|
+
* `data-*` attribute passthrough. The value type is `string | number | boolean | undefined` —
|
|
957
|
+
* what a `data-*` attribute serializes to (mirrors the React adapter's own `DataAttributes`).
|
|
958
|
+
*/
|
|
959
|
+
type DataAttributes = {
|
|
960
|
+
[key: `data-${string}`]: string | number | boolean | undefined;
|
|
961
|
+
};
|
|
755
962
|
//#endregion
|
|
756
963
|
//#region ../../adapters/lit/src/create-contract-component.d.ts
|
|
757
964
|
/**
|
|
@@ -823,10 +1030,15 @@ type ContractProps<T extends HasGenerics<PolymorphicGenerics>> = Simplify<OmitIn
|
|
|
823
1030
|
* tag polymorphism, but SSR quietly provided a fake, DOM-inconsistent form of it until now. Need
|
|
824
1031
|
* different semantics for one instance? Register a second component with a different `tag`, or
|
|
825
1032
|
* set `role` directly — both already work today, unaffected by this.
|
|
1033
|
+
*
|
|
1034
|
+
* `TDefault`/`TProps`/`TVariants`/`TPreset`/`TPlugin` are each `ContractXOf<C>`-derived *defaults*
|
|
1035
|
+
* on this function's own type parameter list, mirroring every other adapter's identical fix — see
|
|
1036
|
+
* `@praxis-kit/react`'s own doc comment for why (computed as function type-parameter defaults, not
|
|
1037
|
+
* inline body computations, which doesn't resolve for a still-abstract `C`).
|
|
826
1038
|
*/
|
|
827
|
-
export declare function createContractComponent<TDefault extends ElementType
|
|
1039
|
+
export declare function createContractComponent<C extends LitFactoryOptions, TDefault extends ElementType = ContractTagOf<C>, TProps extends UnknownProps = ContractPropsOf<C>, TVariants extends Readonly<VariantMap> = ContractVariantsOf<C>, TPreset extends RecipeMap<VariantMap> = ContractPresetOf<C>, TPlugin extends AnyClassPluginFactory = ContractPluginOf<C>, TSubComponents extends Readonly<AnyRecord> = EmptyRecord>(options: C & {
|
|
828
1040
|
readonly subComponents?: TSubComponents;
|
|
829
|
-
}): MergeRecords<LitContractComponent<TVariants, ExtractPluginProps<TPlugin>,
|
|
1041
|
+
}): MergeRecords<LitContractComponent<TVariants, ExtractPluginProps<TPlugin>, ContractGenericsOf<C>, C>, TSubComponents>;
|
|
830
1042
|
//#endregion
|
|
831
1043
|
//#region ../../adapters/lit/src/render-to-string.d.ts
|
|
832
1044
|
/**
|
|
@@ -859,4 +1071,4 @@ export declare function createContractComponent<TDefault extends ElementType, TP
|
|
|
859
1071
|
*/
|
|
860
1072
|
export declare function renderContractToString(component: LitContractComponent, props?: UnknownProps, innerHTML?: string): string;
|
|
861
1073
|
//#endregion
|
|
862
|
-
export type {
|
|
1074
|
+
export type { ContractProps, ElementType, EmptyRecord, FactoryOptions, GenericsOf, LitContractComponent, LitFactoryOptions, PolymorphicGenerics };
|