praxis-kit 4.1.2 → 5.0.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/dist/_shared/diagnostics.d.ts +10 -1
- package/dist/_shared/diagnostics.js +12 -0
- package/dist/{chunk-R2RKHZNX.js → chunk-SKAY22CZ.js} +67 -33
- package/dist/codemod/index.js +39 -22
- package/dist/contract/index.d.ts +18 -2
- package/dist/eslint/index.js +16 -16
- package/dist/lit/index.d.ts +18 -2
- package/dist/lit/index.js +64 -33
- package/dist/preact/index.d.ts +18 -2
- package/dist/preact/index.js +66 -33
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -1
- package/dist/react/legacy.d.ts +2 -2
- package/dist/react/legacy.js +1 -1
- package/dist/{react-options-XrRof248.d.ts → react-options-D6rRB2gJ.d.ts} +18 -2
- package/dist/solid/index.d.ts +18 -2
- package/dist/solid/index.js +109 -64
- package/dist/svelte/Polymorphic.svelte +146 -0
- package/dist/svelte/index.d.ts +34 -3
- package/dist/svelte/index.js +67 -33
- package/dist/tailwind/safelist.css +20 -0
- package/dist/vite-plugin/index.d.ts +31 -0
- package/dist/vite-plugin/index.js +32 -15
- package/dist/vue/index.d.ts +18 -2
- package/dist/vue/index.js +67 -33
- package/dist/web/index.d.ts +18 -2
- package/dist/web/index.js +64 -33
- package/package.json +62 -7
package/dist/solid/index.js
CHANGED
|
@@ -1856,17 +1856,22 @@ var RuleValidator = class _RuleValidator extends InvariantBase {
|
|
|
1856
1856
|
};
|
|
1857
1857
|
|
|
1858
1858
|
// ../../lib/contract/src/children/children-evaluator.ts
|
|
1859
|
+
var isTextLike = (child) => isString(child) || isNumber(child);
|
|
1859
1860
|
var ChildrenEvaluator = class extends InvariantBase {
|
|
1860
1861
|
#context;
|
|
1861
1862
|
#rules;
|
|
1862
1863
|
#ruleNames;
|
|
1863
1864
|
#matcher;
|
|
1864
1865
|
#ruleValidator;
|
|
1865
|
-
|
|
1866
|
+
#exclusiveChildren;
|
|
1867
|
+
#allowText;
|
|
1868
|
+
constructor(rules, diagnostics, context = "Component", options = {}) {
|
|
1866
1869
|
super(diagnostics);
|
|
1867
1870
|
this.#context = context;
|
|
1868
1871
|
this.#rules = rules.map((r) => normalizeChildRule(r));
|
|
1869
1872
|
this.#ruleNames = this.#rules.map((r) => r.name);
|
|
1873
|
+
this.#exclusiveChildren = options.exclusiveChildren ?? false;
|
|
1874
|
+
this.#allowText = options.allowText ?? true;
|
|
1870
1875
|
iterate.forEach(this.#rules, (rule) => {
|
|
1871
1876
|
const { name, position, cardinality } = rule;
|
|
1872
1877
|
if ((position === "first" || position === "last") && (cardinality.kind === "unbounded" || cardinality.max > 1)) {
|
|
@@ -1880,8 +1885,17 @@ var ChildrenEvaluator = class extends InvariantBase {
|
|
|
1880
1885
|
}
|
|
1881
1886
|
evaluate(children) {
|
|
1882
1887
|
if (!this.active) return;
|
|
1883
|
-
const {
|
|
1888
|
+
const {
|
|
1889
|
+
matrix,
|
|
1890
|
+
unexpectedIndices: rawUnexpectedIndices,
|
|
1891
|
+
ambiguousIndices
|
|
1892
|
+
} = this.#matcher.match(children);
|
|
1884
1893
|
this.#ruleValidator.validate(this.#rules, matrix, children.length);
|
|
1894
|
+
const unexpectedIndices = new Set(
|
|
1895
|
+
[...rawUnexpectedIndices].filter(
|
|
1896
|
+
(ci) => isTextLike(children[ci]) ? this.#allowText === false : this.#exclusiveChildren
|
|
1897
|
+
)
|
|
1898
|
+
);
|
|
1885
1899
|
if (unexpectedIndices.size === 0 && ambiguousIndices.size === 0) return;
|
|
1886
1900
|
const violating = [...unexpectedIndices, ...ambiguousIndices].sort((a, b) => a - b);
|
|
1887
1901
|
iterate.forEach(violating, (ci) => {
|
|
@@ -2003,8 +2017,11 @@ function metadata(name = "metadata") {
|
|
|
2003
2017
|
function firstOptional(name, tag) {
|
|
2004
2018
|
return { name, match: isTag(tag), cardinality: { max: 1 }, position: "first" };
|
|
2005
2019
|
}
|
|
2006
|
-
function contract(children) {
|
|
2007
|
-
return { diagnostics: warnDiagnostics, children };
|
|
2020
|
+
function contract(children, options) {
|
|
2021
|
+
return { diagnostics: warnDiagnostics, children, ...options };
|
|
2022
|
+
}
|
|
2023
|
+
function closedContract(children) {
|
|
2024
|
+
return contract(children, { exclusiveChildren: true });
|
|
2008
2025
|
}
|
|
2009
2026
|
function ariaContract(aria) {
|
|
2010
2027
|
return { diagnostics: warnDiagnostics, aria };
|
|
@@ -2030,8 +2047,10 @@ var VOID_TAGS = [
|
|
|
2030
2047
|
];
|
|
2031
2048
|
var TEXT_ONLY_TAGS = ["option", "script", "style", "textarea", "title"];
|
|
2032
2049
|
var LANDMARK_TAGS = ["article", "aside", "footer", "header", "main", "nav"];
|
|
2033
|
-
var listContract =
|
|
2034
|
-
|
|
2050
|
+
var listContract = closedContract([
|
|
2051
|
+
{ name: "list-item", match: isTag("li", ...METADATA_TAGS) }
|
|
2052
|
+
]);
|
|
2053
|
+
var tableContract = closedContract([
|
|
2035
2054
|
firstOptional("caption", "caption"),
|
|
2036
2055
|
{ name: "colgroup", match: isTag("colgroup") },
|
|
2037
2056
|
{ name: "thead", match: isTag("thead"), cardinality: { max: 1 } },
|
|
@@ -2039,29 +2058,31 @@ var tableContract = contract([
|
|
|
2039
2058
|
{ name: "tfoot", match: isTag("tfoot"), cardinality: { max: 1 } },
|
|
2040
2059
|
{ name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
|
|
2041
2060
|
]);
|
|
2042
|
-
var tableBodyContract =
|
|
2061
|
+
var tableBodyContract = closedContract([
|
|
2043
2062
|
{ name: "table-row", match: isTag("tr", ...METADATA_TAGS) }
|
|
2044
2063
|
]);
|
|
2045
|
-
var tableRowContract =
|
|
2064
|
+
var tableRowContract = closedContract([
|
|
2046
2065
|
{ name: "table-cell", match: isTag("td", "th", ...METADATA_TAGS) }
|
|
2047
2066
|
]);
|
|
2048
|
-
var colgroupContract =
|
|
2049
|
-
|
|
2067
|
+
var colgroupContract = closedContract([
|
|
2068
|
+
{ name: "column", match: isTag("col", "template") }
|
|
2069
|
+
]);
|
|
2070
|
+
var dlContract = closedContract([
|
|
2050
2071
|
{ name: "term", match: isTag("dt") },
|
|
2051
2072
|
{ name: "description", match: isTag("dd") },
|
|
2052
2073
|
{ name: "group", match: isTag("div") },
|
|
2053
2074
|
metadata()
|
|
2054
2075
|
]);
|
|
2055
|
-
var selectContract =
|
|
2076
|
+
var selectContract = closedContract([
|
|
2056
2077
|
{ name: "option", match: isTag("option", "optgroup", "hr", ...METADATA_TAGS) }
|
|
2057
2078
|
]);
|
|
2058
|
-
var optgroupContract =
|
|
2079
|
+
var optgroupContract = closedContract([
|
|
2059
2080
|
{ name: "option", match: isTag("option", ...METADATA_TAGS) }
|
|
2060
2081
|
]);
|
|
2061
|
-
var datalistContract =
|
|
2082
|
+
var datalistContract = closedContract([
|
|
2062
2083
|
{ name: "option", match: isTag("option", ...METADATA_TAGS) }
|
|
2063
2084
|
]);
|
|
2064
|
-
var pictureContract =
|
|
2085
|
+
var pictureContract = closedContract([
|
|
2065
2086
|
{ name: "source", match: isTag("source", ...METADATA_TAGS) },
|
|
2066
2087
|
{ name: "image", match: isTag("img"), cardinality: { min: 1, max: 1 }, position: "last" }
|
|
2067
2088
|
]);
|
|
@@ -2077,23 +2098,18 @@ var mediaContract = contract([
|
|
|
2077
2098
|
metadata(),
|
|
2078
2099
|
{ name: "content", match: isOpenContent("source", "track", ...METADATA_TAGS) }
|
|
2079
2100
|
]);
|
|
2080
|
-
var headContract =
|
|
2101
|
+
var headContract = closedContract([
|
|
2081
2102
|
{
|
|
2082
2103
|
name: "metadata",
|
|
2083
2104
|
match: isTag("base", "link", "meta", "noscript", "script", "style", "template", "title")
|
|
2084
2105
|
}
|
|
2085
2106
|
]);
|
|
2086
|
-
var htmlContract =
|
|
2107
|
+
var htmlContract = closedContract([
|
|
2087
2108
|
{ name: "head", match: isTag("head"), cardinality: { min: 1, max: 1 }, position: "first" },
|
|
2088
2109
|
{ name: "body", match: isTag("body"), cardinality: { min: 1, max: 1 } }
|
|
2089
2110
|
]);
|
|
2090
|
-
var voidContract = contract([]);
|
|
2091
|
-
var textOnlyContract = contract([
|
|
2092
|
-
{
|
|
2093
|
-
name: "text",
|
|
2094
|
-
match: (child) => isString(child) || isNumber(child)
|
|
2095
|
-
}
|
|
2096
|
-
]);
|
|
2111
|
+
var voidContract = contract([], { exclusiveChildren: true, allowText: false });
|
|
2112
|
+
var textOnlyContract = contract([], { exclusiveChildren: true });
|
|
2097
2113
|
var landmarkContract = ariaContract([landmarkRoleRule, landmarkNameAdvisory]);
|
|
2098
2114
|
var dialogContract = ariaContract([requireAccessibleName]);
|
|
2099
2115
|
var menuContract = ariaContract([requireAccessibleName]);
|
|
@@ -2138,9 +2154,15 @@ var htmlContracts = {
|
|
|
2138
2154
|
var htmlDiagnostics = warnDiagnostics2;
|
|
2139
2155
|
function buildEvaluatorMap() {
|
|
2140
2156
|
const map2 = /* @__PURE__ */ new Map();
|
|
2141
|
-
iterate.forEachEntry(htmlContracts, (tag, { children }) => {
|
|
2142
|
-
if (children?.length) {
|
|
2143
|
-
map2.set(
|
|
2157
|
+
iterate.forEachEntry(htmlContracts, (tag, { children, exclusiveChildren, allowText }) => {
|
|
2158
|
+
if (children?.length || exclusiveChildren || allowText === false) {
|
|
2159
|
+
map2.set(
|
|
2160
|
+
tag,
|
|
2161
|
+
new ChildrenEvaluator(children ?? [], htmlDiagnostics, `<${tag}>`, {
|
|
2162
|
+
exclusiveChildren,
|
|
2163
|
+
allowText
|
|
2164
|
+
})
|
|
2165
|
+
);
|
|
2144
2166
|
}
|
|
2145
2167
|
});
|
|
2146
2168
|
return map2;
|
|
@@ -2351,7 +2373,7 @@ function definePipeline(factory) {
|
|
|
2351
2373
|
}
|
|
2352
2374
|
|
|
2353
2375
|
// ../core/src/options/resolve-factory-options.ts
|
|
2354
|
-
import { silentDiagnostics } from "../_shared/diagnostics.js";
|
|
2376
|
+
import { resolveDiagnostics, silentDiagnostics } from "../_shared/diagnostics.js";
|
|
2355
2377
|
var EMPTY_VARIANT_KEYS = /* @__PURE__ */ new Set();
|
|
2356
2378
|
function composeNormalizers(normalizers, fn) {
|
|
2357
2379
|
if (!normalizers?.length) return fn;
|
|
@@ -2372,7 +2394,7 @@ function resolveFactoryOptions(options = {}) {
|
|
|
2372
2394
|
const variantKeys = styling?.variants === void 0 ? EMPTY_VARIANT_KEYS : new Set(Object.keys(styling.variants));
|
|
2373
2395
|
return Object.freeze({
|
|
2374
2396
|
defaultTag: options.tag ?? "div",
|
|
2375
|
-
diagnostics: enforcement?.diagnostics
|
|
2397
|
+
diagnostics: resolveDiagnostics(enforcement?.diagnostics, silentDiagnostics),
|
|
2376
2398
|
variantKeys,
|
|
2377
2399
|
...whenDefined("displayName", options.name),
|
|
2378
2400
|
...whenDefined("defaultProps", options.defaults),
|
|
@@ -2385,6 +2407,8 @@ function resolveFactoryOptions(options = {}) {
|
|
|
2385
2407
|
...whenDefined("normalizeFn", composedNormalizeFn),
|
|
2386
2408
|
...whenDefined("ariaRules", enforcement?.aria),
|
|
2387
2409
|
...whenDefined("childRules", enforcement?.children),
|
|
2410
|
+
...whenDefined("exclusiveChildren", enforcement?.exclusiveChildren),
|
|
2411
|
+
...whenDefined("allowText", enforcement?.allowText),
|
|
2388
2412
|
...whenDefined("allowedAs", enforcement?.allowedAs),
|
|
2389
2413
|
...whenDefined("precomputedClasses", styling?.precomputedClasses)
|
|
2390
2414
|
});
|
|
@@ -2569,16 +2593,22 @@ function buildCoreRuntime(normalized) {
|
|
|
2569
2593
|
}
|
|
2570
2594
|
|
|
2571
2595
|
// ../../lib/adapter-utils/src/runtime/build-engines.ts
|
|
2572
|
-
function buildEngines(diagnostics, childRules, context) {
|
|
2573
|
-
|
|
2596
|
+
function buildEngines(diagnostics, childRules, context, childrenOptions) {
|
|
2597
|
+
const { exclusiveChildren, allowText } = childrenOptions ?? {};
|
|
2598
|
+
return childRules?.length || exclusiveChildren || allowText === false ? {
|
|
2599
|
+
childrenEvaluator: new ChildrenEvaluator(childRules ?? [], diagnostics, context, {
|
|
2600
|
+
exclusiveChildren,
|
|
2601
|
+
allowText
|
|
2602
|
+
})
|
|
2603
|
+
} : {};
|
|
2574
2604
|
}
|
|
2575
2605
|
|
|
2576
2606
|
// ../../lib/adapter-utils/src/runtime/resolve-adapter-common-options.ts
|
|
2577
|
-
import { throwDiagnostics as throwDiagnostics2, silentDiagnostics as silentDiagnostics3 } from "../_shared/diagnostics.js";
|
|
2607
|
+
import { throwDiagnostics as throwDiagnostics2, silentDiagnostics as silentDiagnostics3, resolveDiagnostics as resolveDiagnostics2 } from "../_shared/diagnostics.js";
|
|
2578
2608
|
function resolveAdapterCommonOptions(options, defaultName = "PolymorphicComponent", defaultDiagnostics = throwDiagnostics2) {
|
|
2579
2609
|
return {
|
|
2580
2610
|
name: options.name ?? defaultName,
|
|
2581
|
-
diagnostics: options.enforcement?.diagnostics
|
|
2611
|
+
diagnostics: resolveDiagnostics2(options.enforcement?.diagnostics, defaultDiagnostics)
|
|
2582
2612
|
};
|
|
2583
2613
|
}
|
|
2584
2614
|
|
|
@@ -2633,7 +2663,11 @@ function buildRuntime(options) {
|
|
|
2633
2663
|
const { childrenEvaluator } = buildEngines(
|
|
2634
2664
|
normalized.diagnostics,
|
|
2635
2665
|
normalized.enforcement?.children,
|
|
2636
|
-
normalized.name
|
|
2666
|
+
normalized.name,
|
|
2667
|
+
{
|
|
2668
|
+
exclusiveChildren: normalized.enforcement?.exclusiveChildren,
|
|
2669
|
+
allowText: normalized.enforcement?.allowText
|
|
2670
|
+
}
|
|
2637
2671
|
);
|
|
2638
2672
|
const filterProps = composeFilter(ownedKeys, normalized.filterProps);
|
|
2639
2673
|
const slotValidator = new SlotValidator(normalized.name, normalized.diagnostics);
|
|
@@ -2646,9 +2680,10 @@ function buildRuntime(options) {
|
|
|
2646
2680
|
}
|
|
2647
2681
|
|
|
2648
2682
|
// ../../adapters/solid/src/render.tsx
|
|
2683
|
+
import { createComponent as _$createComponent } from "solid-js/web";
|
|
2684
|
+
import { mergeProps as _$mergeProps } from "solid-js/web";
|
|
2649
2685
|
import { createEffect, createMemo, splitProps } from "solid-js";
|
|
2650
2686
|
import { Dynamic } from "solid-js/web";
|
|
2651
|
-
import { jsx } from "solid-js/jsx-runtime";
|
|
2652
2687
|
var SPLIT_KEYS = ["as", "asChild", "children", "class", "recipe", "ref"];
|
|
2653
2688
|
function toChildArray(children) {
|
|
2654
2689
|
if (children === void 0 || children === null) return [];
|
|
@@ -2656,22 +2691,38 @@ function toChildArray(children) {
|
|
|
2656
2691
|
return [children];
|
|
2657
2692
|
}
|
|
2658
2693
|
function buildElementProps(props, classStr, ref, children) {
|
|
2659
|
-
const {
|
|
2694
|
+
const {
|
|
2695
|
+
role,
|
|
2696
|
+
...rest
|
|
2697
|
+
} = props;
|
|
2660
2698
|
return {
|
|
2661
2699
|
...rest,
|
|
2662
2700
|
class: classStr,
|
|
2663
|
-
...ref !== void 0 && {
|
|
2664
|
-
|
|
2665
|
-
|
|
2701
|
+
...ref !== void 0 && {
|
|
2702
|
+
ref
|
|
2703
|
+
},
|
|
2704
|
+
...children !== void 0 && {
|
|
2705
|
+
children
|
|
2706
|
+
},
|
|
2707
|
+
...isKnownAriaRole(role) && {
|
|
2708
|
+
role
|
|
2709
|
+
}
|
|
2666
2710
|
};
|
|
2667
2711
|
}
|
|
2668
2712
|
function buildSlotProps(props, classStr, ref) {
|
|
2669
|
-
const {
|
|
2713
|
+
const {
|
|
2714
|
+
role,
|
|
2715
|
+
...rest
|
|
2716
|
+
} = props;
|
|
2670
2717
|
return {
|
|
2671
2718
|
...rest,
|
|
2672
2719
|
class: classStr,
|
|
2673
|
-
...ref !== void 0 && {
|
|
2674
|
-
|
|
2720
|
+
...ref !== void 0 && {
|
|
2721
|
+
ref
|
|
2722
|
+
},
|
|
2723
|
+
...isKnownAriaRole(role) && {
|
|
2724
|
+
role
|
|
2725
|
+
}
|
|
2675
2726
|
};
|
|
2676
2727
|
}
|
|
2677
2728
|
function resolveTag2(runtime, as) {
|
|
@@ -2688,9 +2739,7 @@ function tryRenderAsChild(known, filteredProps, resolvedClass, slotValidator) {
|
|
|
2688
2739
|
}
|
|
2689
2740
|
if (!slotValidator.assertRenderFn(known.children)) return null;
|
|
2690
2741
|
const renderFn = known.children;
|
|
2691
|
-
return createMemo(
|
|
2692
|
-
() => renderFn(buildSlotProps(filteredProps(), resolvedClass(), known.ref))
|
|
2693
|
-
);
|
|
2742
|
+
return createMemo(() => renderFn(buildSlotProps(filteredProps(), resolvedClass(), known.ref)));
|
|
2694
2743
|
}
|
|
2695
2744
|
function render({
|
|
2696
2745
|
runtime,
|
|
@@ -2706,32 +2755,24 @@ function render({
|
|
|
2706
2755
|
const normalizedProps = createMemo(() => {
|
|
2707
2756
|
const base = runtime.options.normalizeFn ? runtime.options.normalizeFn(mergedProps()) : mergedProps();
|
|
2708
2757
|
const htmlNormalizers = runtime.options.htmlPropNormalizersFn?.(tag());
|
|
2709
|
-
return htmlNormalizers?.length ? htmlNormalizers.reduce((acc, fn) => ({
|
|
2758
|
+
return htmlNormalizers?.length ? htmlNormalizers.reduce((acc, fn) => ({
|
|
2759
|
+
...acc,
|
|
2760
|
+
...fn(acc)
|
|
2761
|
+
}), base) : base;
|
|
2710
2762
|
});
|
|
2711
|
-
const resolvedClass = createMemo(
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
known.recipe
|
|
2717
|
-
)
|
|
2718
|
-
);
|
|
2719
|
-
const filteredProps = createMemo(
|
|
2720
|
-
() => applyFilter(normalizedProps(), filterProps, runtime.options.variantKeys)
|
|
2721
|
-
);
|
|
2722
|
-
const { allowedAs } = runtime.options;
|
|
2763
|
+
const resolvedClass = createMemo(() => runtime.resolveClasses(tag(), normalizedProps(), known.class, known.recipe));
|
|
2764
|
+
const filteredProps = createMemo(() => applyFilter(normalizedProps(), filterProps, runtime.options.variantKeys));
|
|
2765
|
+
const {
|
|
2766
|
+
allowedAs
|
|
2767
|
+
} = runtime.options;
|
|
2723
2768
|
if (allowedAs !== void 0) {
|
|
2724
|
-
createEffect(
|
|
2725
|
-
() => enforceAllowedAs(tag(), allowedAs, runtime.options.diagnostics, runtime.options.displayName)
|
|
2726
|
-
);
|
|
2769
|
+
createEffect(() => enforceAllowedAs(tag(), allowedAs, runtime.options.diagnostics, runtime.options.displayName));
|
|
2727
2770
|
}
|
|
2728
2771
|
if (process.env.NODE_ENV !== "production" && childrenEvaluator) {
|
|
2729
2772
|
createEffect(() => childrenEvaluator.evaluate(toChildArray(known.children)));
|
|
2730
2773
|
}
|
|
2731
2774
|
if (process.env.NODE_ENV !== "production") {
|
|
2732
|
-
createEffect(
|
|
2733
|
-
() => runtime.options.htmlChildrenEvaluatorFn?.(tag())?.evaluate(toChildArray(known.children))
|
|
2734
|
-
);
|
|
2775
|
+
createEffect(() => runtime.options.htmlChildrenEvaluatorFn?.(tag())?.evaluate(toChildArray(known.children)));
|
|
2735
2776
|
}
|
|
2736
2777
|
const slotResult = tryRenderAsChild(known, filteredProps, resolvedClass, slotValidator);
|
|
2737
2778
|
if (slotResult !== null) return slotResult;
|
|
@@ -2739,7 +2780,11 @@ function render({
|
|
|
2739
2780
|
const ep = buildElementProps(filteredProps(), resolvedClass(), known.ref, known.children);
|
|
2740
2781
|
return resolveDomProps(tag(), ep, runtime);
|
|
2741
2782
|
});
|
|
2742
|
-
return
|
|
2783
|
+
return _$createComponent(Dynamic, _$mergeProps({
|
|
2784
|
+
get component() {
|
|
2785
|
+
return tag();
|
|
2786
|
+
}
|
|
2787
|
+
}, () => domProps()));
|
|
2743
2788
|
}
|
|
2744
2789
|
|
|
2745
2790
|
// ../../adapters/solid/src/create-contract-component.ts
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
declare const process: { env: { NODE_ENV: string } }
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { enforceAllowedAs, isKnownAriaRole } from '@praxis-kit/core'
|
|
7
|
+
import type { ElementType, IntrinsicProps } from '@praxis-kit/core'
|
|
8
|
+
import { isObject, isString } from '@praxis-kit/primitive'
|
|
9
|
+
import { applyFilter } from '@praxis-kit/adapter-utils'
|
|
10
|
+
import type { Snippet } from 'svelte'
|
|
11
|
+
import type {
|
|
12
|
+
PolymorphicComponentProps,
|
|
13
|
+
ResolvedAttributes,
|
|
14
|
+
StyleObject,
|
|
15
|
+
UnknownProps,
|
|
16
|
+
} from './types'
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
bundle,
|
|
20
|
+
as: asProp,
|
|
21
|
+
asChild,
|
|
22
|
+
class: cls,
|
|
23
|
+
recipe,
|
|
24
|
+
children,
|
|
25
|
+
...rest
|
|
26
|
+
}: PolymorphicComponentProps = $props()
|
|
27
|
+
let hostEl: Element | undefined = $state()
|
|
28
|
+
|
|
29
|
+
// Svelte 5 event delegation requires lowercase handler names (onclick, onfocus…).
|
|
30
|
+
// Normalize React-style camelCase handlers so spreads on <svelte:element> work.
|
|
31
|
+
const EVENT_RE = /^on[A-Z]/
|
|
32
|
+
function normalizeEventKeys(props: UnknownProps): UnknownProps {
|
|
33
|
+
const out: ResolvedAttributes = {}
|
|
34
|
+
for (const k in props) {
|
|
35
|
+
out[EVENT_RE.test(k) ? k.toLowerCase() : k] = (props as ResolvedAttributes)[k]
|
|
36
|
+
}
|
|
37
|
+
return out as UnknownProps
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function serializeStyle(style: StyleObject): string {
|
|
41
|
+
let result = ''
|
|
42
|
+
for (const key in style) {
|
|
43
|
+
const value = style[key]
|
|
44
|
+
if (value == null) continue
|
|
45
|
+
if (result) result += ';'
|
|
46
|
+
result += `${key.replace(/([A-Z])/g, '-$1').toLowerCase()}:${value}`
|
|
47
|
+
}
|
|
48
|
+
return result
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildDomProps(
|
|
52
|
+
props: UnknownProps,
|
|
53
|
+
classStr: string,
|
|
54
|
+
tag: ElementType,
|
|
55
|
+
): ResolvedAttributes {
|
|
56
|
+
const { role, style, ...r } = normalizeEventKeys(props)
|
|
57
|
+
const styleStr = isObject(style, true)
|
|
58
|
+
? serializeStyle(style as StyleObject)
|
|
59
|
+
: (style as string | undefined)
|
|
60
|
+
const ep: IntrinsicProps = {
|
|
61
|
+
...(r as IntrinsicProps),
|
|
62
|
+
class: classStr,
|
|
63
|
+
...(styleStr !== undefined && { style: styleStr }),
|
|
64
|
+
}
|
|
65
|
+
if (isKnownAriaRole(role)) ep.role = role
|
|
66
|
+
if (!isString(tag)) return ep as ResolvedAttributes
|
|
67
|
+
return bundle.runtime.resolveAria(tag, ep).props as ResolvedAttributes
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildSlotProps(props: UnknownProps, classStr: string): UnknownProps {
|
|
71
|
+
const { role, ...r } = props
|
|
72
|
+
return {
|
|
73
|
+
...r,
|
|
74
|
+
class: classStr,
|
|
75
|
+
...(isKnownAriaRole(role) && { role }),
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const tag = $derived(bundle.runtime.resolveTag(asProp as ElementType | undefined))
|
|
80
|
+
const mergedProps = $derived(bundle.runtime.resolveProps(rest as UnknownProps))
|
|
81
|
+
const normalizedProps = $derived.by(() => {
|
|
82
|
+
const { runtime: { options } } = bundle
|
|
83
|
+
// Folded into this derived (rather than a standalone one) so it's guaranteed to run on
|
|
84
|
+
// both SSR and DOM — $derived is lazy in Svelte 5 and only evaluates when read; this one
|
|
85
|
+
// is read downstream by resolvedClass/filteredProps/domProps, which the template renders.
|
|
86
|
+
if (options.allowedAs !== undefined) {
|
|
87
|
+
enforceAllowedAs(
|
|
88
|
+
tag,
|
|
89
|
+
options.allowedAs,
|
|
90
|
+
options.diagnostics,
|
|
91
|
+
options.displayName,
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
const base =
|
|
95
|
+
typeof options.normalizeFn === 'function' ? options.normalizeFn(mergedProps) : mergedProps
|
|
96
|
+
|
|
97
|
+
const htmlNormalizers = options.htmlPropNormalizersFn?.(tag)
|
|
98
|
+
|
|
99
|
+
if (!htmlNormalizers?.length) {
|
|
100
|
+
return base
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return htmlNormalizers.reduce((acc, fn) => ({ ...acc, ...fn(acc) }), base)
|
|
104
|
+
})
|
|
105
|
+
const resolvedClass = $derived(
|
|
106
|
+
bundle.runtime.resolveClasses(tag, normalizedProps, cls as string | undefined, recipe),
|
|
107
|
+
)
|
|
108
|
+
const filteredProps = $derived(
|
|
109
|
+
applyFilter(normalizedProps, bundle.filterProps, bundle.runtime.options.variantKeys),
|
|
110
|
+
)
|
|
111
|
+
const domProps = $derived(buildDomProps(filteredProps, resolvedClass, tag))
|
|
112
|
+
|
|
113
|
+
// Resolves whether to render as child slot; also enforces as+asChild mutual exclusion.
|
|
114
|
+
const useAsChild = $derived.by(() => {
|
|
115
|
+
if (!asChild) return false
|
|
116
|
+
if (asProp !== undefined) {
|
|
117
|
+
bundle.slotValidator.assertExclusive()
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
return true
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// DOM-only (like Lit skipping htmlChildrenEvaluatorFn for its SSR renderToString). Merely
|
|
124
|
+
// *registering* $effect during svelte/server's render() throws ("effect_orphan") — it's not
|
|
125
|
+
// enough to no-op inside the callback, the rune call itself must never happen during SSR.
|
|
126
|
+
// Reads real child nodes off the mounted host, same approach as Lit's Array.from(this.childNodes).
|
|
127
|
+
// Not reachable in the asChild branch — no host element there.
|
|
128
|
+
if (typeof document !== 'undefined') {
|
|
129
|
+
$effect(() => {
|
|
130
|
+
if (process.env.NODE_ENV === 'production' || !hostEl) return
|
|
131
|
+
const childArray = Array.from(hostEl.childNodes)
|
|
132
|
+
bundle.childrenEvaluator?.evaluate(childArray)
|
|
133
|
+
bundle.runtime.options.htmlChildrenEvaluatorFn?.(tag)?.evaluate(childArray)
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
{#if useAsChild}
|
|
139
|
+
{#if children}
|
|
140
|
+
{@render (children as Snippet<[UnknownProps]>)(buildSlotProps(filteredProps, resolvedClass))}
|
|
141
|
+
{/if}
|
|
142
|
+
{:else}
|
|
143
|
+
<svelte:element this={tag as string} bind:this={hostEl} {...domProps}>
|
|
144
|
+
{@render (children as Snippet | undefined)?.()}
|
|
145
|
+
</svelte:element>
|
|
146
|
+
{/if}
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequireAtLeastOne, Simplify, ReadonlyDeep } from 'type-fest';
|
|
2
|
-
import { Diagnostics, DiagnosticInput } from '../_shared/diagnostics.js';
|
|
2
|
+
import { Diagnostics, DiagnosticInput, DiagnosticsMode } from '../_shared/diagnostics.js';
|
|
3
3
|
|
|
4
4
|
type StringMap<T = unknown> = Record<string, T>;
|
|
5
5
|
type AnyRecord = StringMap<unknown>;
|
|
@@ -217,9 +217,25 @@ type AriaRule<C extends AriaContext = AriaContext> = (context: C) => readonly Ar
|
|
|
217
217
|
type PropNormalizer = (props: Readonly<AnyRecord & IntrinsicProps>) => Partial<AnyRecord & IntrinsicProps>;
|
|
218
218
|
|
|
219
219
|
type EnforcementOptions<TAllowed extends ElementType = ElementType> = {
|
|
220
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Accepts a preset name (`'warn'`, `'throw'`, `'silent'`) or a full `Diagnostics`
|
|
222
|
+
* instance for custom reporting/policy. The string form needs no import from
|
|
223
|
+
* `@praxis-kit/diagnostics`.
|
|
224
|
+
*/
|
|
225
|
+
readonly diagnostics?: Diagnostics | DiagnosticsMode;
|
|
221
226
|
readonly aria?: readonly AriaRule[];
|
|
222
227
|
readonly children?: readonly ChildRuleInput[];
|
|
228
|
+
/**
|
|
229
|
+
* When true, only children matching a `children` rule (or text, per `allowText`)
|
|
230
|
+
* are valid — anything else is rejected. Default: false (open — children not
|
|
231
|
+
* matching any rule are allowed).
|
|
232
|
+
*/
|
|
233
|
+
readonly exclusiveChildren?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* When false, text/number child nodes are rejected regardless of exclusiveChildren
|
|
236
|
+
* or any listed rule. Default: true.
|
|
237
|
+
*/
|
|
238
|
+
readonly allowText?: boolean;
|
|
223
239
|
readonly props?: readonly PropNormalizer[];
|
|
224
240
|
/** Restricts the `as` prop to this set of tags. Violations route through diagnostics. */
|
|
225
241
|
readonly allowedAs?: readonly TAllowed[];
|
|
@@ -265,6 +281,8 @@ type ResolvedFactoryOptions<TDefault extends ElementType = ElementType, Props ex
|
|
|
265
281
|
readonly htmlPropNormalizersFn?: (tag: unknown) => readonly PropNormalizer[] | undefined;
|
|
266
282
|
readonly htmlChildrenEvaluatorFn?: (tag: unknown) => ChildrenEvaluator$1 | undefined;
|
|
267
283
|
readonly childRules?: readonly ChildRuleInput[];
|
|
284
|
+
readonly exclusiveChildren?: boolean;
|
|
285
|
+
readonly allowText?: boolean;
|
|
268
286
|
readonly ariaRules?: readonly AriaRule[];
|
|
269
287
|
readonly allowedAs?: readonly ElementType[];
|
|
270
288
|
readonly precomputedClasses?: Readonly<Record<string, string>>;
|
|
@@ -302,9 +320,22 @@ declare abstract class InvariantBase {
|
|
|
302
320
|
protected invariant(condition: unknown, input: DiagnosticInput): void;
|
|
303
321
|
}
|
|
304
322
|
|
|
323
|
+
type ChildrenEvaluatorOptions = {
|
|
324
|
+
/**
|
|
325
|
+
* When true, only children matching a rule (or text, per `allowText`) are
|
|
326
|
+
* valid — anything else is rejected. Default: false (open — children not
|
|
327
|
+
* matching any rule are allowed).
|
|
328
|
+
*/
|
|
329
|
+
readonly exclusiveChildren?: boolean | undefined;
|
|
330
|
+
/**
|
|
331
|
+
* When false, text/number child nodes are rejected regardless of
|
|
332
|
+
* exclusiveChildren or any listed rule. Default: true.
|
|
333
|
+
*/
|
|
334
|
+
readonly allowText?: boolean | undefined;
|
|
335
|
+
};
|
|
305
336
|
declare class ChildrenEvaluator extends InvariantBase {
|
|
306
337
|
#private;
|
|
307
|
-
constructor(rules: readonly ChildRuleInput[], diagnostics: Diagnostics, context?: string);
|
|
338
|
+
constructor(rules: readonly ChildRuleInput[], diagnostics: Diagnostics, context?: string, options?: ChildrenEvaluatorOptions);
|
|
308
339
|
evaluate(children: unknown[]): void;
|
|
309
340
|
}
|
|
310
341
|
|