praxis-kit 0.1.0
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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/_shared/diagnostics.d.ts +312 -0
- package/dist/_shared/diagnostics.js +360 -0
- package/dist/build-runtime-CJ_nQEaZ.js +5065 -0
- package/dist/codemod/index.d.ts +2 -0
- package/dist/codemod/index.js +176520 -0
- package/dist/contract/index.d.ts +677 -0
- package/dist/contract/index.js +341 -0
- package/dist/eslint/index.d.ts +90 -0
- package/dist/eslint/index.js +1047 -0
- package/dist/guards/index.d.ts +78 -0
- package/dist/guards/index.js +118 -0
- package/dist/html/index.d.ts +151 -0
- package/dist/html/index.js +1244 -0
- package/dist/index-BIBd_iPD.d.ts +951 -0
- package/dist/lit/index.d.ts +862 -0
- package/dist/lit/index.js +4893 -0
- package/dist/preact/index.d.ts +796 -0
- package/dist/preact/index.js +5043 -0
- package/dist/react/index.d.ts +28 -0
- package/dist/react/index.js +205 -0
- package/dist/react/legacy.d.ts +29 -0
- package/dist/react/legacy.js +80 -0
- package/dist/solid/index.d.ts +728 -0
- package/dist/solid/index.js +4821 -0
- package/dist/svelte/Polymorphic.svelte +190 -0
- package/dist/svelte/_polymorphic-runtime.d.ts +102 -0
- package/dist/svelte/_polymorphic-runtime.js +371 -0
- package/dist/svelte/index.d.ts +994 -0
- package/dist/svelte/index.js +4482 -0
- package/dist/tailwind/index.d.ts +197 -0
- package/dist/tailwind/index.js +767 -0
- package/dist/tailwind/safelist.css +20 -0
- package/dist/ts-plugin/index.cjs +166 -0
- package/dist/ts-plugin/index.d.cts +9 -0
- package/dist/utils/index.d.ts +19 -0
- package/dist/utils/index.js +21 -0
- package/dist/vite-plugin/index.d.ts +200 -0
- package/dist/vite-plugin/index.js +2106 -0
- package/dist/vue/index.d.ts +729 -0
- package/dist/vue/index.js +4945 -0
- package/dist/web/index.d.ts +832 -0
- package/dist/web/index.js +4868 -0
- package/package.json +258 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component
|
|
3
|
+
Renders a `createContractComponent` bundle. Every praxis-kit component in the Svelte adapter
|
|
4
|
+
is a bundle passed to this component via the `bundle` prop:
|
|
5
|
+
|
|
6
|
+
```svelte
|
|
7
|
+
<Polymorphic {bundle} intent="ghost" as="a" href="/home">Home</Polymorphic>
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Resolves the tag (`as` or the bundle's default), variant classes, filtered props, and ARIA
|
|
11
|
+
attributes, then renders the result via `<svelte:element>` — or, with `asChild`, renders
|
|
12
|
+
`children` as a snippet receiving the resolved props instead of a host element.
|
|
13
|
+
-->
|
|
14
|
+
<script module lang="ts">
|
|
15
|
+
declare const process: { env: { NODE_ENV: string } }
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<script lang="ts">
|
|
19
|
+
import { enforceAllowedAs, isKnownAriaRole } from './_polymorphic-runtime.js'
|
|
20
|
+
import type { ElementType, IntrinsicProps } from './_polymorphic-runtime.js'
|
|
21
|
+
import { isObject, isString } from './_polymorphic-runtime.js'
|
|
22
|
+
import { applyFilter, resolveNormalizedProps } from './_polymorphic-runtime.js'
|
|
23
|
+
import type { Snippet } from 'svelte'
|
|
24
|
+
import type {
|
|
25
|
+
PolymorphicComponentProps,
|
|
26
|
+
ResolvedAttributes,
|
|
27
|
+
StyleObject,
|
|
28
|
+
UnknownProps,
|
|
29
|
+
} from './index.js'
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
bundle,
|
|
33
|
+
as: asProp,
|
|
34
|
+
asChild,
|
|
35
|
+
class: cls,
|
|
36
|
+
recipe,
|
|
37
|
+
children,
|
|
38
|
+
...rest
|
|
39
|
+
}: PolymorphicComponentProps = $props()
|
|
40
|
+
let hostEl: Element | undefined = $state()
|
|
41
|
+
|
|
42
|
+
// Svelte 5 event delegation requires lowercase handler names (onclick, onfocus…). This is a
|
|
43
|
+
// deliberate, documented compatibility feature — not an accident of the regex — so a bundle's
|
|
44
|
+
// caller can write React-style camelCase (onClick, onPointerDown) and it reaches
|
|
45
|
+
// <svelte:element> as the native lowercase name Svelte's runtime binds via addEventListener.
|
|
46
|
+
//
|
|
47
|
+
// The contract only covers camelCase→lowercase for keys already shaped like `onXxx`; every
|
|
48
|
+
// "on"-prefixed key, normalized or not, is still treated as event-related by Svelte's own
|
|
49
|
+
// attribute-spreading runtime (a non-function value is silently dropped, not rendered as a
|
|
50
|
+
// literal attribute) — including a component-level "callback prop" that happens to start with
|
|
51
|
+
// "on" (`onValueChange`), which becomes a dead listener rather than a plain callback. See
|
|
52
|
+
// `DECISIONS.md` → "`adapters/svelte` — the event-normalization contract" and
|
|
53
|
+
// `event-normalization.test.ts` for the exact pinned cases.
|
|
54
|
+
const EVENT_RE = /^on[A-Z]/
|
|
55
|
+
function normalizeEventKeys(props: UnknownProps): UnknownProps {
|
|
56
|
+
const out: ResolvedAttributes = {}
|
|
57
|
+
for (const k in props) {
|
|
58
|
+
out[EVENT_RE.test(k) ? k.toLowerCase() : k] = (props as ResolvedAttributes)[k]
|
|
59
|
+
}
|
|
60
|
+
return out as UnknownProps
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// key.replace(/([A-Z])/g, '-$1') camelCase→kebab-case is also what makes a CSS custom property
|
|
64
|
+
// (`'--my-color'`) pass through untouched — it has no uppercase letters to rewrite — and what
|
|
65
|
+
// makes a vendor-prefixed key (`WebkitLineClamp`) come out correctly hyphenated
|
|
66
|
+
// (`-webkit-line-clamp`, a leading dash and all — that IS the real CSS property name). Both are
|
|
67
|
+
// asserted by test, not left as an implicit property of the regex. `value == null` (not
|
|
68
|
+
// `=== undefined`) skips both `null` and `undefined` values but keeps a falsy `0`.
|
|
69
|
+
function serializeStyle(style: StyleObject): string {
|
|
70
|
+
let result = ''
|
|
71
|
+
for (const key in style) {
|
|
72
|
+
const value = style[key]
|
|
73
|
+
if (value == null) continue
|
|
74
|
+
if (result) result += ';'
|
|
75
|
+
result += `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}:${value}`
|
|
76
|
+
}
|
|
77
|
+
return result
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function buildDomProps(
|
|
81
|
+
props: UnknownProps,
|
|
82
|
+
classStr: string | undefined,
|
|
83
|
+
tag: ElementType,
|
|
84
|
+
normalizedProps: UnknownProps,
|
|
85
|
+
): ResolvedAttributes {
|
|
86
|
+
const { role, style, ...r } = normalizeEventKeys(props)
|
|
87
|
+
const styleStr = isObject(style, true)
|
|
88
|
+
? serializeStyle(style as StyleObject)
|
|
89
|
+
: (style as string | undefined)
|
|
90
|
+
const ep: IntrinsicProps = {
|
|
91
|
+
...(r as IntrinsicProps),
|
|
92
|
+
class: classStr,
|
|
93
|
+
...(styleStr !== undefined && { style: styleStr }),
|
|
94
|
+
}
|
|
95
|
+
if (isKnownAriaRole(role)) ep.role = role
|
|
96
|
+
if (!isString(tag)) return ep as ResolvedAttributes
|
|
97
|
+
return bundle.runtime.resolveAria(tag, ep, normalizedProps as IntrinsicProps)
|
|
98
|
+
.props as ResolvedAttributes
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Unlike buildDomProps above, this intentionally skips normalizeEventKeys and style
|
|
102
|
+
// serialization — the asChild path hands props straight to a caller-authored snippet, which
|
|
103
|
+
// spreads them onto its own element via ordinary JSX/attribute semantics, not through
|
|
104
|
+
// <svelte:element>'s own attribute application; event-handler keys keep their caller-authored
|
|
105
|
+
// casing (`onClick`, not lowercased to `onclick`), and `style` passes through however the
|
|
106
|
+
// caller wrote it (object or string), unserialized. See `ResolvedSlotProps<G>`
|
|
107
|
+
// (types/resolved-slot-props.ts) for the type this produces — it deliberately omits `style`
|
|
108
|
+
// rather than asserting either shape, for this exact reason.
|
|
109
|
+
function buildSlotProps(props: UnknownProps, classStr: string | undefined): UnknownProps {
|
|
110
|
+
const { role, ...r } = props
|
|
111
|
+
return {
|
|
112
|
+
...r,
|
|
113
|
+
class: classStr,
|
|
114
|
+
...(isKnownAriaRole(role) && { role }),
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const tag = $derived(bundle.runtime.resolveTag(asProp as ElementType | undefined))
|
|
119
|
+
const mergedProps = $derived(bundle.runtime.resolveProps(rest as UnknownProps))
|
|
120
|
+
const normalizedProps = $derived.by(() => {
|
|
121
|
+
const { runtime: { options } } = bundle
|
|
122
|
+
// Folded into this derived (rather than a standalone one) so it's guaranteed to run on
|
|
123
|
+
// both SSR and DOM — $derived is lazy in Svelte 5 and only evaluates when read; this one
|
|
124
|
+
// is read downstream by resolvedClass/filteredProps/domProps, which the template renders.
|
|
125
|
+
if (options.allowedAs !== undefined) {
|
|
126
|
+
enforceAllowedAs(
|
|
127
|
+
tag,
|
|
128
|
+
options.allowedAs,
|
|
129
|
+
options.diagnostics,
|
|
130
|
+
options.displayName,
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
return resolveNormalizedProps(options, tag, mergedProps)
|
|
134
|
+
})
|
|
135
|
+
const resolvedClass = $derived(
|
|
136
|
+
bundle.runtime.resolveClasses(tag, normalizedProps, cls as string | undefined, recipe),
|
|
137
|
+
)
|
|
138
|
+
const filteredProps = $derived(
|
|
139
|
+
applyFilter(normalizedProps, bundle.filterProps, bundle.runtime.options.variantKeys),
|
|
140
|
+
)
|
|
141
|
+
const domProps = $derived(buildDomProps(filteredProps, resolvedClass, tag, normalizedProps))
|
|
142
|
+
|
|
143
|
+
// Resolves whether to render as child slot; also enforces as+asChild mutual exclusion.
|
|
144
|
+
const useAsChild = $derived.by(() => {
|
|
145
|
+
if (!asChild) return false
|
|
146
|
+
if (asProp !== undefined) {
|
|
147
|
+
bundle.slotValidator.assertExclusive()
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
return true
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
// DOM-only (like Lit skipping htmlChildrenEvaluatorFn for its SSR renderToString). Merely
|
|
154
|
+
// *registering* $effect during svelte/server's render() throws ("effect_orphan") — it's not
|
|
155
|
+
// enough to no-op inside the callback, the rune call itself must never happen during SSR.
|
|
156
|
+
// Reads real child nodes off the mounted host, same approach as Lit's Array.from(this.childNodes).
|
|
157
|
+
// Not reachable in the asChild branch — no host element there.
|
|
158
|
+
if (typeof document !== 'undefined') {
|
|
159
|
+
$effect(() => {
|
|
160
|
+
if (process.env.NODE_ENV === 'production' || !hostEl) return
|
|
161
|
+
const childArray = Array.from(hostEl.childNodes)
|
|
162
|
+
bundle.childrenEvaluator?.evaluate(childArray, { tag, props: normalizedProps })
|
|
163
|
+
bundle.runtime.options.htmlChildrenEvaluatorFn
|
|
164
|
+
?.(tag)
|
|
165
|
+
?.evaluate(childArray, { tag, props: normalizedProps })
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
const onElement = $derived(bundle.onElement)
|
|
169
|
+
|
|
170
|
+
// Unlike the effect above, this runs in every environment (not dev-only) — onElement is a
|
|
171
|
+
// real runtime feature, not a diagnostic. Re-runs (cleaning up the previous call first, via
|
|
172
|
+
// the returned teardown) whenever hostEl changes identity, e.g. the resolved tag changes and
|
|
173
|
+
// Svelte mounts a new host element.
|
|
174
|
+
$effect(() => {
|
|
175
|
+
if (!hostEl) return
|
|
176
|
+
const cleanup = onElement?.(hostEl, () => normalizedProps)
|
|
177
|
+
return () => cleanup?.()
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
</script>
|
|
181
|
+
|
|
182
|
+
{#if useAsChild}
|
|
183
|
+
{#if children}
|
|
184
|
+
{@render (children as Snippet<[UnknownProps]>)(buildSlotProps(filteredProps, resolvedClass))}
|
|
185
|
+
{/if}
|
|
186
|
+
{:else}
|
|
187
|
+
<svelte:element this={tag as string} bind:this={hostEl} {...domProps}>
|
|
188
|
+
{@render (children as Snippet | undefined)?.()}
|
|
189
|
+
</svelte:element>
|
|
190
|
+
{/if}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import "clsx";
|
|
2
|
+
import { DiagnosticCode, Diagnostics } from "../_shared/diagnostics.js";
|
|
3
|
+
import "type-fest";
|
|
4
|
+
//#region ../../lib/foundation/src/string-map.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* A string-keyed object whose values are of type `T`.
|
|
7
|
+
*/
|
|
8
|
+
type StringMap<T = unknown> = Record<string, T>;
|
|
9
|
+
/**
|
|
10
|
+
* A string-keyed object with values of unknown type.
|
|
11
|
+
*/
|
|
12
|
+
type AnyRecord = StringMap<unknown>;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region ../../lib/foundation/src/type-guards.d.ts
|
|
15
|
+
export declare function isString(value: unknown): value is string;
|
|
16
|
+
export declare function isObject(value: unknown, excludeArrays: true): value is AnyRecord;
|
|
17
|
+
export declare function isObject(value: unknown, excludeArrays?: false): value is object;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region ../../lib/primitive/src/types/intrinsic-tag.d.ts
|
|
20
|
+
type IntrinsicTag = keyof HTMLElementTagNameMap;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region ../../lib/primitive/src/types/element-type.d.ts
|
|
23
|
+
type ElementType = IntrinsicTag | (string & {});
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region ../../lib/primitive/src/constants/aria/known-aria-roles.d.ts
|
|
26
|
+
declare const KNOWN_ARIA_ROLES: readonly ["alert", "alertdialog", "application", "article", "banner", "blockquote", "button", "caption", "cell", "checkbox", "code", "columnheader", "combobox", "complementary", "contentinfo", "definition", "deletion", "dialog", "document", "emphasis", "feed", "figure", "form", "generic", "grid", "gridcell", "group", "heading", "img", "insertion", "link", "list", "listbox", "listitem", "log", "main", "marquee", "math", "menu", "menubar", "menuitem", "menuitemcheckbox", "menuitemradio", "meter", "navigation", "none", "note", "option", "paragraph", "presentation", "progressbar", "radio", "radiogroup", "region", "row", "rowgroup", "rowheader", "scrollbar", "search", "searchbox", "separator", "slider", "spinbutton", "status", "strong", "subscript", "superscript", "switch", "tab", "table", "tablist", "tabpanel", "term", "textbox", "time", "timer", "toolbar", "tooltip", "tree", "treegrid", "treeitem"];
|
|
27
|
+
type KnownAriaRole = (typeof KNOWN_ARIA_ROLES)[number];
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region ../../lib/primitive/src/types/primitives/index.d.ts
|
|
30
|
+
type AriaRole = KnownAriaRole | (string & {});
|
|
31
|
+
type IntrinsicProps = AnyRecord & {
|
|
32
|
+
role?: AriaRole;
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region ../../lib/primitive/src/types/factory/prop-normalizer.d.ts
|
|
36
|
+
type PropNormalizer = (props: Readonly<AnyRecord & IntrinsicProps>) => Partial<AnyRecord & IntrinsicProps>;
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region ../../lib/primitive/src/types/factory/factory-options.d.ts
|
|
39
|
+
type NormalizeFn<Props extends AnyRecord = AnyRecord> = {
|
|
40
|
+
normalize(props: Readonly<Props & IntrinsicProps>): Props & IntrinsicProps;
|
|
41
|
+
}['normalize'];
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region ../../lib/primitive/src/guards/aria/is-known-aria-role.d.ts
|
|
44
|
+
export declare function isKnownAriaRole(value: unknown): value is KnownAriaRole;
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region ../core/src/resolver/resolver.d.ts
|
|
47
|
+
export declare function enforceAllowedAs(tag: ElementType, allowedAs: readonly ElementType[], diagnostics: Diagnostics | undefined, displayName?: string): void;
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region ../../lib/adapter-utils/src/types/filter-predicate.d.ts
|
|
50
|
+
/**
|
|
51
|
+
* Determines whether a prop should be stripped before forwarding to the
|
|
52
|
+
* rendered element.
|
|
53
|
+
*
|
|
54
|
+
* Returning `true` excludes the prop from the output; returning `false`
|
|
55
|
+
* keeps it. This is the inverse polarity of `shouldForwardProp`-style
|
|
56
|
+
* predicates (Emotion/styled-components), where `true` means include.
|
|
57
|
+
*
|
|
58
|
+
* @param key - The prop name being evaluated.
|
|
59
|
+
* @param variantKeys - The set of configured variant prop names.
|
|
60
|
+
* @returns `true` to strip the prop; `false` to forward it.
|
|
61
|
+
*/
|
|
62
|
+
type FilterPredicate = (key: string, variantKeys: ReadonlySet<string>) => boolean;
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region ../../lib/adapter-utils/src/props/apply-filter.d.ts
|
|
65
|
+
export declare function applyFilter(props: Readonly<AnyRecord>, filterProps: FilterPredicate, variantKeys: ReadonlySet<string>): AnyRecord;
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region ../../lib/adapter-utils/src/props/resolve-normalized-props.d.ts
|
|
68
|
+
/**
|
|
69
|
+
* The subset of the resolved runtime options that {@link resolveNormalizedProps}
|
|
70
|
+
* reads. Kept structural so every adapter's own `runtime.options` type satisfies
|
|
71
|
+
* it without a shared nominal import.
|
|
72
|
+
*/
|
|
73
|
+
interface NormalizeCapableOptions {
|
|
74
|
+
readonly htmlPropNormalizersFn?: ((tag: ElementType) => readonly PropNormalizer[] | undefined) | undefined;
|
|
75
|
+
readonly normalizeFn?: NormalizeFn | undefined;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The single canonical prop-normalization step for every render path — the five
|
|
79
|
+
* VDOM adapters (React, Preact, Vue, Solid, Svelte) and the host-state path
|
|
80
|
+
* shared by Lit, Web, and SSR.
|
|
81
|
+
*
|
|
82
|
+
* Ordering is fixed and load-bearing:
|
|
83
|
+
*
|
|
84
|
+
* ```text
|
|
85
|
+
* mergedProps → HTML built-in normalizers → normalizeFn
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* `normalizeFn` is the primitive's composed `enforcement.props` normalizers plus
|
|
89
|
+
* the caller's `normalize` option (folded together by `composeNormalizers`).
|
|
90
|
+
* Running it last means a caller's `normalize` always observes — and can
|
|
91
|
+
* override — an HTML built-in's output for the same key. This must be identical
|
|
92
|
+
* across adapters: the same component with the same props has to normalize the
|
|
93
|
+
* same way whether it renders through React or through SSR.
|
|
94
|
+
*
|
|
95
|
+
* `mergedProps` must already be the result of `runtime.resolveProps(rest)`. The
|
|
96
|
+
* input object is never mutated; a copy is taken only when an HTML normalizer
|
|
97
|
+
* actually runs — most tags have none (`htmlPropNormalizersFn` returns
|
|
98
|
+
* `undefined` for every non-form element).
|
|
99
|
+
*/
|
|
100
|
+
export declare function resolveNormalizedProps(options: NormalizeCapableOptions, tag: ElementType, mergedProps: AnyRecord): AnyRecord;
|
|
101
|
+
//#endregion
|
|
102
|
+
export type { ElementType, IntrinsicProps };
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { DiagnosticCategory, DiagnosticCode } from "../_shared/diagnostics.js";
|
|
2
|
+
//#region ../../lib/foundation/src/iterate.ts
|
|
3
|
+
function find(iterable, callback) {
|
|
4
|
+
for (const value of iterable) {
|
|
5
|
+
const result = callback(value);
|
|
6
|
+
if (result != null) return result;
|
|
7
|
+
}
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
function some(iterable, predicate) {
|
|
11
|
+
for (const value of iterable) if (predicate(value)) return true;
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
function every(iterable, predicate) {
|
|
15
|
+
let index = 0;
|
|
16
|
+
for (const value of iterable) if (!predicate(value, index++)) return false;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
function* filter(iterable, predicate) {
|
|
20
|
+
let index = 0;
|
|
21
|
+
for (const value of iterable) if (predicate(value, index++)) yield value;
|
|
22
|
+
}
|
|
23
|
+
function* map(iterable, callback) {
|
|
24
|
+
let index = 0;
|
|
25
|
+
for (const value of iterable) yield callback(value, index++);
|
|
26
|
+
}
|
|
27
|
+
function forEach(iterable, callback) {
|
|
28
|
+
let index = 0;
|
|
29
|
+
for (const value of iterable) callback(value, index++);
|
|
30
|
+
}
|
|
31
|
+
function reduce(iterable, initial, callback) {
|
|
32
|
+
let accumulator = initial;
|
|
33
|
+
let index = 0;
|
|
34
|
+
for (const value of iterable) accumulator = callback(accumulator, value, index++);
|
|
35
|
+
return accumulator;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Transforms an iterable into a Record.
|
|
39
|
+
*
|
|
40
|
+
* The callback returns a `[key, value]` tuple for each element. Returning
|
|
41
|
+
* `null` aborts the collection and causes `collect()` to return `null`.
|
|
42
|
+
*/
|
|
43
|
+
function collect(iterable, callback) {
|
|
44
|
+
const result = {};
|
|
45
|
+
let index = 0;
|
|
46
|
+
for (const value of iterable) {
|
|
47
|
+
const entry = callback(value, index++);
|
|
48
|
+
if (entry === null) return null;
|
|
49
|
+
result[entry[0]] = entry[1];
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
function findLast(value, callback) {
|
|
54
|
+
for (let index = value.length - 1; index >= 0; index--) {
|
|
55
|
+
const result = callback(value[index], index);
|
|
56
|
+
if (result != null) return result;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
function* items(collection) {
|
|
61
|
+
for (let i = 0; i < collection.length; i++) {
|
|
62
|
+
const item = collection.item(i);
|
|
63
|
+
if (item !== null) yield item;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function nodeList(list) {
|
|
67
|
+
return { *[Symbol.iterator]() {
|
|
68
|
+
for (let i = 0; i < list.length; i++) {
|
|
69
|
+
const node = list.item(i);
|
|
70
|
+
if (node !== null) yield node;
|
|
71
|
+
}
|
|
72
|
+
} };
|
|
73
|
+
}
|
|
74
|
+
function mapEntries(m) {
|
|
75
|
+
return m.entries();
|
|
76
|
+
}
|
|
77
|
+
function set(s) {
|
|
78
|
+
return s.values();
|
|
79
|
+
}
|
|
80
|
+
function hasOwn(object, key) {
|
|
81
|
+
return Object.hasOwn(object, key);
|
|
82
|
+
}
|
|
83
|
+
function* entries(object) {
|
|
84
|
+
for (const key in object) {
|
|
85
|
+
if (!hasOwn(object, key)) continue;
|
|
86
|
+
yield [key, object[key]];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function* keys(object) {
|
|
90
|
+
for (const [key] of entries(object)) yield key;
|
|
91
|
+
}
|
|
92
|
+
function* values(object) {
|
|
93
|
+
for (const [, value] of entries(object)) yield value;
|
|
94
|
+
}
|
|
95
|
+
function mapValues(object, callback) {
|
|
96
|
+
const result = {};
|
|
97
|
+
for (const [key, value] of entries(object)) result[key] = callback(value, key);
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
function forEachEntry(object, callback) {
|
|
101
|
+
for (const [key, value] of entries(object)) callback(key, value);
|
|
102
|
+
}
|
|
103
|
+
function forEachKey(object, callback) {
|
|
104
|
+
for (const key of keys(object)) callback(key);
|
|
105
|
+
}
|
|
106
|
+
function forEachValue(object, callback) {
|
|
107
|
+
for (const value of values(object)) callback(value);
|
|
108
|
+
}
|
|
109
|
+
function forEachSet(s, callback) {
|
|
110
|
+
for (const value of s) callback(value);
|
|
111
|
+
}
|
|
112
|
+
const iterate = Object.freeze({
|
|
113
|
+
entries,
|
|
114
|
+
filter,
|
|
115
|
+
find,
|
|
116
|
+
findLast,
|
|
117
|
+
forEach,
|
|
118
|
+
forEachEntry,
|
|
119
|
+
forEachKey,
|
|
120
|
+
forEachSet,
|
|
121
|
+
forEachValue,
|
|
122
|
+
items,
|
|
123
|
+
keys,
|
|
124
|
+
map,
|
|
125
|
+
mapEntries,
|
|
126
|
+
mapValues,
|
|
127
|
+
nodeList,
|
|
128
|
+
reduce,
|
|
129
|
+
collect,
|
|
130
|
+
set,
|
|
131
|
+
some,
|
|
132
|
+
every,
|
|
133
|
+
values
|
|
134
|
+
});
|
|
135
|
+
//#endregion
|
|
136
|
+
//#region ../../lib/foundation/src/type-guards.ts
|
|
137
|
+
function isString(value) {
|
|
138
|
+
return typeof value === "string";
|
|
139
|
+
}
|
|
140
|
+
function isObject(value, excludeArrays = false) {
|
|
141
|
+
if (value === null || typeof value !== "object") return false;
|
|
142
|
+
return excludeArrays ? !Array.isArray(value) : true;
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region ../../lib/primitive/src/constants/aria/known-aria-roles.ts
|
|
146
|
+
const KNOWN_ARIA_ROLES = Object.freeze([
|
|
147
|
+
"alert",
|
|
148
|
+
"alertdialog",
|
|
149
|
+
"application",
|
|
150
|
+
"article",
|
|
151
|
+
"banner",
|
|
152
|
+
"blockquote",
|
|
153
|
+
"button",
|
|
154
|
+
"caption",
|
|
155
|
+
"cell",
|
|
156
|
+
"checkbox",
|
|
157
|
+
"code",
|
|
158
|
+
"columnheader",
|
|
159
|
+
"combobox",
|
|
160
|
+
"complementary",
|
|
161
|
+
"contentinfo",
|
|
162
|
+
"definition",
|
|
163
|
+
"deletion",
|
|
164
|
+
"dialog",
|
|
165
|
+
"document",
|
|
166
|
+
"emphasis",
|
|
167
|
+
"feed",
|
|
168
|
+
"figure",
|
|
169
|
+
"form",
|
|
170
|
+
"generic",
|
|
171
|
+
"grid",
|
|
172
|
+
"gridcell",
|
|
173
|
+
"group",
|
|
174
|
+
"heading",
|
|
175
|
+
"img",
|
|
176
|
+
"insertion",
|
|
177
|
+
"link",
|
|
178
|
+
"list",
|
|
179
|
+
"listbox",
|
|
180
|
+
"listitem",
|
|
181
|
+
"log",
|
|
182
|
+
"main",
|
|
183
|
+
"marquee",
|
|
184
|
+
"math",
|
|
185
|
+
"menu",
|
|
186
|
+
"menubar",
|
|
187
|
+
"menuitem",
|
|
188
|
+
"menuitemcheckbox",
|
|
189
|
+
"menuitemradio",
|
|
190
|
+
"meter",
|
|
191
|
+
"navigation",
|
|
192
|
+
"none",
|
|
193
|
+
"note",
|
|
194
|
+
"option",
|
|
195
|
+
"paragraph",
|
|
196
|
+
"presentation",
|
|
197
|
+
"progressbar",
|
|
198
|
+
"radio",
|
|
199
|
+
"radiogroup",
|
|
200
|
+
"region",
|
|
201
|
+
"row",
|
|
202
|
+
"rowgroup",
|
|
203
|
+
"rowheader",
|
|
204
|
+
"scrollbar",
|
|
205
|
+
"search",
|
|
206
|
+
"searchbox",
|
|
207
|
+
"separator",
|
|
208
|
+
"slider",
|
|
209
|
+
"spinbutton",
|
|
210
|
+
"status",
|
|
211
|
+
"strong",
|
|
212
|
+
"subscript",
|
|
213
|
+
"superscript",
|
|
214
|
+
"switch",
|
|
215
|
+
"tab",
|
|
216
|
+
"table",
|
|
217
|
+
"tablist",
|
|
218
|
+
"tabpanel",
|
|
219
|
+
"term",
|
|
220
|
+
"textbox",
|
|
221
|
+
"time",
|
|
222
|
+
"timer",
|
|
223
|
+
"toolbar",
|
|
224
|
+
"tooltip",
|
|
225
|
+
"tree",
|
|
226
|
+
"treegrid",
|
|
227
|
+
"treeitem"
|
|
228
|
+
]);
|
|
229
|
+
const KNOWN_ARIA_ROLES_SET = new Set(KNOWN_ARIA_ROLES);
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region ../../lib/primitive/src/guards/aria/is-known-aria-role.ts
|
|
232
|
+
function isKnownAriaRole(value) {
|
|
233
|
+
return isString(value) && KNOWN_ARIA_ROLES_SET.has(value);
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region ../../lib/contract/src/diagnostics/contract.ts
|
|
237
|
+
const ContractDiagnostics = {
|
|
238
|
+
unexpectedChild(typeName, index, context) {
|
|
239
|
+
return {
|
|
240
|
+
code: DiagnosticCode.UnexpectedChild,
|
|
241
|
+
category: DiagnosticCategory.Contract,
|
|
242
|
+
component: context,
|
|
243
|
+
message: `${context}: unexpected child "${typeName}" at index ${index}.`
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
ambiguousChild(typeName, index, ruleNames, context) {
|
|
247
|
+
const quoted = ruleNames.map((n) => `"${n}"`).join(" and ");
|
|
248
|
+
return {
|
|
249
|
+
code: DiagnosticCode.AmbiguousChild,
|
|
250
|
+
category: DiagnosticCategory.Contract,
|
|
251
|
+
component: context,
|
|
252
|
+
message: `${context}: child "${typeName}" at index ${index} matches multiple child rules: ${quoted}.`
|
|
253
|
+
};
|
|
254
|
+
},
|
|
255
|
+
cardinalityMin(ruleName, min, context) {
|
|
256
|
+
return {
|
|
257
|
+
code: DiagnosticCode.CardinalityMin,
|
|
258
|
+
category: DiagnosticCategory.Contract,
|
|
259
|
+
component: context,
|
|
260
|
+
message: `${context}: "${ruleName}" requires at least ${min}.`
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
cardinalityMax(ruleName, max, context) {
|
|
264
|
+
return {
|
|
265
|
+
code: DiagnosticCode.CardinalityMax,
|
|
266
|
+
category: DiagnosticCategory.Contract,
|
|
267
|
+
component: context,
|
|
268
|
+
message: `${context}: "${ruleName}" allows at most ${max}.`
|
|
269
|
+
};
|
|
270
|
+
},
|
|
271
|
+
positionViolation(ruleName, position, index, context) {
|
|
272
|
+
return {
|
|
273
|
+
code: DiagnosticCode.PositionViolation,
|
|
274
|
+
category: DiagnosticCategory.Contract,
|
|
275
|
+
component: context,
|
|
276
|
+
message: `${context}: "${ruleName}" must be ${position}, got index ${index}`
|
|
277
|
+
};
|
|
278
|
+
},
|
|
279
|
+
unknownVariantDim(component, label, dim) {
|
|
280
|
+
return {
|
|
281
|
+
code: DiagnosticCode.ContractUnknownVariantDim,
|
|
282
|
+
category: DiagnosticCategory.Contract,
|
|
283
|
+
message: `${component}: ${label} references unknown variant "${dim}".`
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
unknownVariantValue(component, label, dim, value, valid) {
|
|
287
|
+
return {
|
|
288
|
+
code: DiagnosticCode.ContractUnknownVariantValue,
|
|
289
|
+
category: DiagnosticCategory.Contract,
|
|
290
|
+
message: `${component}: ${label} sets "${dim}" to unknown value "${value}" (valid: ${valid.join(", ")}).`
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
unknownRecipeKey(component, key) {
|
|
294
|
+
return {
|
|
295
|
+
code: DiagnosticCode.ContractUnknownRecipeKey,
|
|
296
|
+
category: DiagnosticCategory.Contract,
|
|
297
|
+
message: `${component}: unknown recipeKey "${key}" — no preset with that name exists.`
|
|
298
|
+
};
|
|
299
|
+
},
|
|
300
|
+
invalidVariantValue(component, key, value) {
|
|
301
|
+
return {
|
|
302
|
+
code: DiagnosticCode.ContractInvalidVariantValue,
|
|
303
|
+
category: DiagnosticCategory.Contract,
|
|
304
|
+
message: `${component}: variant "${key}=${value}" is not a defined value for the "${key}" dimension.`
|
|
305
|
+
};
|
|
306
|
+
},
|
|
307
|
+
allowedAsViolation(tag, allowedAs, component) {
|
|
308
|
+
const allowed = allowedAs.map((t) => `"${String(t)}"`).join(", ");
|
|
309
|
+
return {
|
|
310
|
+
code: DiagnosticCode.AllowedAsViolation,
|
|
311
|
+
category: DiagnosticCategory.Contract,
|
|
312
|
+
component,
|
|
313
|
+
message: `<${component}>: "as" prop received "${tag}" but only [${allowed}] are allowed.`
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region ../core/src/resolver/resolver.ts
|
|
319
|
+
function enforceAllowedAs(tag, allowedAs, diagnostics, displayName) {
|
|
320
|
+
if (allowedAs.includes(tag)) return;
|
|
321
|
+
if (!diagnostics) return;
|
|
322
|
+
const component = displayName ?? String(tag);
|
|
323
|
+
diagnostics.error(ContractDiagnostics.allowedAsViolation(String(tag), allowedAs, component));
|
|
324
|
+
}
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region ../../lib/adapter-utils/src/props/apply-filter.ts
|
|
327
|
+
function applyFilter(props, filterProps, variantKeys) {
|
|
328
|
+
const out = {};
|
|
329
|
+
iterate.forEachEntry(props, (k) => {
|
|
330
|
+
if (!Object.hasOwn(props, k)) return;
|
|
331
|
+
if (filterProps(k, variantKeys)) return;
|
|
332
|
+
out[k] = props[k];
|
|
333
|
+
});
|
|
334
|
+
return out;
|
|
335
|
+
}
|
|
336
|
+
//#endregion
|
|
337
|
+
//#region ../../lib/adapter-utils/src/props/resolve-normalized-props.ts
|
|
338
|
+
/**
|
|
339
|
+
* The single canonical prop-normalization step for every render path — the five
|
|
340
|
+
* VDOM adapters (React, Preact, Vue, Solid, Svelte) and the host-state path
|
|
341
|
+
* shared by Lit, Web, and SSR.
|
|
342
|
+
*
|
|
343
|
+
* Ordering is fixed and load-bearing:
|
|
344
|
+
*
|
|
345
|
+
* ```text
|
|
346
|
+
* mergedProps → HTML built-in normalizers → normalizeFn
|
|
347
|
+
* ```
|
|
348
|
+
*
|
|
349
|
+
* `normalizeFn` is the primitive's composed `enforcement.props` normalizers plus
|
|
350
|
+
* the caller's `normalize` option (folded together by `composeNormalizers`).
|
|
351
|
+
* Running it last means a caller's `normalize` always observes — and can
|
|
352
|
+
* override — an HTML built-in's output for the same key. This must be identical
|
|
353
|
+
* across adapters: the same component with the same props has to normalize the
|
|
354
|
+
* same way whether it renders through React or through SSR.
|
|
355
|
+
*
|
|
356
|
+
* `mergedProps` must already be the result of `runtime.resolveProps(rest)`. The
|
|
357
|
+
* input object is never mutated; a copy is taken only when an HTML normalizer
|
|
358
|
+
* actually runs — most tags have none (`htmlPropNormalizersFn` returns
|
|
359
|
+
* `undefined` for every non-form element).
|
|
360
|
+
*/
|
|
361
|
+
function resolveNormalizedProps(options, tag, mergedProps) {
|
|
362
|
+
const htmlNormalizers = options.htmlPropNormalizersFn?.(tag);
|
|
363
|
+
let base = mergedProps;
|
|
364
|
+
if (htmlNormalizers?.length) {
|
|
365
|
+
base = { ...mergedProps };
|
|
366
|
+
for (const normalize of htmlNormalizers) Object.assign(base, normalize(base));
|
|
367
|
+
}
|
|
368
|
+
return typeof options.normalizeFn === "function" ? options.normalizeFn(base) : base;
|
|
369
|
+
}
|
|
370
|
+
//#endregion
|
|
371
|
+
export { applyFilter, enforceAllowedAs, isKnownAriaRole, isObject, isString, resolveNormalizedProps };
|