@lynstack/recipe 1.2.0 → 1.4.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/CHANGELOG.md +48 -0
- package/dist/index.d.ts +196 -33
- package/dist/index.js +255 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@ All notable changes to `@lynstack/recipe`. Each version is published on
|
|
|
4
4
|
npm and as a [GitHub release](https://github.com/lynstack/recipe/releases)
|
|
5
5
|
tagged `recipe@<version>`.
|
|
6
6
|
|
|
7
|
+
## 1.4.0 — 2026-10-07
|
|
8
|
+
|
|
9
|
+
A recipe kind can combine two values into one, so that a recipe that
|
|
10
|
+
composes others builds its results as fast as the one config it stands
|
|
11
|
+
for.
|
|
12
|
+
|
|
13
|
+
- `RecipeKind` takes `combine(first, second)`, optional, which returns
|
|
14
|
+
one value that adds what `first` then `second` add. A recipe that
|
|
15
|
+
composes others calls it when it is created, never on a call, to
|
|
16
|
+
combine its bases and the values that several configs give one option.
|
|
17
|
+
A slot recipe combines the values of each slot in the same way.
|
|
18
|
+
- A kind with `combine` must not change `first` or `second`, and follows
|
|
19
|
+
two rules, in which two accumulators must give the same results from
|
|
20
|
+
`finish`, now and after the same values are reduced into each: reducing
|
|
21
|
+
`first` then `second` gives what reducing `combine(first, second)`
|
|
22
|
+
gives, and `initial(base)` gives what reducing `base` into
|
|
23
|
+
`initial(undefined)` gives.
|
|
24
|
+
- Without `combine`, a composed recipe returns what it returned before.
|
|
25
|
+
When no option or compound variant has values from several configs, it
|
|
26
|
+
now compiles as one config too, and builds its results as fast.
|
|
27
|
+
- 1.3.0 said that a composed recipe is as fast as its one config. That
|
|
28
|
+
holds for cached calls; a result built without the cache cost more when
|
|
29
|
+
several configs gave values to one option or had a base. With
|
|
30
|
+
`combine`, it costs what the one config costs.
|
|
31
|
+
|
|
32
|
+
## 1.3.0 — 2026-10-07
|
|
33
|
+
|
|
34
|
+
A recipe can build on other recipes with `composes`.
|
|
35
|
+
|
|
36
|
+
- A recipe and a slot recipe take `composes`, a list of recipes whose
|
|
37
|
+
configs they add to their own, as if written in one config: their bases
|
|
38
|
+
first, every variant and option of each, with the values of an option in
|
|
39
|
+
the order of the recipes, their compound variants first, and the last
|
|
40
|
+
default given for each variant. A slot recipe has the slots of the slot
|
|
41
|
+
recipes it composes, theirs first. A recipe composed several times
|
|
42
|
+
counts once.
|
|
43
|
+
- A recipe composes recipes of any kind whose values have the type of its
|
|
44
|
+
own. Composing anything else, or a slot recipe in a recipe, is a type
|
|
45
|
+
error and throws a `TypeError` when the recipe is created.
|
|
46
|
+
- The configs are merged when the recipe is created: a composed recipe is
|
|
47
|
+
as fast as the one config it stands for.
|
|
48
|
+
- A recipe's type carries what it passes on to the recipes that compose
|
|
49
|
+
it, under a `~composition` property that exists in the type only.
|
|
50
|
+
`KindRecipe` takes it as a third, optional type parameter.
|
|
51
|
+
- New types for libraries built on the engine: `RecipeComposition`,
|
|
52
|
+
`Composable`, `ComposableKindRecipe`, `ComposableKindSlotRecipe`,
|
|
53
|
+
`ComposedVariants`, `ComposedDefaultedName`, and `ComposedSlot`.
|
|
54
|
+
|
|
7
55
|
## 1.2.0 — 2026-10-06
|
|
8
56
|
|
|
9
57
|
- A recipe and a slot recipe take `cache` in their config, which
|
package/dist/index.d.ts
CHANGED
|
@@ -76,15 +76,17 @@ type VariantKey<Selection> = KeyName<keyof Selection>;
|
|
|
76
76
|
* ```
|
|
77
77
|
*/
|
|
78
78
|
type VariantsOf<Recipe extends (props: never) => unknown> = Simplify<NonNullable<Parameters<Recipe>[0]>>;
|
|
79
|
-
//#endregion
|
|
80
|
-
//#region src/variants.d.ts
|
|
81
79
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
80
|
+
* Any selection, for variants whose names are not known at compile time,
|
|
81
|
+
* such as `Record<string, Record<string, string>>`.
|
|
84
82
|
*/
|
|
85
|
-
type
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
type AnySelection = Readonly<Record<string, unknown>>;
|
|
84
|
+
/** The selection of a recipe, or any selection for unknown variant names. */
|
|
85
|
+
type KindSelection<Variants, DefaultedName extends keyof Variants> = string extends keyof Variants ? AnySelection : VariantSelection<Variants, DefaultedName>;
|
|
86
|
+
/** The condition of a compound variant, or any for unknown variant names. */
|
|
87
|
+
type KindCompoundCondition<Variants> = string extends keyof Variants ? AnySelection : CompoundCondition<Variants>;
|
|
88
|
+
/** The default variants of a recipe, or any for unknown variant names. */
|
|
89
|
+
type KindDefaultVariants<Variants, DefaultedName extends keyof Variants> = string extends keyof Variants ? AnySelection : DefaultVariants<Variants, DefaultedName>;
|
|
88
90
|
/**
|
|
89
91
|
* How a kind of recipe turns the values of a selection into its result,
|
|
90
92
|
* such as by joining class names or merging style objects.
|
|
@@ -110,6 +112,23 @@ interface RecipeKind<Value, Accumulator, Result> {
|
|
|
110
112
|
* is `undefined` adds nothing.
|
|
111
113
|
*/
|
|
112
114
|
readonly reduce: (accumulator: Accumulator, value: Value) => Accumulator;
|
|
115
|
+
/**
|
|
116
|
+
* Returns one value that adds to an accumulator what `first` then
|
|
117
|
+
* `second` add. A recipe that composes other recipes calls it when it is
|
|
118
|
+
* created, to combine the bases of its recipes, and the values that its
|
|
119
|
+
* recipes give one option, so that it reduces one value for each, as the
|
|
120
|
+
* one config it stands for would. Without it, such a recipe reduces each
|
|
121
|
+
* value. It must not change `first` or `second`, which other recipes
|
|
122
|
+
* share, and the kind must then follow two rules. In each, the two
|
|
123
|
+
* accumulators must be interchangeable: `finish` returns the same result
|
|
124
|
+
* for both, and again after the same values are reduced into each.
|
|
125
|
+
*
|
|
126
|
+
* - Reducing `first` then `second` into an accumulator gives what
|
|
127
|
+
* reducing `combine(first, second)` gives.
|
|
128
|
+
* - `initial(base)` gives what reducing `base` into `initial(undefined)`
|
|
129
|
+
* gives.
|
|
130
|
+
*/
|
|
131
|
+
readonly combine?: ((first: Value, second: Value) => Value) | undefined;
|
|
113
132
|
/**
|
|
114
133
|
* Turns the accumulator into the result, for example by freezing it.
|
|
115
134
|
* Without it, the result is the accumulator.
|
|
@@ -122,17 +141,106 @@ interface RecipeKind<Value, Accumulator, Result> {
|
|
|
122
141
|
*/
|
|
123
142
|
readonly cache?: boolean | undefined;
|
|
124
143
|
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/composition.d.ts
|
|
125
146
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
147
|
+
* What a recipe passes on, in its type only, to the recipes that compose
|
|
148
|
+
* it.
|
|
149
|
+
*
|
|
150
|
+
* @typeParam Variants - The variant definitions of the recipe, keyed by
|
|
151
|
+
* variant name.
|
|
152
|
+
* @typeParam DefaultedName - The names of its variants that have a default.
|
|
153
|
+
* @typeParam Value - The value of an option.
|
|
154
|
+
* @typeParam Slots - The names of the slots of a slot recipe, as a list,
|
|
155
|
+
* or `undefined` for a recipe without slots.
|
|
128
156
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
157
|
+
interface RecipeComposition<Variants, DefaultedName, Value, Slots> {
|
|
158
|
+
readonly variants: Variants;
|
|
159
|
+
readonly defaultedName: DefaultedName;
|
|
160
|
+
readonly value: Value;
|
|
161
|
+
readonly slots: Slots;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Marks the type of a recipe that other recipes can compose, with what it
|
|
165
|
+
* passes on to them, under `~composition`. The property exists in the type
|
|
166
|
+
* only: a recipe never has it at runtime.
|
|
167
|
+
*
|
|
168
|
+
* @typeParam Composition - What the recipe passes on, a
|
|
169
|
+
* {@link RecipeComposition}, or `unknown` for a recipe that cannot be
|
|
170
|
+
* composed, which the type then leaves unmarked.
|
|
171
|
+
*/
|
|
172
|
+
type Composable<Composition> = unknown extends Composition ? unknown : {
|
|
173
|
+
readonly "~composition"?: Composition | undefined;
|
|
174
|
+
};
|
|
175
|
+
/** What a recipe of type `Recipe` passes on to the recipes that compose it. */
|
|
176
|
+
type CompositionOf<Recipe> = Recipe extends {
|
|
177
|
+
readonly "~composition"?: infer Composition;
|
|
178
|
+
} ? Exclude<Composition, undefined> : never;
|
|
179
|
+
/** What the recipes of `Composed` pass on under `Part`, as a union. */
|
|
180
|
+
type ComposedPart<Composed extends readonly unknown[], Part extends keyof RecipeComposition<unknown, unknown, unknown, unknown>> = ValueOfEach<CompositionOf<Composed[number]>, Part>;
|
|
181
|
+
/** The keys of each type of the union `Union`. */
|
|
182
|
+
type KeyOfEach<Union> = Union extends unknown ? keyof Union : never;
|
|
183
|
+
/** The values that the types of the union `Union` give to `Key`. */
|
|
184
|
+
type ValueOfEach<Union, Key extends PropertyKey> = Union extends unknown ? Key extends keyof Union ? Union[Key] : never : never;
|
|
185
|
+
/** One object with the keys of each object of `Union`. */
|
|
186
|
+
type MergeEach<Union> = { readonly [Key in KeyOfEach<Union>]: ValueOfEach<Union, Key>; };
|
|
187
|
+
/**
|
|
188
|
+
* The variants of a recipe that composes the recipes of `Composed`: the
|
|
189
|
+
* variants of each, and of `Variants`, with the options of each variant
|
|
190
|
+
* that any of them declares.
|
|
191
|
+
*
|
|
192
|
+
* @typeParam Composed - The types of the recipes composed.
|
|
193
|
+
* @typeParam Variants - The variant definitions of the recipe's own config.
|
|
194
|
+
*/
|
|
195
|
+
type ComposedVariants<Composed extends readonly unknown[], Variants> = [Composed[number]] extends [never] ? Variants : MergeVariants<ComposedPart<Composed, "variants"> | Variants>;
|
|
196
|
+
type MergeVariants<Union> = { readonly [Name in KeyOfEach<Union>]: MergeEach<ValueOfEach<Union, Name>>; };
|
|
197
|
+
/**
|
|
198
|
+
* The names of the variants with a default in a recipe that composes the
|
|
199
|
+
* recipes of `Composed`: those of each, and `DefaultedName`.
|
|
200
|
+
*
|
|
201
|
+
* @typeParam Composed - The types of the recipes composed.
|
|
202
|
+
* @typeParam DefaultedName - The names of the variants with a default in the
|
|
203
|
+
* recipe's own config.
|
|
204
|
+
*/
|
|
205
|
+
type ComposedDefaultedName<Composed extends readonly unknown[], DefaultedName> = DefaultedName | ComposedPart<Composed, "defaultedName">;
|
|
206
|
+
/**
|
|
207
|
+
* The slots of a slot recipe that composes the slot recipes of `Composed`:
|
|
208
|
+
* those of each, and `Slot`.
|
|
209
|
+
*
|
|
210
|
+
* @typeParam Composed - The types of the slot recipes composed.
|
|
211
|
+
* @typeParam Slot - The names of the slots of the recipe's own config.
|
|
212
|
+
*/
|
|
213
|
+
type ComposedSlot<Composed extends readonly unknown[], Slot extends string> = Slot | SlotOf<ComposedPart<Composed, "slots">>;
|
|
214
|
+
type SlotOf<Slots> = Slots extends readonly (infer Slot extends string)[] ? Slot : never;
|
|
215
|
+
/**
|
|
216
|
+
* A recipe that a recipe whose values are of type `Value` can compose: a
|
|
217
|
+
* recipe without slots, whose values are of type `Value`.
|
|
218
|
+
*
|
|
219
|
+
* @typeParam Value - The value of an option of the recipe that composes it.
|
|
220
|
+
*/
|
|
221
|
+
type ComposableKindRecipe<Value> = Composable<RecipeComposition<object, PropertyKey, Value, undefined>>;
|
|
222
|
+
/**
|
|
223
|
+
* A slot recipe that a slot recipe whose values are of type `Value` can
|
|
224
|
+
* compose: a slot recipe whose values are of type `Value`.
|
|
225
|
+
*
|
|
226
|
+
* @typeParam Value - The value of a slot of the slot recipe that composes
|
|
227
|
+
* it.
|
|
228
|
+
*/
|
|
229
|
+
type ComposableKindSlotRecipe<Value> = Composable<RecipeComposition<object, PropertyKey, Value, readonly string[]>>;
|
|
230
|
+
/**
|
|
231
|
+
* The recipe of a config whose variants, once composed, are `Variants`, and
|
|
232
|
+
* whose variants with a default are `DefaultedName`.
|
|
233
|
+
*/
|
|
234
|
+
type ComposedKindRecipe<Variants, DefaultedName, Value, Result, Slots> = KindRecipe<KindSelection<Variants, Extract<DefaultedName, keyof Variants>>, Result, RecipeComposition<Variants, Extract<DefaultedName, keyof Variants>, Value, Slots>>;
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region src/variants.d.ts
|
|
237
|
+
/**
|
|
238
|
+
* Variant definitions in the loose shape the runtime works with: for each
|
|
239
|
+
* variant name, the value of each of its options.
|
|
240
|
+
*/
|
|
241
|
+
type LooseVariants<Value> = Readonly<Record<string, Readonly<Record<string, Value>>>>;
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/recipe-kind.d.ts
|
|
136
244
|
/**
|
|
137
245
|
* The variants of a {@link KindRecipeConfig}: for each variant name, the
|
|
138
246
|
* value of each of its options.
|
|
@@ -162,8 +270,16 @@ interface KindCompoundVariant<Variants, Value> {
|
|
|
162
270
|
* @typeParam Value - The value of an option.
|
|
163
271
|
* @typeParam Variants - The variant definitions, keyed by variant name.
|
|
164
272
|
* @typeParam DefaultedName - The names of the variants that have a default.
|
|
273
|
+
* @typeParam Composed - The types of the recipes that the recipe composes.
|
|
165
274
|
*/
|
|
166
|
-
interface KindRecipeConfig<Value, Variants extends KindVariants<Value>, DefaultedName extends keyof Variants> {
|
|
275
|
+
interface KindRecipeConfig<Value, Variants extends KindVariants<Value>, DefaultedName extends keyof ComposedVariants<Composed, Variants>, Composed extends readonly ComposableKindRecipe<Value>[] = readonly []> {
|
|
276
|
+
/**
|
|
277
|
+
* Recipes whose config the recipe adds to its own, in order, as if it
|
|
278
|
+
* were written in one config: their bases first, the values of each of
|
|
279
|
+
* their options before its own, and their compound variants first. A
|
|
280
|
+
* recipe composed several times counts once.
|
|
281
|
+
*/
|
|
282
|
+
readonly composes?: Composed | undefined;
|
|
167
283
|
/** The value that the values of every selection are added to. */
|
|
168
284
|
readonly base?: Value | undefined;
|
|
169
285
|
/** For each variant name, the value of each of its options. */
|
|
@@ -172,9 +288,9 @@ interface KindRecipeConfig<Value, Variants extends KindVariants<Value>, Defaulte
|
|
|
172
288
|
* Values added when several variants have particular options at the same
|
|
173
289
|
* time, applied in order after the values of the variants' options.
|
|
174
290
|
*/
|
|
175
|
-
readonly compoundVariants?: readonly KindCompoundVariant<NoInfer<Variants
|
|
291
|
+
readonly compoundVariants?: readonly KindCompoundVariant<NoInfer<ComposedVariants<Composed, Variants>>, Value>[] | undefined;
|
|
176
292
|
/** The option each variant uses when the recipe is called without it. */
|
|
177
|
-
readonly defaultVariants?: KindDefaultVariants<
|
|
293
|
+
readonly defaultVariants?: KindDefaultVariants<ComposedVariants<Composed, Variants>, DefaultedName> | undefined;
|
|
178
294
|
/** Whether the recipe caches its results. Defaults to the kind's `cache`. */
|
|
179
295
|
readonly cache?: boolean | undefined;
|
|
180
296
|
}
|
|
@@ -185,14 +301,18 @@ interface KindRecipeConfig<Value, Variants extends KindVariants<Value>, Defaulte
|
|
|
185
301
|
*
|
|
186
302
|
* @typeParam Selection - The variants the recipe accepts.
|
|
187
303
|
* @typeParam Result - What the recipe returns.
|
|
304
|
+
* @typeParam Composition - What the recipe passes on to the recipes that
|
|
305
|
+
* compose it, a `RecipeComposition`. Without it, the type does not
|
|
306
|
+
* allow composing the recipe.
|
|
188
307
|
*/
|
|
189
|
-
type KindRecipe<Selection, Result> = RecipeFunction<Selection, Result> & {
|
|
308
|
+
type KindRecipe<Selection, Result, Composition = unknown> = RecipeFunction<Selection, Result> & {
|
|
190
309
|
/**
|
|
191
310
|
* The names of the recipe's variants, in the order of
|
|
192
|
-
* `Object.keys(config.variants)
|
|
311
|
+
* `Object.keys(config.variants)`, after those of the recipes it
|
|
312
|
+
* composes.
|
|
193
313
|
*/
|
|
194
314
|
readonly variantKeys: readonly VariantKey<Selection>[];
|
|
195
|
-
}
|
|
315
|
+
} & Composable<Composition>;
|
|
196
316
|
/**
|
|
197
317
|
* Creates recipes of one kind, the function that {@link createRecipeKind}
|
|
198
318
|
* returns.
|
|
@@ -203,7 +323,7 @@ type KindRecipe<Selection, Result> = RecipeFunction<Selection, Result> & {
|
|
|
203
323
|
* variants of the recipe, and whether it caches its results.
|
|
204
324
|
* @returns The recipe.
|
|
205
325
|
*/
|
|
206
|
-
type CreateKindRecipe<Value, Result> = <const Variants extends KindVariants<Value>, const DefaultedName extends keyof Variants = never>(config: KindRecipeConfig<Value, Variants, DefaultedName>) =>
|
|
326
|
+
type CreateKindRecipe<Value, Result> = <const Variants extends KindVariants<Value>, const DefaultedName extends keyof ComposedVariants<Composed, Variants> = never, const Composed extends readonly ComposableKindRecipe<Value>[] = readonly []>(config: KindRecipeConfig<Value, Variants, DefaultedName, Composed>) => ComposedKindRecipe<ComposedVariants<Composed, Variants>, ComposedDefaultedName<Composed, DefaultedName>, Value, Result, undefined>;
|
|
207
327
|
/**
|
|
208
328
|
* Creates a kind of recipe from how it turns the values of a selection into
|
|
209
329
|
* its result, and returns the function that creates recipes of that kind.
|
|
@@ -232,6 +352,16 @@ type CreateKindRecipe<Value, Result> = <const Variants extends KindVariants<Valu
|
|
|
232
352
|
* When the variant names are not known at compile time, as in a library
|
|
233
353
|
* that passes on a config it received, a recipe accepts any selection.
|
|
234
354
|
*
|
|
355
|
+
* A recipe composes the recipes listed in its config's `composes`, of any
|
|
356
|
+
* kind whose values have its type, as if their configs and its own were one:
|
|
357
|
+
* the accumulator starts from the first base, and the other bases are
|
|
358
|
+
* reduced first; every option of each is declared, with its values reduced
|
|
359
|
+
* in the order of the recipes; their compound variants come before its own;
|
|
360
|
+
* and a variant's default is the last one given. A recipe composed several
|
|
361
|
+
* times counts once. With `kind.combine`, the bases, and the values that
|
|
362
|
+
* the recipes give one option, are combined into one when the recipe is
|
|
363
|
+
* created, so that it reduces one value for each.
|
|
364
|
+
*
|
|
235
365
|
* @typeParam Value - The value of an option, inferred from the `value`
|
|
236
366
|
* parameter of `kind.reduce` or the `base` parameter of `kind.initial`.
|
|
237
367
|
* @typeParam Accumulator - What the values are reduced to, inferred from
|
|
@@ -249,6 +379,7 @@ type CreateKindRecipe<Value, Result> = <const Variants extends KindVariants<Valu
|
|
|
249
379
|
* const styleRecipe = createRecipeKind({
|
|
250
380
|
* initial: (base?: Style): Record<string, string | number> => ({ ...base }),
|
|
251
381
|
* reduce: (style, value: Style) => Object.assign(style, value),
|
|
382
|
+
* combine: (first, second) => ({ ...first, ...second }),
|
|
252
383
|
* finish: (style): Style => Object.freeze(style),
|
|
253
384
|
* });
|
|
254
385
|
*
|
|
@@ -270,6 +401,15 @@ type CreateKindRecipe<Value, Result> = <const Variants extends KindVariants<Valu
|
|
|
270
401
|
* // => { color: "black", fontSize: 24, opacity: 0.6, fontWeight: 300 }
|
|
271
402
|
*
|
|
272
403
|
* text.variantKeys; // => ["size", "muted"]
|
|
404
|
+
*
|
|
405
|
+
* const heading = styleRecipe({
|
|
406
|
+
* composes: [text],
|
|
407
|
+
* base: { fontWeight: 700 },
|
|
408
|
+
* variants: { size: { xl: { fontSize: 32 } } },
|
|
409
|
+
* });
|
|
410
|
+
*
|
|
411
|
+
* heading({ size: "xl" });
|
|
412
|
+
* // => { color: "black", fontWeight: 700, fontSize: 32 }
|
|
273
413
|
* ```
|
|
274
414
|
*/
|
|
275
415
|
declare function createRecipeKind<Value, Accumulator, Result = Accumulator>(kind: RecipeKind<Value, Accumulator, Result>): CreateKindRecipe<Value, Result>;
|
|
@@ -319,21 +459,31 @@ interface KindSlotCompoundVariant<Variants, Slot extends string, Value> {
|
|
|
319
459
|
* @typeParam Variants - The variant definitions, keyed by variant name.
|
|
320
460
|
* @typeParam DefaultedName - The names of the variants that have a default.
|
|
321
461
|
*/
|
|
322
|
-
interface KindSlotRecipeConfig<Slot extends string, Value, Variants extends KindSlotVariants<Value>, DefaultedName extends keyof Variants> {
|
|
323
|
-
/**
|
|
462
|
+
interface KindSlotRecipeConfig<Slot extends string, Value, Variants extends KindSlotVariants<Value>, DefaultedName extends keyof ComposedVariants<Composed, Variants>, Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly []> {
|
|
463
|
+
/**
|
|
464
|
+
* Slot recipes whose config the recipe adds to its own, in order, as if
|
|
465
|
+
* it were written in one config: their slots and bases first, the values
|
|
466
|
+
* of each of their options before its own, and their compound variants
|
|
467
|
+
* first. A slot recipe composed several times counts once.
|
|
468
|
+
*/
|
|
469
|
+
readonly composes?: Composed | undefined;
|
|
470
|
+
/**
|
|
471
|
+
* The names of the slots, in the order of the recipe's result, after
|
|
472
|
+
* those of the slot recipes it composes.
|
|
473
|
+
*/
|
|
324
474
|
readonly slots: readonly Slot[];
|
|
325
475
|
/** The value of each slot that the values of every selection are added to. */
|
|
326
|
-
readonly base?: SlotValues<NoInfer<Slot
|
|
476
|
+
readonly base?: SlotValues<NoInfer<ComposedSlot<Composed, Slot>>, Value> | undefined;
|
|
327
477
|
/** For each variant name, the values of each slot for each of its options. */
|
|
328
|
-
readonly variants: Variants & NoUnknownSlots<Variants, NoInfer<Slot
|
|
478
|
+
readonly variants: Variants & NoUnknownSlots<Variants, NoInfer<ComposedSlot<Composed, Slot>>>;
|
|
329
479
|
/**
|
|
330
480
|
* Values added to some slots when several variants have particular
|
|
331
481
|
* options at the same time, applied in order after the values of the
|
|
332
482
|
* variants' options.
|
|
333
483
|
*/
|
|
334
|
-
readonly compoundVariants?: readonly KindSlotCompoundVariant<NoInfer<Variants
|
|
484
|
+
readonly compoundVariants?: readonly KindSlotCompoundVariant<NoInfer<ComposedVariants<Composed, Variants>>, NoInfer<ComposedSlot<Composed, Slot>>, Value>[] | undefined;
|
|
335
485
|
/** The option each variant uses when the recipe is called without it. */
|
|
336
|
-
readonly defaultVariants?: KindDefaultVariants<
|
|
486
|
+
readonly defaultVariants?: KindDefaultVariants<ComposedVariants<Composed, Variants>, DefaultedName> | undefined;
|
|
337
487
|
/** Whether the recipe caches its results. Defaults to the kind's `cache`. */
|
|
338
488
|
readonly cache?: boolean | undefined;
|
|
339
489
|
}
|
|
@@ -343,11 +493,12 @@ interface KindSlotRecipeConfig<Slot extends string, Value, Variants extends Kind
|
|
|
343
493
|
*
|
|
344
494
|
* @typeParam Value - The value of a slot.
|
|
345
495
|
* @typeParam Result - What a recipe of this kind returns for each slot.
|
|
346
|
-
* @param config - The
|
|
347
|
-
* default variants of the slot recipe,
|
|
496
|
+
* @param config - The slot recipes it composes, and the slots, base values,
|
|
497
|
+
* variants, compound variants, and default variants of the slot recipe,
|
|
498
|
+
* and whether it caches its results.
|
|
348
499
|
* @returns The slot recipe.
|
|
349
500
|
*/
|
|
350
|
-
type CreateKindSlotRecipe<Value, Result> = <const Slot extends string, const Variants extends KindSlotVariants<Value>, const DefaultedName extends keyof Variants = never>(config: KindSlotRecipeConfig<Slot, Value, Variants, DefaultedName>) =>
|
|
501
|
+
type CreateKindSlotRecipe<Value, Result> = <const Slot extends string, const Variants extends KindSlotVariants<Value>, const DefaultedName extends keyof ComposedVariants<Composed, Variants> = never, const Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly []>(config: KindSlotRecipeConfig<Slot, Value, Variants, DefaultedName, Composed>) => ComposedKindRecipe<ComposedVariants<Composed, Variants>, ComposedDefaultedName<Composed, DefaultedName>, Value, Readonly<Record<ComposedSlot<Composed, Slot>, Result>>, readonly ComposedSlot<Composed, Slot>[]>;
|
|
351
502
|
/**
|
|
352
503
|
* Creates a kind of slot recipe from how it turns the values of each slot
|
|
353
504
|
* into that slot's result, and returns the function that creates slot
|
|
@@ -373,6 +524,10 @@ type CreateKindSlotRecipe<Value, Result> = <const Slot extends string, const Var
|
|
|
373
524
|
* options behave as in {@link createRecipeKind}, and the recipe's
|
|
374
525
|
* `variantKeys` property lists the names of its variants.
|
|
375
526
|
*
|
|
527
|
+
* A slot recipe composes the slot recipes listed in `composes` as a recipe
|
|
528
|
+
* composes recipes, and has the slots of each, theirs first. With
|
|
529
|
+
* `kind.combine`, the values of each slot are combined as a recipe's are.
|
|
530
|
+
*
|
|
376
531
|
* @typeParam Value - The value of a slot, inferred from the `value`
|
|
377
532
|
* parameter of `kind.reduce` or the `base` parameter of `kind.initial`.
|
|
378
533
|
* @typeParam Accumulator - What the values of a slot are reduced to,
|
|
@@ -423,9 +578,17 @@ type CreateKindSlotRecipe<Value, Result> = <const Slot extends string, const Var
|
|
|
423
578
|
* // }
|
|
424
579
|
*
|
|
425
580
|
* card.variantKeys; // => ["tone"]
|
|
581
|
+
*
|
|
582
|
+
* const dialog = slotStyleRecipe({
|
|
583
|
+
* composes: [card],
|
|
584
|
+
* slots: ["footer"],
|
|
585
|
+
* variants: { tone: { dark: { footer: { borderColor: "white" } } } },
|
|
586
|
+
* });
|
|
587
|
+
*
|
|
588
|
+
* dialog({ tone: "dark" }).footer; // => { borderColor: "white" }
|
|
426
589
|
* ```
|
|
427
590
|
*/
|
|
428
591
|
declare function createSlotRecipeKind<Value, Accumulator, Result = Accumulator>(kind: RecipeKind<Value, Accumulator, Result>): CreateKindSlotRecipe<Value, Result>;
|
|
429
592
|
//#endregion
|
|
430
|
-
export { type CompoundCondition, type CreateKindRecipe, type CreateKindSlotRecipe, type DefaultVariants, type KindCompoundVariant, type KindRecipe, type KindRecipeConfig, type KindSlotCompoundVariant, type KindSlotRecipeConfig, type KindSlotVariants, type KindVariants, type RecipeFunction, type RecipeKind, type SlotValues, type VariantKey, type VariantOption, type VariantSelection, type VariantsOf, createRecipeKind, createSlotRecipeKind };
|
|
593
|
+
export { type Composable, type ComposableKindRecipe, type ComposableKindSlotRecipe, type ComposedDefaultedName, type ComposedSlot, type ComposedVariants, type CompoundCondition, type CreateKindRecipe, type CreateKindSlotRecipe, type DefaultVariants, type KindCompoundVariant, type KindRecipe, type KindRecipeConfig, type KindSlotCompoundVariant, type KindSlotRecipeConfig, type KindSlotVariants, type KindVariants, type RecipeComposition, type RecipeFunction, type RecipeKind, type SlotValues, type VariantKey, type VariantOption, type VariantSelection, type VariantsOf, createRecipeKind, createSlotRecipeKind };
|
|
431
594
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -86,6 +86,209 @@ function isBooleanName(option) {
|
|
|
86
86
|
return option === "true" || option === "false";
|
|
87
87
|
}
|
|
88
88
|
//#endregion
|
|
89
|
+
//#region src/reduce-values.ts
|
|
90
|
+
/** Whether every condition of `compound` matches the selected indexes. */
|
|
91
|
+
function matches(compound, indexes) {
|
|
92
|
+
for (const [variant, matchingIndexes] of compound.conditions) if (!matchingIndexes.includes(indexes[variant] ?? 0)) return false;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Reduces the values that apply to a selection, in order of precedence: the
|
|
97
|
+
* value of each variant's option, then the value of each matching compound
|
|
98
|
+
* variant. A value that is undefined adds nothing.
|
|
99
|
+
*/
|
|
100
|
+
function reduceValues(compiled, indexes, reducer) {
|
|
101
|
+
const { reduce } = reducer;
|
|
102
|
+
let accumulator = reducer.initial();
|
|
103
|
+
for (let variant = 0; variant < compiled.valuesByIndex.length; variant += 1) {
|
|
104
|
+
const value = compiled.valuesByIndex[variant]?.[indexes[variant] ?? 0];
|
|
105
|
+
if (value !== void 0) accumulator = reduce(accumulator, value);
|
|
106
|
+
}
|
|
107
|
+
for (const compound of compiled.compounds) if (compound.value !== void 0 && matches(compound, indexes)) accumulator = reduce(accumulator, compound.value);
|
|
108
|
+
return accumulator;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Reduces the values that apply to a selection, as {@link reduceValues}
|
|
112
|
+
* does, when each option and compound variant has a list of values, which
|
|
113
|
+
* are reduced in order.
|
|
114
|
+
*/
|
|
115
|
+
function reduceValueLists(compiled, indexes, reducer) {
|
|
116
|
+
const { reduce } = reducer;
|
|
117
|
+
let accumulator = reducer.initial();
|
|
118
|
+
for (let variant = 0; variant < compiled.valuesByIndex.length; variant += 1) {
|
|
119
|
+
const values = compiled.valuesByIndex[variant]?.[indexes[variant] ?? 0];
|
|
120
|
+
accumulator = reduceEach(accumulator, values ?? [], reduce);
|
|
121
|
+
}
|
|
122
|
+
for (const compound of compiled.compounds) if (matches(compound, indexes)) accumulator = reduceEach(accumulator, compound.value, reduce);
|
|
123
|
+
return accumulator;
|
|
124
|
+
}
|
|
125
|
+
/** Reduces `values` into `accumulator`, in order. */
|
|
126
|
+
function reduceEach(accumulator, values, reduce) {
|
|
127
|
+
let result = accumulator;
|
|
128
|
+
for (const value of values) result = reduce(result, value);
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/compose.ts
|
|
133
|
+
/**
|
|
134
|
+
* Returns the layer of a recipe's own config, with copies of its records,
|
|
135
|
+
* so that changing the config later changes no recipe that composes it.
|
|
136
|
+
*/
|
|
137
|
+
function layerOf(config, slots) {
|
|
138
|
+
return {
|
|
139
|
+
base: config.base,
|
|
140
|
+
compoundVariants: (config.compoundVariants ?? []).map(({ variants, value }) => ({
|
|
141
|
+
value,
|
|
142
|
+
variants: { ...variants }
|
|
143
|
+
})),
|
|
144
|
+
defaultVariants: { ...config.defaultVariants },
|
|
145
|
+
slots: [...slots],
|
|
146
|
+
variants: Object.fromEntries(Object.entries(config.variants).map(([name, options]) => [name, { ...options }]))
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/** Returns an empty registry of recipes of type `type`. */
|
|
150
|
+
function createRegistry(type) {
|
|
151
|
+
const layersByRecipe = /* @__PURE__ */ new WeakMap();
|
|
152
|
+
const layersOfRecipe = (recipe) => {
|
|
153
|
+
const layers = typeof recipe === "function" ? layersByRecipe.get(recipe) : void 0;
|
|
154
|
+
if (layers === void 0) throw new TypeError(`A ${type} composes only ${type}s created by @lynstack/recipe. Check that the app installs one copy of @lynstack/recipe.`);
|
|
155
|
+
return layers;
|
|
156
|
+
};
|
|
157
|
+
return {
|
|
158
|
+
layersOf: (composes, own) => [.../* @__PURE__ */ new Set([...composes.flatMap((recipe) => layersOfRecipe(recipe)), own])],
|
|
159
|
+
withLayers: (recipe, layers) => {
|
|
160
|
+
layersByRecipe.set(recipe, layers);
|
|
161
|
+
return recipe;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Merges layers into one config: the union of their slots, of their
|
|
167
|
+
* variants, and of the options of each, the values of each option in the
|
|
168
|
+
* order of the layers, their compound variants in order, and the default
|
|
169
|
+
* of each variant from the last layer that gives one.
|
|
170
|
+
*/
|
|
171
|
+
function mergeLayers(layers) {
|
|
172
|
+
const variantNames = unique(layers.flatMap((layer) => Object.keys(layer.variants)));
|
|
173
|
+
return {
|
|
174
|
+
bases: layers.flatMap(({ base }) => listOf$1(base)),
|
|
175
|
+
compoundVariants: layers.flatMap((layer) => layer.compoundVariants.map(({ variants, value }) => ({
|
|
176
|
+
value: listOf$1(value),
|
|
177
|
+
variants
|
|
178
|
+
}))),
|
|
179
|
+
defaultVariants: Object.fromEntries(layers.flatMap((layer) => Object.entries(layer.defaultVariants).filter(([, option]) => option !== void 0))),
|
|
180
|
+
slots: unique(layers.flatMap(({ slots }) => slots)),
|
|
181
|
+
variants: Object.fromEntries(variantNames.map((name) => [name, mergeOptions(layers, name)]))
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** The options of the variant `name` in any layer, with their values. */
|
|
185
|
+
function mergeOptions(layers, name) {
|
|
186
|
+
const optionsOfLayers = layers.map((layer) => ownValue(layer.variants, name) ?? {});
|
|
187
|
+
const optionNames = unique(optionsOfLayers.flatMap((options) => Object.keys(options)));
|
|
188
|
+
return Object.fromEntries(optionNames.map((option) => [option, optionsOfLayers.flatMap((options) => listOf$1(ownValue(options, option)))]));
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Combines values, none of them undefined, into one with `combine`, in
|
|
192
|
+
* order, or returns undefined when there are none.
|
|
193
|
+
*/
|
|
194
|
+
function combineValues(values, combine) {
|
|
195
|
+
let combined = void 0;
|
|
196
|
+
for (const value of values) combined = combined === void 0 ? value : combine(combined, value);
|
|
197
|
+
return combined;
|
|
198
|
+
}
|
|
199
|
+
function ownValue(record, key) {
|
|
200
|
+
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
201
|
+
}
|
|
202
|
+
function listOf$1(value) {
|
|
203
|
+
return value === void 0 ? [] : [value];
|
|
204
|
+
}
|
|
205
|
+
function unique(names) {
|
|
206
|
+
return [...new Set(names)];
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/build-recipe.ts
|
|
210
|
+
const asResult$1 = (accumulator) => accumulator;
|
|
211
|
+
const noValues = Object.freeze([]);
|
|
212
|
+
const firstValue = (values) => values[0];
|
|
213
|
+
/** Compiles a recipe that composes no other recipe. */
|
|
214
|
+
function compileLayer(kind, layer) {
|
|
215
|
+
const { initial, reduce, finish } = kind;
|
|
216
|
+
const { base } = layer;
|
|
217
|
+
const compiled = compileVariants({
|
|
218
|
+
...layer,
|
|
219
|
+
noValue: void 0
|
|
220
|
+
});
|
|
221
|
+
const reducer = {
|
|
222
|
+
initial: () => initial(base),
|
|
223
|
+
reduce
|
|
224
|
+
};
|
|
225
|
+
return {
|
|
226
|
+
build: finish === void 0 ? (indexes) => reduceValues(compiled, indexes, reducer) : (indexes) => finish(reduceValues(compiled, indexes, reducer)),
|
|
227
|
+
compiled
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Compiles a recipe from the merged layers of the recipes it composes and
|
|
232
|
+
* its own. With `kind.combine`, it combines the bases and the values of
|
|
233
|
+
* each option and compiles them as one config. Without it, its accumulator
|
|
234
|
+
* starts from the first base, and the other bases are reduced into it
|
|
235
|
+
* first; when no option or compound variant has more than one value, it
|
|
236
|
+
* compiles them as one config.
|
|
237
|
+
*/
|
|
238
|
+
function compileMergedLayers(kind, merged) {
|
|
239
|
+
const { combine } = kind;
|
|
240
|
+
if (combine !== void 0) {
|
|
241
|
+
const combined = (values) => combineValues(values, combine);
|
|
242
|
+
return compileLayer(kind, layerOfMerged(merged, combined(merged.bases), combined));
|
|
243
|
+
}
|
|
244
|
+
const [base, ...otherBases] = merged.bases;
|
|
245
|
+
const kindOfBases = otherBases.length === 0 ? kind : {
|
|
246
|
+
...kind,
|
|
247
|
+
initial: () => reduceEach(kind.initial(base), otherBases, kind.reduce)
|
|
248
|
+
};
|
|
249
|
+
return isSingleValued(merged) ? compileLayer(kindOfBases, layerOfMerged(merged, base, firstValue)) : compileValueLists(kindOfBases, merged, base);
|
|
250
|
+
}
|
|
251
|
+
/** Whether no option or compound variant has more than one value. */
|
|
252
|
+
function isSingleValued(merged) {
|
|
253
|
+
return Object.values(merged.variants).flatMap((options) => Object.values(options)).every((values) => hasOneValueAtMost(values)) && merged.compoundVariants.every(({ value }) => hasOneValueAtMost(value));
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Returns the merged layers as one config with `base`, the value of each
|
|
257
|
+
* option and compound variant made by `valueOf` from its values.
|
|
258
|
+
*/
|
|
259
|
+
function layerOfMerged(merged, base, valueOf) {
|
|
260
|
+
return {
|
|
261
|
+
base,
|
|
262
|
+
compoundVariants: merged.compoundVariants.map(({ variants, value }) => ({
|
|
263
|
+
value: valueOf(value),
|
|
264
|
+
variants
|
|
265
|
+
})),
|
|
266
|
+
defaultVariants: merged.defaultVariants,
|
|
267
|
+
slots: [],
|
|
268
|
+
variants: Object.fromEntries(Object.entries(merged.variants).map(([name, options]) => [name, Object.fromEntries(Object.entries(options).map(([option, values]) => [option, valueOf(values)]))]))
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function hasOneValueAtMost(values) {
|
|
272
|
+
return values.length <= 1;
|
|
273
|
+
}
|
|
274
|
+
/** Compiles a recipe whose options or compound variants have value lists. */
|
|
275
|
+
function compileValueLists(kind, merged, base) {
|
|
276
|
+
const { initial, reduce, finish = asResult$1 } = kind;
|
|
277
|
+
const compiled = compileVariants({
|
|
278
|
+
...merged,
|
|
279
|
+
noValue: noValues
|
|
280
|
+
});
|
|
281
|
+
const reducer = {
|
|
282
|
+
initial: () => initial(base),
|
|
283
|
+
reduce
|
|
284
|
+
};
|
|
285
|
+
const build = (indexes) => finish(reduceValueLists(compiled, indexes, reducer));
|
|
286
|
+
return {
|
|
287
|
+
build,
|
|
288
|
+
compiled
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
89
292
|
//#region src/selector.ts
|
|
90
293
|
/** Whether `results` holds `result`, read at `key`, even if it is undefined. */
|
|
91
294
|
function isCached(results, key, result) {
|
|
@@ -112,45 +315,16 @@ function createSelector(compiled, build, options) {
|
|
|
112
315
|
};
|
|
113
316
|
}
|
|
114
317
|
//#endregion
|
|
115
|
-
//#region src/reduce-values.ts
|
|
116
|
-
/** Whether every condition of `compound` matches the selected indexes. */
|
|
117
|
-
function matches(compound, indexes) {
|
|
118
|
-
for (const [variant, matchingIndexes] of compound.conditions) if (!matchingIndexes.includes(indexes[variant] ?? 0)) return false;
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Reduces the values that apply to a selection, in order of precedence: the
|
|
123
|
-
* value of each variant's option, then the value of each matching compound
|
|
124
|
-
* variant. A value that is undefined adds nothing.
|
|
125
|
-
*/
|
|
126
|
-
function reduceValues(compiled, indexes, reducer) {
|
|
127
|
-
const { reduce } = reducer;
|
|
128
|
-
let accumulator = reducer.initial();
|
|
129
|
-
for (let variant = 0; variant < compiled.valuesByIndex.length; variant += 1) {
|
|
130
|
-
const value = compiled.valuesByIndex[variant]?.[indexes[variant] ?? 0];
|
|
131
|
-
if (value !== void 0) accumulator = reduce(accumulator, value);
|
|
132
|
-
}
|
|
133
|
-
for (const compound of compiled.compounds) if (compound.value !== void 0 && matches(compound, indexes)) accumulator = reduce(accumulator, compound.value);
|
|
134
|
-
return accumulator;
|
|
135
|
-
}
|
|
136
|
-
//#endregion
|
|
137
318
|
//#region src/recipe-kind.ts
|
|
319
|
+
const recipes = createRegistry("recipe");
|
|
138
320
|
function createRecipeKind(kind) {
|
|
139
|
-
const { initial, reduce, finish } = kind;
|
|
140
321
|
const kindCache = kind.cache ?? true;
|
|
141
322
|
return (config) => {
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
variants: config.variants
|
|
148
|
-
});
|
|
149
|
-
const reducer = {
|
|
150
|
-
initial: () => initial(base),
|
|
151
|
-
reduce
|
|
152
|
-
};
|
|
153
|
-
return withVariantKeys(createSelector(compiled, finish === void 0 ? (indexes) => reduceValues(compiled, indexes, reducer) : (indexes) => finish(reduceValues(compiled, indexes, reducer)), { cache: config.cache ?? kindCache }), compiled);
|
|
323
|
+
const own = layerOf(config, []);
|
|
324
|
+
const layers = recipes.layersOf(config.composes ?? [], own);
|
|
325
|
+
const { compiled, build } = layers.length === 1 ? compileLayer(kind, own) : compileMergedLayers(kind, mergeLayers(layers));
|
|
326
|
+
const recipe = createSelector(compiled, build, { cache: config.cache ?? kindCache });
|
|
327
|
+
return recipes.withLayers(withVariantKeys(recipe, compiled), layers);
|
|
154
328
|
};
|
|
155
329
|
}
|
|
156
330
|
//#endregion
|
|
@@ -158,31 +332,41 @@ function createRecipeKind(kind) {
|
|
|
158
332
|
function valueOfSlot(values, slot) {
|
|
159
333
|
return values !== void 0 && Object.hasOwn(values, slot) ? values[slot] : void 0;
|
|
160
334
|
}
|
|
161
|
-
/**
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Returns the values of each slot in a list of values for some slots, in
|
|
337
|
+
* the order of the list, combined into one with `combine` when the kind
|
|
338
|
+
* has it.
|
|
339
|
+
*/
|
|
340
|
+
function slotValuesOf(slots, valuesList, combine) {
|
|
341
|
+
return slots.map((slot) => {
|
|
342
|
+
const values = valuesList.flatMap((each) => listOf(valueOfSlot(each, slot)));
|
|
343
|
+
return combine === void 0 ? values : listOf(combineValues(values, combine));
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Returns the entries of the values of each slot, by slot index, or
|
|
348
|
+
* undefined when no slot has one.
|
|
349
|
+
*/
|
|
350
|
+
function entriesBySlot(valuesBySlot) {
|
|
351
|
+
const slotIndexes = valuesBySlot.flatMap((values, slot) => values.map(() => slot));
|
|
172
352
|
return slotIndexes.length === 0 ? void 0 : {
|
|
173
353
|
slots: slotIndexes,
|
|
174
|
-
values:
|
|
354
|
+
values: valuesBySlot.flat()
|
|
175
355
|
};
|
|
176
356
|
}
|
|
357
|
+
function listOf(value) {
|
|
358
|
+
return value === void 0 ? [] : [value];
|
|
359
|
+
}
|
|
177
360
|
/** Turns the slot values of each option and compound variant into entries. */
|
|
178
|
-
function compileEntries(compiled, slots) {
|
|
361
|
+
function compileEntries(compiled, slots, combine) {
|
|
362
|
+
const entriesOf = (valuesList) => entriesBySlot(slotValuesOf(slots, valuesList, combine));
|
|
179
363
|
return {
|
|
180
364
|
...compiled,
|
|
181
365
|
compounds: compiled.compounds.map((compound) => ({
|
|
182
366
|
conditions: compound.conditions,
|
|
183
|
-
value: entriesOf(
|
|
367
|
+
value: entriesOf(compound.value)
|
|
184
368
|
})).filter((compound) => compound.value !== void 0),
|
|
185
|
-
valuesByIndex: compiled.valuesByIndex.map((valuesOfVariant) => valuesOfVariant.map((values) => entriesOf(
|
|
369
|
+
valuesByIndex: compiled.valuesByIndex.map((valuesOfVariant) => valuesOfVariant.map((values) => entriesOf(values)))
|
|
186
370
|
};
|
|
187
371
|
}
|
|
188
372
|
/** Reduces the values of `added` into the accumulators of their slots. */
|
|
@@ -211,16 +395,24 @@ const asResult = (accumulator) => accumulator;
|
|
|
211
395
|
* see only the reducers of slot recipes and can be inlined.
|
|
212
396
|
*/
|
|
213
397
|
function createSlotsBuilder(kind, compiled, config) {
|
|
214
|
-
const { initial, reduce, finish = asResult } = kind;
|
|
398
|
+
const { initial, reduce, combine, finish = asResult } = kind;
|
|
215
399
|
const names = [...config.slots];
|
|
216
400
|
const slotNames = {
|
|
217
401
|
names,
|
|
218
402
|
template: Object.fromEntries(names.map((slot) => [slot, void 0]))
|
|
219
403
|
};
|
|
220
|
-
const
|
|
221
|
-
const
|
|
222
|
-
|
|
404
|
+
const basesBySlot = slotValuesOf(names, config.bases, combine);
|
|
405
|
+
const bases = basesBySlot.map(([base]) => base);
|
|
406
|
+
const otherBases = entriesBySlot(basesBySlot.map(([, ...others]) => others));
|
|
407
|
+
const { valuesByIndex, compounds } = compileEntries(compiled, names, combine);
|
|
408
|
+
/** Returns the accumulator of each slot, with every base reduced. */
|
|
409
|
+
const initialAccumulators = () => {
|
|
223
410
|
const accumulators = bases.map((base) => initial(base));
|
|
411
|
+
if (otherBases !== void 0) addEntries(accumulators, otherBases, reduce);
|
|
412
|
+
return accumulators;
|
|
413
|
+
};
|
|
414
|
+
return (indexes) => {
|
|
415
|
+
const accumulators = initialAccumulators();
|
|
224
416
|
for (let variant = 0; variant < valuesByIndex.length; variant += 1) {
|
|
225
417
|
const added = valuesByIndex[variant]?.[indexes[variant] ?? 0];
|
|
226
418
|
if (added !== void 0) addEntries(accumulators, added, reduce);
|
|
@@ -231,16 +423,20 @@ function createSlotsBuilder(kind, compiled, config) {
|
|
|
231
423
|
}
|
|
232
424
|
//#endregion
|
|
233
425
|
//#region src/slot-recipe-kind.ts
|
|
426
|
+
const slotRecipes = createRegistry("slot recipe");
|
|
427
|
+
const noSlotValues = Object.freeze([]);
|
|
234
428
|
function createSlotRecipeKind(kind) {
|
|
235
429
|
const kindCache = kind.cache ?? true;
|
|
236
430
|
return (config) => {
|
|
431
|
+
const own = layerOf(config, config.slots);
|
|
432
|
+
const layers = slotRecipes.layersOf(config.composes ?? [], own);
|
|
433
|
+
const merged = mergeLayers(layers);
|
|
237
434
|
const compiled = compileVariants({
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
noValue: void 0,
|
|
241
|
-
variants: config.variants
|
|
435
|
+
...merged,
|
|
436
|
+
noValue: noSlotValues
|
|
242
437
|
});
|
|
243
|
-
|
|
438
|
+
const recipe = createSelector(compiled, createSlotsBuilder(kind, compiled, merged), { cache: config.cache ?? kindCache });
|
|
439
|
+
return slotRecipes.withLayers(withVariantKeys(recipe, compiled), layers);
|
|
244
440
|
};
|
|
245
441
|
}
|
|
246
442
|
//#endregion
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/variants.ts","../src/selector.ts","../src/reduce-values.ts","../src/recipe-kind.ts","../src/slots.ts","../src/slot-recipe-kind.ts"],"sourcesContent":["/**\n * Variant definitions in the loose shape the runtime works with: for each\n * variant name, the value of each of its options.\n */\ntype LooseVariants<Value> = Readonly<\n Record<string, Readonly<Record<string, Value>>>\n>;\n\n/** A selection in the loose shape the runtime works with. */\ntype SelectedVariants = Readonly<Record<string, unknown>>;\n\n/** A compound variant in the loose shape the runtime works with. */\ninterface LooseCompoundVariant<Value> {\n readonly variants: SelectedVariants;\n readonly value: Value;\n}\n\n/** The variant's index and the option indexes that a condition matches. */\ntype CompoundCondition = readonly [number, readonly number[]];\n\n/** A compound variant prepared for matching. */\ninterface Compound<Value> {\n readonly conditions: readonly CompoundCondition[];\n readonly value: Value;\n}\n\n/** What {@link compileVariants} prepares. */\ninterface VariantsConfig<Value> {\n readonly variants: LooseVariants<Value>;\n readonly defaultVariants: SelectedVariants;\n readonly compoundVariants: readonly LooseCompoundVariant<Value>[];\n /**\n * The value of a variant without a selected option, and of a boolean\n * option that the variant does not declare.\n */\n readonly noValue: Value;\n}\n\n/**\n * Variants prepared for selecting values. Each variant numbers its options\n * from 1, and 0 stands for no option, so a selection is a list of option\n * indexes. Read together as the digits of a mixed-radix number, they form\n * the selection's key.\n */\ninterface CompiledVariants<Value> {\n readonly names: readonly string[];\n readonly defaultOptions: readonly (string | undefined)[];\n readonly indexByOption: readonly ReadonlyMap<string, number>[];\n /** The value of each variant's options, by option index. */\n readonly valuesByIndex: readonly (readonly Value[])[];\n readonly strides: readonly number[];\n readonly compounds: readonly Compound<Value>[];\n /** Whether every selection's key is a safe integer. */\n readonly isCacheable: boolean;\n}\n\n/** The option index of a variant without a selected option. */\nconst noOption = 0;\n\n/** The key of a selection with an option that a variant does not declare. */\nconst undeclared = -1;\n\nconst noProps: SelectedVariants = Object.freeze({});\n\n/**\n * Prepares variants for selecting values. A variant that declares an option\n * named `\"true\"` or `\"false\"` also declares the other one, with\n * `config.noValue`, and a variant whose only options are those defaults to\n * `\"false\"`. A compound variant that names an undeclared variant or option,\n * or lists no option for a variant, never matches and is left out.\n */\nfunction compileVariants<Value>(\n config: VariantsConfig<Value>,\n): CompiledVariants<Value> {\n const names = Object.keys(config.variants);\n const optionValues = names.map((name) =>\n withBooleanOptions(config.variants[name] ?? {}, config.noValue),\n );\n const indexByOption: readonly ReadonlyMap<string, number>[] =\n optionValues.map(\n (valuesByOption) =>\n new Map(\n Object.keys(valuesByOption).map((option, index) => [\n option,\n index + 1,\n ]),\n ),\n );\n const radixes = indexByOption.map((indexes) => indexes.size + 1);\n\n return {\n compounds: compileCompounds(config.compoundVariants, names, indexByOption),\n defaultOptions: names.map(\n (name, index) =>\n toOptionName(config.defaultVariants[name]) ??\n (isBooleanVariant(indexByOption[index]) ? \"false\" : undefined),\n ),\n indexByOption,\n isCacheable: product(radixes) <= Number.MAX_SAFE_INTEGER,\n names,\n strides: radixes.map((_radix, index) => product(radixes.slice(0, index))),\n valuesByIndex: valuesByIndexOf(optionValues, config.noValue),\n };\n}\n\n/**\n * Writes the option index of each variant for `selected` into `indexes`\n * and returns the selection's key, or {@link undeclared} when an option is\n * not declared.\n */\nfunction select(\n compiled: CompiledVariants<unknown>,\n selected: SelectedVariants,\n indexes: Int32Array,\n): number {\n let key = 0;\n for (let variant = 0; variant < compiled.names.length; variant += 1) {\n const index = selectIndex(compiled, selected, variant);\n indexes[variant] = index ?? noOption;\n key =\n index === undefined || key === undeclared\n ? undeclared\n : key + index * (compiled.strides[variant] ?? 0);\n }\n return key;\n}\n\nfunction selectIndex(\n compiled: CompiledVariants<unknown>,\n selected: SelectedVariants,\n variant: number,\n): number | undefined {\n const option =\n toOptionName(selected[compiled.names[variant] ?? \"\"]) ??\n compiled.defaultOptions[variant];\n return option === undefined\n ? noOption\n : compiled.indexByOption[variant]?.get(option);\n}\n\nfunction valuesByIndexOf<Value>(\n optionValues: readonly Readonly<Record<string, Value>>[],\n noValue: Value,\n): Value[][] {\n const valuesByIndex: Value[][] = [];\n for (const valuesByOption of optionValues) {\n valuesByIndex.push([noValue, ...Object.values(valuesByOption)]);\n }\n return valuesByIndex;\n}\n\nfunction product(numbers: readonly number[]): number {\n return numbers.reduce((result, number) => result * number, 1);\n}\n\nfunction withBooleanOptions<Value>(\n valuesByOption: Readonly<Record<string, Value>>,\n noValue: Value,\n): Readonly<Record<string, Value>> {\n const optionNames = Object.keys(valuesByOption);\n if (!optionNames.some((option) => isBooleanName(option))) {\n return valuesByOption;\n }\n return { false: noValue, true: noValue, ...valuesByOption };\n}\n\nfunction compileCompounds<Value>(\n compoundVariants: readonly LooseCompoundVariant<Value>[],\n names: readonly string[],\n indexByOption: readonly ReadonlyMap<string, number>[],\n): Compound<Value>[] {\n return compoundVariants.flatMap(({ variants, value }) => {\n const conditions = Object.keys(variants)\n .filter((name) => variants[name] !== undefined)\n .map((name) =>\n compileCondition(indexByOption, names.indexOf(name), variants[name]),\n );\n return conditions.every(([, indexes]) => indexes.length > 0)\n ? [{ conditions, value }]\n : [];\n });\n}\n\nfunction compileCondition(\n indexByOption: readonly ReadonlyMap<string, number>[],\n variant: number,\n value: unknown,\n): CompoundCondition {\n const indexes = indexByOption[variant];\n return [\n variant,\n toOptionNames(value)\n .map((option) => indexes?.get(option))\n .filter((index) => index !== undefined),\n ];\n}\n\nfunction isBooleanVariant(\n indexByOption: ReadonlyMap<string, number> | undefined,\n): boolean {\n return (\n indexByOption !== undefined &&\n indexByOption.size > 0 &&\n [...indexByOption.keys()].every((option) => isBooleanName(option))\n );\n}\n\nfunction toOptionName(value: unknown): string | undefined {\n if (typeof value === \"string\") {\n return value;\n }\n return typeof value === \"number\" || typeof value === \"boolean\"\n ? String(value)\n : undefined;\n}\n\nfunction toOptionNames(value: unknown): readonly string[] {\n const values: readonly unknown[] = Array.isArray(value) ? value : [value];\n return values\n .map((option) => toOptionName(option))\n .filter((option) => option !== undefined);\n}\n\n/** A recipe function with the names of its variants. */\ntype WithVariantKeys<Recipe> = Recipe & {\n readonly variantKeys: readonly string[];\n};\n\n/** Adds the names of the compiled variants to `recipe` as `variantKeys`. */\nfunction withVariantKeys<Recipe extends object>(\n recipe: Recipe,\n compiled: CompiledVariants<unknown>,\n): WithVariantKeys<Recipe> {\n return Object.assign(recipe, {\n variantKeys: Object.freeze([...compiled.names]),\n });\n}\n\nfunction isBooleanName(option: string): boolean {\n return option === \"true\" || option === \"false\";\n}\n\nexport {\n compileVariants,\n noOption,\n noProps,\n select,\n undeclared,\n withVariantKeys,\n};\nexport type {\n CompiledVariants,\n Compound,\n LooseCompoundVariant,\n LooseVariants,\n SelectedVariants,\n WithVariantKeys,\n};\n","import type { CompiledVariants, SelectedVariants } from \"./variants.js\";\nimport { noProps, select, undeclared } from \"./variants.js\";\n\n/** How a selector computes its results. */\ninterface SelectorOptions {\n /** Whether to cache the result of each declared selection. */\n readonly cache: boolean;\n}\n\n/** Whether `results` holds `result`, read at `key`, even if it is undefined. */\nfunction isCached<Result>(\n results: ReadonlyMap<number, Result>,\n key: number,\n result: Result | undefined,\n): result is Result {\n return result !== undefined || results.has(key);\n}\n\n/**\n * Returns a function that builds the result of a selection from the option\n * index of each variant, and treats a missing selection as an empty one.\n * With `options.cache`, it builds the result of each declared selection\n * once and caches it by the selection's key.\n */\nfunction createSelector<Result>(\n compiled: CompiledVariants<unknown>,\n build: (indexes: Int32Array) => Result,\n options: SelectorOptions,\n): (selected?: SelectedVariants | null) => Result {\n const indexes = new Int32Array(compiled.names.length);\n const results =\n options.cache && compiled.isCacheable\n ? new Map<number, Result>()\n : undefined;\n\n return (selected) => {\n const key = select(compiled, selected ?? noProps, indexes);\n if (key !== undeclared && results !== undefined) {\n const cachedResult = results.get(key);\n if (isCached(results, key, cachedResult)) {\n return cachedResult;\n }\n }\n const result = build(indexes);\n if (key !== undeclared) {\n results?.set(key, result);\n }\n return result;\n };\n}\n\nexport { createSelector };\nexport type { SelectorOptions };\n","import type { CompiledVariants, Compound } from \"./variants.js\";\nimport { noOption } from \"./variants.js\";\n\n/** Whether every condition of `compound` matches the selected indexes. */\nfunction matches(compound: Compound<unknown>, indexes: Int32Array): boolean {\n for (const [variant, matchingIndexes] of compound.conditions) {\n if (!matchingIndexes.includes(indexes[variant] ?? noOption)) {\n return false;\n }\n }\n return true;\n}\n\n/** Reduces the values of a selection to an accumulator. */\ninterface Reducer<Value, Accumulator> {\n /** Returns the accumulator to start from. */\n readonly initial: () => Accumulator;\n /** Adds a value to the accumulator and returns the accumulator. */\n readonly reduce: (accumulator: Accumulator, value: Value) => Accumulator;\n}\n\n/**\n * Reduces the values that apply to a selection, in order of precedence: the\n * value of each variant's option, then the value of each matching compound\n * variant. A value that is undefined adds nothing.\n */\nfunction reduceValues<Value, Accumulator>(\n compiled: CompiledVariants<Value | undefined>,\n indexes: Int32Array,\n reducer: Reducer<Value, Accumulator>,\n): Accumulator {\n const { reduce } = reducer;\n let accumulator = reducer.initial();\n for (let variant = 0; variant < compiled.valuesByIndex.length; variant += 1) {\n const value =\n compiled.valuesByIndex[variant]?.[indexes[variant] ?? noOption];\n if (value !== undefined) {\n accumulator = reduce(accumulator, value);\n }\n }\n for (const compound of compiled.compounds) {\n if (compound.value !== undefined && matches(compound, indexes)) {\n accumulator = reduce(accumulator, compound.value);\n }\n }\n return accumulator;\n}\n\nexport { matches, reduceValues };\nexport type { Reducer };\n","import type {\n CompoundCondition,\n DefaultVariants,\n RecipeFunction,\n VariantKey,\n VariantSelection,\n} from \"./types.js\";\nimport type {\n LooseVariants,\n SelectedVariants,\n WithVariantKeys,\n} from \"./variants.js\";\nimport { compileVariants, withVariantKeys } from \"./variants.js\";\nimport { createSelector } from \"./selector.js\";\nimport { reduceValues } from \"./reduce-values.js\";\n\n/**\n * How a kind of recipe turns the values of a selection into its result,\n * such as by joining class names or merging style objects.\n *\n * @typeParam Value - The value of an option.\n * @typeParam Accumulator - What the values of a selection are reduced to.\n * @typeParam Result - What a recipe of this kind returns.\n */\ninterface RecipeKind<Value, Accumulator, Result> {\n /**\n * Returns the accumulator that the values of a selection are reduced\n * into, starting from the recipe's `base`, which is `undefined` when the\n * recipe has none. It is called for each result a recipe builds, so it\n * can return a new object each time.\n */\n readonly initial: (base: Value | undefined) => Accumulator;\n /**\n * Adds a value to the accumulator and returns the accumulator. It is\n * called with the values that apply to a selection, in order of\n * precedence: the value of each variant's selected option, in the order\n * of `variants`, then the value of each matching compound variant, in the\n * order of `compoundVariants`. An option or compound variant whose value\n * is `undefined` adds nothing.\n */\n readonly reduce: (accumulator: Accumulator, value: Value) => Accumulator;\n /**\n * Turns the accumulator into the result, for example by freezing it.\n * Without it, the result is the accumulator.\n */\n readonly finish?: ((accumulator: Accumulator) => Result) | undefined;\n /**\n * Whether a recipe builds the result of each declared selection once and\n * returns it again for the same selection, unless its config sets\n * `cache`. Defaults to `true`.\n */\n readonly cache?: boolean | undefined;\n}\n\n/**\n * Any selection, for variants whose names are not known at compile time,\n * such as `Record<string, Record<string, string>>`.\n */\ntype AnySelection = Readonly<Record<string, unknown>>;\n\n/** The selection of a recipe, or any selection for unknown variant names. */\ntype KindSelection<\n Variants,\n DefaultedName extends keyof Variants,\n> = string extends keyof Variants\n ? AnySelection\n : VariantSelection<Variants, DefaultedName>;\n\n/** The condition of a compound variant, or any for unknown variant names. */\ntype KindCompoundCondition<Variants> = string extends keyof Variants\n ? AnySelection\n : CompoundCondition<Variants>;\n\n/** The default variants of a recipe, or any for unknown variant names. */\ntype KindDefaultVariants<\n Variants,\n DefaultedName extends keyof Variants,\n> = string extends keyof Variants\n ? AnySelection\n : DefaultVariants<Variants, DefaultedName>;\n\n/**\n * The variants of a {@link KindRecipeConfig}: for each variant name, the\n * value of each of its options.\n *\n * @typeParam Value - The value of an option.\n */\ntype KindVariants<Value> = LooseVariants<Value>;\n\n/**\n * A value added when several variants have particular options at the same\n * time.\n *\n * @typeParam Variants - The variant definitions of the recipe.\n * @typeParam Value - The value of an option.\n */\ninterface KindCompoundVariant<Variants, Value> {\n /**\n * The options that must all be selected for\n * {@link KindCompoundVariant.value} to apply.\n */\n readonly variants: KindCompoundCondition<Variants>;\n /** The value added when the condition matches. */\n readonly value: Value;\n}\n\n/**\n * The configuration of a recipe made from a {@link RecipeKind}.\n *\n * @typeParam Value - The value of an option.\n * @typeParam Variants - The variant definitions, keyed by variant name.\n * @typeParam DefaultedName - The names of the variants that have a default.\n */\ninterface KindRecipeConfig<\n Value,\n Variants extends KindVariants<Value>,\n DefaultedName extends keyof Variants,\n> {\n /** The value that the values of every selection are added to. */\n readonly base?: Value | undefined;\n /** For each variant name, the value of each of its options. */\n readonly variants: Variants;\n /**\n * Values added when several variants have particular options at the same\n * time, applied in order after the values of the variants' options.\n */\n readonly compoundVariants?:\n readonly KindCompoundVariant<NoInfer<Variants>, Value>[] | undefined;\n /** The option each variant uses when the recipe is called without it. */\n readonly defaultVariants?:\n KindDefaultVariants<Variants, DefaultedName> | undefined;\n /** Whether the recipe caches its results. Defaults to the kind's `cache`. */\n readonly cache?: boolean | undefined;\n}\n\n/**\n * A recipe made from a {@link RecipeKind}: a function that returns the\n * result of a selection of variants, with the names of those variants in\n * `variantKeys`.\n *\n * @typeParam Selection - The variants the recipe accepts.\n * @typeParam Result - What the recipe returns.\n */\ntype KindRecipe<Selection, Result> = RecipeFunction<Selection, Result> & {\n /**\n * The names of the recipe's variants, in the order of\n * `Object.keys(config.variants)`.\n */\n readonly variantKeys: readonly VariantKey<Selection>[];\n};\n\n/**\n * Creates recipes of one kind, the function that {@link createRecipeKind}\n * returns.\n *\n * @typeParam Value - The value of an option.\n * @typeParam Result - What a recipe of this kind returns.\n * @param config - The base value, variants, compound variants, and default\n * variants of the recipe, and whether it caches its results.\n * @returns The recipe.\n */\ntype CreateKindRecipe<Value, Result> = <\n const Variants extends KindVariants<Value>,\n const DefaultedName extends keyof Variants = never,\n>(\n config: KindRecipeConfig<Value, Variants, DefaultedName>,\n) => KindRecipe<KindSelection<Variants, DefaultedName>, Result>;\n\ninterface LooseRecipeKind {\n readonly initial: (base: unknown) => unknown;\n readonly reduce: (accumulator: unknown, value: unknown) => unknown;\n readonly finish?: ((accumulator: unknown) => unknown) | undefined;\n readonly cache?: boolean | undefined;\n}\n\ninterface LooseKindRecipeConfig {\n readonly base?: unknown;\n readonly variants: KindVariants<unknown>;\n readonly compoundVariants?:\n | readonly {\n readonly variants: SelectedVariants;\n readonly value: unknown;\n }[]\n | undefined;\n readonly defaultVariants?: SelectedVariants | undefined;\n readonly cache?: boolean | undefined;\n}\n\ntype LooseKindRecipe = WithVariantKeys<\n (selection?: SelectedVariants | null) => unknown\n>;\n\n/**\n * Creates a kind of recipe from how it turns the values of a selection into\n * its result, and returns the function that creates recipes of that kind.\n * A recipe maps a selection of variants to a result, as `cva` of\n * `@lynstack/class-recipe` does for class names, for values of any type,\n * such as style objects.\n *\n * @remarks\n * For each selection, a recipe reduces the value of each variant's selected\n * option and the value of each matching compound variant, with\n * `kind.reduce`, into the accumulator that `kind.initial` returns for the\n * recipe's base, then passes it to `kind.finish`. A variant whose only\n * options are `\"true\"` and `\"false\"` defaults to `false`, an option that the\n * config does not declare adds no value, and properties of the selection\n * that are not variants are ignored. A recipe's `variantKeys` property lists\n * the names of its variants.\n *\n * With the cache, a recipe builds the result of each declared selection\n * once, and calling it again with the same variants returns the same\n * result. Freeze an object result in `kind.finish` so that callers cannot\n * change a result that later calls share. A selection with an undeclared\n * option is built on every call. A recipe keeps up to one result for each\n * combination of declared options; set `cache: false` in the config of a\n * recipe whose variants come from untrusted input, which overrides the kind.\n *\n * When the variant names are not known at compile time, as in a library\n * that passes on a config it received, a recipe accepts any selection.\n *\n * @typeParam Value - The value of an option, inferred from the `value`\n * parameter of `kind.reduce` or the `base` parameter of `kind.initial`.\n * @typeParam Accumulator - What the values are reduced to, inferred from\n * `kind.initial`.\n * @typeParam Result - What a recipe returns, inferred from `kind.finish`,\n * or the accumulator without it.\n * @param kind - How a recipe turns the values of a selection into its\n * result, and whether it caches the result.\n * @returns The function that creates recipes of this kind.\n *\n * @example\n * ```ts\n * type Style = Readonly<Record<string, string | number>>;\n *\n * const styleRecipe = createRecipeKind({\n * initial: (base?: Style): Record<string, string | number> => ({ ...base }),\n * reduce: (style, value: Style) => Object.assign(style, value),\n * finish: (style): Style => Object.freeze(style),\n * });\n *\n * const text = styleRecipe({\n * base: { color: \"black\" },\n * variants: {\n * size: { sm: { fontSize: 12 }, lg: { fontSize: 24 } },\n * muted: { true: { opacity: 0.6 } },\n * },\n * compoundVariants: [\n * { variants: { size: \"lg\", muted: true }, value: { fontWeight: 300 } },\n * ],\n * defaultVariants: { size: \"sm\" },\n * });\n *\n * text(); // => { color: \"black\", fontSize: 12 }\n *\n * text({ size: \"lg\", muted: true });\n * // => { color: \"black\", fontSize: 24, opacity: 0.6, fontWeight: 300 }\n *\n * text.variantKeys; // => [\"size\", \"muted\"]\n * ```\n */\nfunction createRecipeKind<Value, Accumulator, Result = Accumulator>(\n kind: RecipeKind<Value, Accumulator, Result>,\n): CreateKindRecipe<Value, Result>;\n\nfunction createRecipeKind(kind: LooseRecipeKind): unknown {\n const { initial, reduce, finish } = kind;\n const kindCache = kind.cache ?? true;\n\n return (config: LooseKindRecipeConfig): LooseKindRecipe => {\n const { base } = config;\n const compiled = compileVariants<unknown>({\n compoundVariants: config.compoundVariants ?? [],\n defaultVariants: config.defaultVariants ?? {},\n noValue: undefined,\n variants: config.variants,\n });\n const reducer = { initial: (): unknown => initial(base), reduce };\n const build =\n finish === undefined\n ? (indexes: Int32Array): unknown =>\n reduceValues(compiled, indexes, reducer)\n : (indexes: Int32Array): unknown =>\n finish(reduceValues(compiled, indexes, reducer));\n const options = { cache: config.cache ?? kindCache };\n return withVariantKeys(createSelector(compiled, build, options), compiled);\n };\n}\n\nexport { createRecipeKind };\nexport type {\n CreateKindRecipe,\n KindCompoundCondition,\n KindCompoundVariant,\n KindDefaultVariants,\n KindRecipe,\n KindRecipeConfig,\n KindSelection,\n KindVariants,\n RecipeKind,\n};\n","import type { CompiledVariants, Compound } from \"./variants.js\";\nimport { matches } from \"./reduce-values.js\";\nimport { noOption } from \"./variants.js\";\n\n/** Values for some slots, keyed by slot name, as the runtime takes them. */\ntype LooseSlotValues = Readonly<Record<string, unknown>>;\n\n/** How the values of each slot are reduced, as a recipe kind gives it. */\ninterface SlotsKind {\n readonly initial: (base: unknown) => unknown;\n readonly reduce: (accumulator: unknown, value: unknown) => unknown;\n readonly finish?: ((accumulator: unknown) => unknown) | undefined;\n}\n\n/** What a slot recipe's config gives for its slots. */\ninterface SlotsConfig {\n readonly slots: readonly string[];\n readonly base?: LooseSlotValues | undefined;\n}\n\n/**\n * The values that one option or compound variant adds to its slots: the\n * index of each slot it gives a value, and that value at the same position.\n * Slots without a value are left out, so reducing skips them.\n */\ninterface SlotEntries {\n readonly slots: readonly number[];\n readonly values: readonly unknown[];\n}\n\n/**\n * The accumulator of each slot while a selection is reduced, which\n * {@link addEntries} updates in place.\n */\ninterface SlotAccumulators {\n readonly length: number;\n [slot: number]: unknown;\n}\n\nfunction valueOfSlot(\n values: LooseSlotValues | undefined,\n slot: string,\n): unknown {\n return values !== undefined && Object.hasOwn(values, slot)\n ? values[slot]\n : undefined;\n}\n\n/** Returns the entries of `values`, or undefined when no slot has one. */\nfunction entriesOf(\n slots: readonly string[],\n values: LooseSlotValues | undefined,\n): SlotEntries | undefined {\n const slotIndexes: number[] = [];\n const slotValues: unknown[] = [];\n for (const [index, slot] of slots.entries()) {\n const value = valueOfSlot(values, slot);\n if (value !== undefined) {\n slotIndexes.push(index);\n slotValues.push(value);\n }\n }\n return slotIndexes.length === 0\n ? undefined\n : { slots: slotIndexes, values: slotValues };\n}\n\n/** Turns the slot values of each option and compound variant into entries. */\nfunction compileEntries(\n compiled: CompiledVariants<LooseSlotValues | undefined>,\n slots: readonly string[],\n): CompiledVariants<SlotEntries | undefined> {\n return {\n ...compiled,\n compounds: compiled.compounds\n .map((compound): Compound<SlotEntries | undefined> => ({\n conditions: compound.conditions,\n value: entriesOf(slots, compound.value),\n }))\n .filter((compound) => compound.value !== undefined),\n valuesByIndex: compiled.valuesByIndex.map((valuesOfVariant) =>\n valuesOfVariant.map((values) => entriesOf(slots, values)),\n ),\n };\n}\n\n/** Reduces the values of `added` into the accumulators of their slots. */\nfunction addEntries(\n accumulators: SlotAccumulators,\n added: SlotEntries,\n reduce: SlotsKind[\"reduce\"],\n): void {\n const { slots, values } = added;\n for (let entry = 0; entry < slots.length; entry += 1) {\n const slot = slots[entry] ?? 0;\n accumulators[slot] = reduce(accumulators[slot], values[entry]);\n }\n}\n\n/**\n * The names of a slot recipe's slots, and an object with a property for\n * each, which results copy so that assigning a slot named `__proto__` sets\n * that property instead of the prototype.\n */\ninterface SlotNames {\n readonly names: readonly string[];\n readonly template: Readonly<Record<string, unknown>>;\n}\n\n/** Returns the frozen result of each slot, keyed by slot name. */\nfunction resultsBySlot(\n slots: SlotNames,\n accumulators: SlotAccumulators,\n finish: (accumulator: unknown) => unknown,\n): Readonly<Record<string, unknown>> {\n const { names } = slots;\n const results: Record<string, unknown> = { ...slots.template };\n for (let slot = 0; slot < names.length; slot += 1) {\n results[names[slot] ?? \"\"] = finish(accumulators[slot]);\n }\n return Object.freeze(results);\n}\n\nconst asResult = (accumulator: unknown): unknown => accumulator;\n\n/**\n * Returns the function that builds the result of a selection for each slot\n * of a slot recipe: each slot reduces its own values with `kind`, and the\n * results are frozen in an object keyed by slot name.\n *\n * It reduces the values in a loop of its own instead of through\n * `reduceValues`, which recipes share, so that its calls to `kind.reduce`\n * see only the reducers of slot recipes and can be inlined.\n */\nfunction createSlotsBuilder(\n kind: SlotsKind,\n compiled: CompiledVariants<LooseSlotValues | undefined>,\n config: SlotsConfig,\n): (indexes: Int32Array) => Readonly<Record<string, unknown>> {\n const { initial, reduce, finish = asResult } = kind;\n const names = [...config.slots];\n const slotNames: SlotNames = {\n names,\n template: Object.fromEntries(names.map((slot) => [slot, undefined])),\n };\n const bases = names.map((slot) => valueOfSlot(config.base, slot));\n const { valuesByIndex, compounds } = compileEntries(compiled, names);\n\n return (indexes) => {\n const accumulators = bases.map((base) => initial(base));\n for (let variant = 0; variant < valuesByIndex.length; variant += 1) {\n const added = valuesByIndex[variant]?.[indexes[variant] ?? noOption];\n if (added !== undefined) {\n addEntries(accumulators, added, reduce);\n }\n }\n for (const compound of compounds) {\n if (compound.value !== undefined && matches(compound, indexes)) {\n addEntries(accumulators, compound.value, reduce);\n }\n }\n return resultsBySlot(slotNames, accumulators, finish);\n };\n}\n\nexport { createSlotsBuilder };\nexport type { LooseSlotValues, SlotsKind };\n","import type {\n KindCompoundCondition,\n KindDefaultVariants,\n KindRecipe,\n KindSelection,\n KindVariants,\n RecipeKind,\n} from \"./recipe-kind.js\";\nimport type { LooseSlotValues, SlotsKind } from \"./slots.js\";\nimport type { SelectedVariants, WithVariantKeys } from \"./variants.js\";\nimport { compileVariants, withVariantKeys } from \"./variants.js\";\nimport { createSelector } from \"./selector.js\";\nimport { createSlotsBuilder } from \"./slots.js\";\n\n/**\n * Values for some of a slot recipe's slots, keyed by slot name.\n *\n * @typeParam Slot - The names of the slots.\n * @typeParam Value - The value of a slot.\n */\ntype SlotValues<Slot extends string, Value> = Readonly<\n Partial<Record<Slot, Value | undefined>>\n>;\n\n/**\n * The variants of a {@link KindSlotRecipeConfig}: for each variant name,\n * the values of each slot for each of its options.\n *\n * @typeParam Value - The value of a slot.\n */\ntype KindSlotVariants<Value> = KindVariants<SlotValues<string, Value>>;\n\n/**\n * Rejects the slots of each option's values that `Slot` does not name,\n * unless the option's slot names are not known at compile time.\n */\ntype NoUnknownSlots<Variants, Slot extends string> = {\n readonly [Name in keyof Variants]: {\n readonly [\n Option in keyof Variants[Name]\n ]: string extends keyof Variants[Name][Option]\n ? unknown\n : Readonly<\n Partial<Record<Exclude<keyof Variants[Name][Option], Slot>, never>>\n >;\n };\n};\n\n/**\n * Values added to some slots when several variants have particular options\n * at the same time.\n *\n * @typeParam Variants - The variant definitions of the slot recipe.\n * @typeParam Slot - The names of the slots.\n * @typeParam Value - The value of a slot.\n */\ninterface KindSlotCompoundVariant<Variants, Slot extends string, Value> {\n /**\n * The options that must all be selected for\n * {@link KindSlotCompoundVariant.value} to apply.\n */\n readonly variants: KindCompoundCondition<Variants>;\n /** The value added to each slot when the condition matches. */\n readonly value: SlotValues<Slot, Value>;\n}\n\n/**\n * The configuration of a slot recipe made from a {@link RecipeKind}.\n *\n * @typeParam Slot - The names of the slots.\n * @typeParam Value - The value of a slot.\n * @typeParam Variants - The variant definitions, keyed by variant name.\n * @typeParam DefaultedName - The names of the variants that have a default.\n */\ninterface KindSlotRecipeConfig<\n Slot extends string,\n Value,\n Variants extends KindSlotVariants<Value>,\n DefaultedName extends keyof Variants,\n> {\n /** The names of the slots, in the order of the recipe's result. */\n readonly slots: readonly Slot[];\n /** The value of each slot that the values of every selection are added to. */\n readonly base?: SlotValues<NoInfer<Slot>, Value> | undefined;\n /** For each variant name, the values of each slot for each of its options. */\n readonly variants: Variants & NoUnknownSlots<Variants, NoInfer<Slot>>;\n /**\n * Values added to some slots when several variants have particular\n * options at the same time, applied in order after the values of the\n * variants' options.\n */\n readonly compoundVariants?:\n | readonly KindSlotCompoundVariant<\n NoInfer<Variants>,\n NoInfer<Slot>,\n Value\n >[]\n | undefined;\n /** The option each variant uses when the recipe is called without it. */\n readonly defaultVariants?:\n KindDefaultVariants<Variants, DefaultedName> | undefined;\n /** Whether the recipe caches its results. Defaults to the kind's `cache`. */\n readonly cache?: boolean | undefined;\n}\n\n/**\n * Creates slot recipes of one kind, the function that\n * {@link createSlotRecipeKind} returns.\n *\n * @typeParam Value - The value of a slot.\n * @typeParam Result - What a recipe of this kind returns for each slot.\n * @param config - The slots, base values, variants, compound variants, and\n * default variants of the slot recipe, and whether it caches its results.\n * @returns The slot recipe.\n */\ntype CreateKindSlotRecipe<Value, Result> = <\n const Slot extends string,\n const Variants extends KindSlotVariants<Value>,\n const DefaultedName extends keyof Variants = never,\n>(\n config: KindSlotRecipeConfig<Slot, Value, Variants, DefaultedName>,\n) => KindRecipe<\n KindSelection<Variants, DefaultedName>,\n Readonly<Record<Slot, Result>>\n>;\n\ninterface LooseRecipeKind extends SlotsKind {\n readonly cache?: boolean | undefined;\n}\n\ninterface LooseSlotRecipeConfig {\n readonly slots: readonly string[];\n readonly base?: LooseSlotValues | undefined;\n readonly variants: KindSlotVariants<unknown>;\n readonly compoundVariants?:\n | readonly {\n readonly variants: SelectedVariants;\n readonly value: LooseSlotValues;\n }[]\n | undefined;\n readonly defaultVariants?: SelectedVariants | undefined;\n readonly cache?: boolean | undefined;\n}\n\ntype LooseSlotRecipe = WithVariantKeys<\n (selection?: SelectedVariants | null) => Readonly<Record<string, unknown>>\n>;\n\n/**\n * Creates a kind of slot recipe from how it turns the values of each slot\n * into that slot's result, and returns the function that creates slot\n * recipes of that kind. A slot recipe maps a selection of variants to the\n * result of each of several slots, such as the class names or the styles of\n * the elements of a component, as `sva` of `@lynstack/class-recipe` does.\n *\n * @remarks\n * A slot recipe reduces the values of each slot as a recipe of the same\n * kind from {@link createRecipeKind} would: `kind.initial` returns the\n * accumulator for the slot's base value, `kind.reduce` adds the slot's\n * value of each variant's selected option, in the order of `variants`, and\n * of each matching compound variant, in the order of `compoundVariants`,\n * and `kind.finish` turns the accumulator into the slot's result. A slot\n * without values gets the result of its accumulator for an `undefined`\n * base, so every slot is in the result.\n *\n * The result is a frozen object of each slot's result, keyed by slot name\n * in the order of `slots`. With the cache, a slot recipe builds it once for\n * each declared selection and returns the same object for the same\n * variants. The `cache` of a slot recipe's config overrides the kind's.\n * Variants, default variants, boolean variants, and undeclared\n * options behave as in {@link createRecipeKind}, and the recipe's\n * `variantKeys` property lists the names of its variants.\n *\n * @typeParam Value - The value of a slot, inferred from the `value`\n * parameter of `kind.reduce` or the `base` parameter of `kind.initial`.\n * @typeParam Accumulator - What the values of a slot are reduced to,\n * inferred from `kind.initial`.\n * @typeParam Result - What a slot recipe returns for each slot, inferred\n * from `kind.finish`, or the accumulator without it.\n * @param kind - How a slot recipe turns the values of each slot into its\n * result, and whether it caches its results. The same kind can create\n * recipes with {@link createRecipeKind}.\n * @returns The function that creates slot recipes of this kind.\n *\n * @example\n * ```ts\n * type Style = Readonly<Record<string, string | number>>;\n *\n * const styleKind = {\n * initial: (base?: Style): Record<string, string | number> => ({ ...base }),\n * reduce: (style: Record<string, string | number>, value: Style) =>\n * Object.assign(style, value),\n * finish: (style: Record<string, string | number>): Style =>\n * Object.freeze(style),\n * };\n *\n * const slotStyleRecipe = createSlotRecipeKind(styleKind);\n *\n * const card = slotStyleRecipe({\n * slots: [\"root\", \"title\"],\n * base: { root: { padding: 16 }, title: { fontSize: 18 } },\n * variants: {\n * tone: {\n * light: { root: { backgroundColor: \"white\" } },\n * dark: { root: { backgroundColor: \"black\" }, title: { color: \"white\" } },\n * },\n * },\n * compoundVariants: [\n * { variants: { tone: \"dark\" }, value: { title: { fontWeight: 600 } } },\n * ],\n * defaultVariants: { tone: \"light\" },\n * });\n *\n * card();\n * // => { root: { padding: 16, backgroundColor: \"white\" }, title: { fontSize: 18 } }\n *\n * card({ tone: \"dark\" });\n * // => {\n * // root: { padding: 16, backgroundColor: \"black\" },\n * // title: { fontSize: 18, color: \"white\", fontWeight: 600 },\n * // }\n *\n * card.variantKeys; // => [\"tone\"]\n * ```\n */\nfunction createSlotRecipeKind<Value, Accumulator, Result = Accumulator>(\n kind: RecipeKind<Value, Accumulator, Result>,\n): CreateKindSlotRecipe<Value, Result>;\n\nfunction createSlotRecipeKind(kind: LooseRecipeKind): unknown {\n const kindCache = kind.cache ?? true;\n\n return (config: LooseSlotRecipeConfig): LooseSlotRecipe => {\n const compiled = compileVariants<LooseSlotValues | undefined>({\n compoundVariants: config.compoundVariants ?? [],\n defaultVariants: config.defaultVariants ?? {},\n noValue: undefined,\n variants: config.variants,\n });\n const build = createSlotsBuilder(kind, compiled, config);\n const options = { cache: config.cache ?? kindCache };\n return withVariantKeys(createSelector(compiled, build, options), compiled);\n };\n}\n\nexport { createSlotRecipeKind };\nexport type {\n CreateKindSlotRecipe,\n KindSlotCompoundVariant,\n KindSlotRecipeConfig,\n KindSlotVariants,\n SlotValues,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["listOf","asResult"],"sources":["../src/variants.ts","../src/reduce-values.ts","../src/compose.ts","../src/build-recipe.ts","../src/selector.ts","../src/recipe-kind.ts","../src/slots.ts","../src/slot-recipe-kind.ts"],"sourcesContent":["/**\n * Variant definitions in the loose shape the runtime works with: for each\n * variant name, the value of each of its options.\n */\ntype LooseVariants<Value> = Readonly<\n Record<string, Readonly<Record<string, Value>>>\n>;\n\n/** A selection in the loose shape the runtime works with. */\ntype SelectedVariants = Readonly<Record<string, unknown>>;\n\n/** A compound variant in the loose shape the runtime works with. */\ninterface LooseCompoundVariant<Value> {\n readonly variants: SelectedVariants;\n readonly value: Value;\n}\n\n/** The variant's index and the option indexes that a condition matches. */\ntype CompoundCondition = readonly [number, readonly number[]];\n\n/** A compound variant prepared for matching. */\ninterface Compound<Value> {\n readonly conditions: readonly CompoundCondition[];\n readonly value: Value;\n}\n\n/** What {@link compileVariants} prepares. */\ninterface VariantsConfig<Value> {\n readonly variants: LooseVariants<Value>;\n readonly defaultVariants: SelectedVariants;\n readonly compoundVariants: readonly LooseCompoundVariant<Value>[];\n /**\n * The value of a variant without a selected option, and of a boolean\n * option that the variant does not declare.\n */\n readonly noValue: Value;\n}\n\n/**\n * Variants prepared for selecting values. Each variant numbers its options\n * from 1, and 0 stands for no option, so a selection is a list of option\n * indexes. Read together as the digits of a mixed-radix number, they form\n * the selection's key.\n */\ninterface CompiledVariants<Value> {\n readonly names: readonly string[];\n readonly defaultOptions: readonly (string | undefined)[];\n readonly indexByOption: readonly ReadonlyMap<string, number>[];\n /** The value of each variant's options, by option index. */\n readonly valuesByIndex: readonly (readonly Value[])[];\n readonly strides: readonly number[];\n readonly compounds: readonly Compound<Value>[];\n /** Whether every selection's key is a safe integer. */\n readonly isCacheable: boolean;\n}\n\n/** The option index of a variant without a selected option. */\nconst noOption = 0;\n\n/** The key of a selection with an option that a variant does not declare. */\nconst undeclared = -1;\n\nconst noProps: SelectedVariants = Object.freeze({});\n\n/**\n * Prepares variants for selecting values. A variant that declares an option\n * named `\"true\"` or `\"false\"` also declares the other one, with\n * `config.noValue`, and a variant whose only options are those defaults to\n * `\"false\"`. A compound variant that names an undeclared variant or option,\n * or lists no option for a variant, never matches and is left out.\n */\nfunction compileVariants<Value>(\n config: VariantsConfig<Value>,\n): CompiledVariants<Value> {\n const names = Object.keys(config.variants);\n const optionValues = names.map((name) =>\n withBooleanOptions(config.variants[name] ?? {}, config.noValue),\n );\n const indexByOption: readonly ReadonlyMap<string, number>[] =\n optionValues.map(\n (valuesByOption) =>\n new Map(\n Object.keys(valuesByOption).map((option, index) => [\n option,\n index + 1,\n ]),\n ),\n );\n const radixes = indexByOption.map((indexes) => indexes.size + 1);\n\n return {\n compounds: compileCompounds(config.compoundVariants, names, indexByOption),\n defaultOptions: names.map(\n (name, index) =>\n toOptionName(config.defaultVariants[name]) ??\n (isBooleanVariant(indexByOption[index]) ? \"false\" : undefined),\n ),\n indexByOption,\n isCacheable: product(radixes) <= Number.MAX_SAFE_INTEGER,\n names,\n strides: radixes.map((_radix, index) => product(radixes.slice(0, index))),\n valuesByIndex: valuesByIndexOf(optionValues, config.noValue),\n };\n}\n\n/**\n * Writes the option index of each variant for `selected` into `indexes`\n * and returns the selection's key, or {@link undeclared} when an option is\n * not declared.\n */\nfunction select(\n compiled: CompiledVariants<unknown>,\n selected: SelectedVariants,\n indexes: Int32Array,\n): number {\n let key = 0;\n for (let variant = 0; variant < compiled.names.length; variant += 1) {\n const index = selectIndex(compiled, selected, variant);\n indexes[variant] = index ?? noOption;\n key =\n index === undefined || key === undeclared\n ? undeclared\n : key + index * (compiled.strides[variant] ?? 0);\n }\n return key;\n}\n\nfunction selectIndex(\n compiled: CompiledVariants<unknown>,\n selected: SelectedVariants,\n variant: number,\n): number | undefined {\n const option =\n toOptionName(selected[compiled.names[variant] ?? \"\"]) ??\n compiled.defaultOptions[variant];\n return option === undefined\n ? noOption\n : compiled.indexByOption[variant]?.get(option);\n}\n\nfunction valuesByIndexOf<Value>(\n optionValues: readonly Readonly<Record<string, Value>>[],\n noValue: Value,\n): Value[][] {\n const valuesByIndex: Value[][] = [];\n for (const valuesByOption of optionValues) {\n valuesByIndex.push([noValue, ...Object.values(valuesByOption)]);\n }\n return valuesByIndex;\n}\n\nfunction product(numbers: readonly number[]): number {\n return numbers.reduce((result, number) => result * number, 1);\n}\n\nfunction withBooleanOptions<Value>(\n valuesByOption: Readonly<Record<string, Value>>,\n noValue: Value,\n): Readonly<Record<string, Value>> {\n const optionNames = Object.keys(valuesByOption);\n if (!optionNames.some((option) => isBooleanName(option))) {\n return valuesByOption;\n }\n return { false: noValue, true: noValue, ...valuesByOption };\n}\n\nfunction compileCompounds<Value>(\n compoundVariants: readonly LooseCompoundVariant<Value>[],\n names: readonly string[],\n indexByOption: readonly ReadonlyMap<string, number>[],\n): Compound<Value>[] {\n return compoundVariants.flatMap(({ variants, value }) => {\n const conditions = Object.keys(variants)\n .filter((name) => variants[name] !== undefined)\n .map((name) =>\n compileCondition(indexByOption, names.indexOf(name), variants[name]),\n );\n return conditions.every(([, indexes]) => indexes.length > 0)\n ? [{ conditions, value }]\n : [];\n });\n}\n\nfunction compileCondition(\n indexByOption: readonly ReadonlyMap<string, number>[],\n variant: number,\n value: unknown,\n): CompoundCondition {\n const indexes = indexByOption[variant];\n return [\n variant,\n toOptionNames(value)\n .map((option) => indexes?.get(option))\n .filter((index) => index !== undefined),\n ];\n}\n\nfunction isBooleanVariant(\n indexByOption: ReadonlyMap<string, number> | undefined,\n): boolean {\n return (\n indexByOption !== undefined &&\n indexByOption.size > 0 &&\n [...indexByOption.keys()].every((option) => isBooleanName(option))\n );\n}\n\nfunction toOptionName(value: unknown): string | undefined {\n if (typeof value === \"string\") {\n return value;\n }\n return typeof value === \"number\" || typeof value === \"boolean\"\n ? String(value)\n : undefined;\n}\n\nfunction toOptionNames(value: unknown): readonly string[] {\n const values: readonly unknown[] = Array.isArray(value) ? value : [value];\n return values\n .map((option) => toOptionName(option))\n .filter((option) => option !== undefined);\n}\n\n/** A recipe function with the names of its variants. */\ntype WithVariantKeys<Recipe> = Recipe & {\n readonly variantKeys: readonly string[];\n};\n\n/** Adds the names of the compiled variants to `recipe` as `variantKeys`. */\nfunction withVariantKeys<Recipe extends object>(\n recipe: Recipe,\n compiled: CompiledVariants<unknown>,\n): WithVariantKeys<Recipe> {\n return Object.assign(recipe, {\n variantKeys: Object.freeze([...compiled.names]),\n });\n}\n\nfunction isBooleanName(option: string): boolean {\n return option === \"true\" || option === \"false\";\n}\n\nexport {\n compileVariants,\n noOption,\n noProps,\n select,\n undeclared,\n withVariantKeys,\n};\nexport type {\n CompiledVariants,\n Compound,\n LooseCompoundVariant,\n LooseVariants,\n SelectedVariants,\n WithVariantKeys,\n};\n","import type { CompiledVariants, Compound } from \"./variants.js\";\nimport { noOption } from \"./variants.js\";\n\n/** Whether every condition of `compound` matches the selected indexes. */\nfunction matches(compound: Compound<unknown>, indexes: Int32Array): boolean {\n for (const [variant, matchingIndexes] of compound.conditions) {\n if (!matchingIndexes.includes(indexes[variant] ?? noOption)) {\n return false;\n }\n }\n return true;\n}\n\n/** Reduces the values of a selection to an accumulator. */\ninterface Reducer<Value, Accumulator> {\n /** Returns the accumulator to start from. */\n readonly initial: () => Accumulator;\n /** Adds a value to the accumulator and returns the accumulator. */\n readonly reduce: (accumulator: Accumulator, value: Value) => Accumulator;\n}\n\n/**\n * Reduces the values that apply to a selection, in order of precedence: the\n * value of each variant's option, then the value of each matching compound\n * variant. A value that is undefined adds nothing.\n */\nfunction reduceValues<Value, Accumulator>(\n compiled: CompiledVariants<Value | undefined>,\n indexes: Int32Array,\n reducer: Reducer<Value, Accumulator>,\n): Accumulator {\n const { reduce } = reducer;\n let accumulator = reducer.initial();\n for (let variant = 0; variant < compiled.valuesByIndex.length; variant += 1) {\n const value =\n compiled.valuesByIndex[variant]?.[indexes[variant] ?? noOption];\n if (value !== undefined) {\n accumulator = reduce(accumulator, value);\n }\n }\n for (const compound of compiled.compounds) {\n if (compound.value !== undefined && matches(compound, indexes)) {\n accumulator = reduce(accumulator, compound.value);\n }\n }\n return accumulator;\n}\n\n/**\n * Reduces the values that apply to a selection, as {@link reduceValues}\n * does, when each option and compound variant has a list of values, which\n * are reduced in order.\n */\nfunction reduceValueLists<Value, Accumulator>(\n compiled: CompiledVariants<readonly Value[]>,\n indexes: Int32Array,\n reducer: Reducer<Value, Accumulator>,\n): Accumulator {\n const { reduce } = reducer;\n let accumulator = reducer.initial();\n for (let variant = 0; variant < compiled.valuesByIndex.length; variant += 1) {\n const values =\n compiled.valuesByIndex[variant]?.[indexes[variant] ?? noOption];\n accumulator = reduceEach(accumulator, values ?? [], reduce);\n }\n for (const compound of compiled.compounds) {\n if (matches(compound, indexes)) {\n accumulator = reduceEach(accumulator, compound.value, reduce);\n }\n }\n return accumulator;\n}\n\n/** Reduces `values` into `accumulator`, in order. */\nfunction reduceEach<Value, Accumulator>(\n accumulator: Accumulator,\n values: readonly Value[],\n reduce: Reducer<Value, Accumulator>[\"reduce\"],\n): Accumulator {\n let result = accumulator;\n for (const value of values) {\n result = reduce(result, value);\n }\n return result;\n}\n\nexport { matches, reduceEach, reduceValueLists, reduceValues };\n","import type {\n LooseCompoundVariant,\n LooseVariants,\n SelectedVariants,\n} from \"./variants.js\";\n\n/** Whether a recipe has slots, which decides the recipes it composes. */\ntype RecipeType = \"recipe\" | \"slot recipe\";\n\n/**\n * The values of one config that a recipe is made of: its own config, or\n * that of a recipe it composes.\n */\ninterface Layer<Value> {\n readonly slots: readonly string[];\n readonly base: Value | undefined;\n readonly variants: LooseVariants<Value>;\n readonly compoundVariants: readonly LooseCompoundVariant<Value>[];\n readonly defaultVariants: SelectedVariants;\n}\n\n/** What a recipe's config gives for its layer. */\ninterface LayerConfig<Value> {\n readonly base?: Value | undefined;\n readonly variants: LooseVariants<Value>;\n readonly compoundVariants?:\n readonly LooseCompoundVariant<Value>[] | undefined;\n readonly defaultVariants?: SelectedVariants | undefined;\n}\n\n/**\n * Returns the layer of a recipe's own config, with copies of its records,\n * so that changing the config later changes no recipe that composes it.\n */\nfunction layerOf<Value>(\n config: LayerConfig<Value>,\n slots: readonly string[],\n): Layer<Value> {\n return {\n base: config.base,\n compoundVariants: (config.compoundVariants ?? []).map(\n ({ variants, value }) => ({ value, variants: { ...variants } }),\n ),\n defaultVariants: { ...config.defaultVariants },\n slots: [...slots],\n variants: Object.fromEntries(\n Object.entries(config.variants).map(\n ([name, options]: readonly [\n string,\n Readonly<Record<string, Value>>,\n ]) => [name, { ...options }],\n ),\n ),\n };\n}\n\n/** The layers of the recipes of one type, which the engine created. */\ninterface Registry<Value> {\n /**\n * Returns the layers of a recipe: those of each recipe it composes, in\n * order, each only at its first occurrence, then its own.\n *\n * @throws TypeError when a recipe it composes is not in the registry.\n */\n readonly layersOf: (\n composes: readonly unknown[],\n own: Layer<Value>,\n ) => readonly Layer<Value>[];\n /** Records the layers of `recipe`, so that other recipes can compose it. */\n readonly withLayers: <Recipe extends object>(\n recipe: Recipe,\n layers: readonly Layer<Value>[],\n ) => Recipe;\n}\n\n/** Returns an empty registry of recipes of type `type`. */\nfunction createRegistry<Value>(type: RecipeType): Registry<Value> {\n const layersByRecipe = new WeakMap<object, readonly Layer<Value>[]>();\n\n const layersOfRecipe = (recipe: unknown): readonly Layer<Value>[] => {\n const layers =\n typeof recipe === \"function\" ? layersByRecipe.get(recipe) : undefined;\n if (layers === undefined) {\n throw new TypeError(\n `A ${type} composes only ${type}s created by @lynstack/recipe. ` +\n \"Check that the app installs one copy of @lynstack/recipe.\",\n );\n }\n return layers;\n };\n\n return {\n layersOf: (composes, own) => [\n ...new Set([\n ...composes.flatMap((recipe) => layersOfRecipe(recipe)),\n own,\n ]),\n ],\n withLayers: (recipe, layers) => {\n layersByRecipe.set(recipe, layers);\n return recipe;\n },\n };\n}\n\n/**\n * The layers of a recipe merged into one config, in which every option and\n * compound variant has the list of its values, in the order of the layers.\n */\ninterface MergedLayers<Value> {\n /** The slots of every layer, each once, in order. */\n readonly slots: readonly string[];\n /** The base of each layer that has one. */\n readonly bases: readonly Value[];\n readonly variants: LooseVariants<readonly Value[]>;\n readonly compoundVariants: readonly LooseCompoundVariant<readonly Value[]>[];\n readonly defaultVariants: SelectedVariants;\n}\n\n/**\n * Merges layers into one config: the union of their slots, of their\n * variants, and of the options of each, the values of each option in the\n * order of the layers, their compound variants in order, and the default\n * of each variant from the last layer that gives one.\n */\nfunction mergeLayers<Value>(\n layers: readonly Layer<Value>[],\n): MergedLayers<Value> {\n const variantNames = unique(\n layers.flatMap((layer) => Object.keys(layer.variants)),\n );\n return {\n bases: layers.flatMap(({ base }) => listOf(base)),\n compoundVariants: layers.flatMap((layer) =>\n layer.compoundVariants.map(({ variants, value }) => ({\n value: listOf(value),\n variants,\n })),\n ),\n defaultVariants: Object.fromEntries(\n layers.flatMap((layer) =>\n Object.entries(layer.defaultVariants).filter(\n ([, option]: readonly [string, unknown]) => option !== undefined,\n ),\n ),\n ),\n slots: unique(layers.flatMap(({ slots }) => slots)),\n variants: Object.fromEntries(\n variantNames.map((name) => [name, mergeOptions(layers, name)]),\n ),\n };\n}\n\n/** The options of the variant `name` in any layer, with their values. */\nfunction mergeOptions<Value>(\n layers: readonly Layer<Value>[],\n name: string,\n): Readonly<Record<string, readonly Value[]>> {\n const optionsOfLayers = layers.map(\n (layer) => ownValue(layer.variants, name) ?? {},\n );\n const optionNames = unique(\n optionsOfLayers.flatMap((options) => Object.keys(options)),\n );\n return Object.fromEntries(\n optionNames.map((option) => [\n option,\n optionsOfLayers.flatMap((options) => listOf(ownValue(options, option))),\n ]),\n );\n}\n\n/**\n * Combines values, none of them undefined, into one with `combine`, in\n * order, or returns undefined when there are none.\n */\nfunction combineValues<Value>(\n values: readonly Value[],\n combine: (first: Value, second: Value) => Value,\n): Value | undefined {\n let combined: Value | undefined = undefined;\n for (const value of values) {\n combined = combined === undefined ? value : combine(combined, value);\n }\n return combined;\n}\n\nfunction ownValue<Value>(\n record: Readonly<Record<string, Value>>,\n key: string,\n): Value | undefined {\n return Object.hasOwn(record, key) ? record[key] : undefined;\n}\n\nfunction listOf<Value>(value: Value | undefined): readonly Value[] {\n return value === undefined ? [] : [value];\n}\n\nfunction unique(names: readonly string[]): readonly string[] {\n return [...new Set(names)];\n}\n\nexport { combineValues, createRegistry, layerOf, mergeLayers };\nexport type { Layer, MergedLayers, Registry };\n","import type { CompiledVariants, SelectedVariants } from \"./variants.js\";\nimport type { Layer, MergedLayers } from \"./compose.js\";\nimport { reduceEach, reduceValueLists, reduceValues } from \"./reduce-values.js\";\nimport type { KindVariants } from \"./recipe-kind.js\";\nimport { combineValues } from \"./compose.js\";\nimport { compileVariants } from \"./variants.js\";\n\n/** A recipe kind in the loose shape the runtime works with. */\ninterface LooseRecipeKind {\n readonly initial: (base: unknown) => unknown;\n readonly reduce: (accumulator: unknown, value: unknown) => unknown;\n readonly combine?: ((first: unknown, second: unknown) => unknown) | undefined;\n readonly finish?: ((accumulator: unknown) => unknown) | undefined;\n readonly cache?: boolean | undefined;\n}\n\ninterface LooseKindRecipeConfig {\n readonly composes?: readonly unknown[] | undefined;\n readonly base?: unknown;\n readonly variants: KindVariants<unknown>;\n readonly compoundVariants?:\n | readonly {\n readonly variants: SelectedVariants;\n readonly value: unknown;\n }[]\n | undefined;\n readonly defaultVariants?: SelectedVariants | undefined;\n readonly cache?: boolean | undefined;\n}\n\n/** The variants of a recipe, and how it builds the result of a selection. */\ninterface CompiledRecipe {\n readonly compiled: CompiledVariants<unknown>;\n readonly build: (indexes: Int32Array) => unknown;\n}\n\nconst asResult = (accumulator: unknown): unknown => accumulator;\n\nconst noValues: readonly unknown[] = Object.freeze([]);\n\nconst firstValue = (values: readonly unknown[]): unknown => values[0];\n\n/** Compiles a recipe that composes no other recipe. */\nfunction compileLayer(\n kind: LooseRecipeKind,\n layer: Layer<unknown>,\n): CompiledRecipe {\n const { initial, reduce, finish } = kind;\n const { base } = layer;\n const compiled = compileVariants<unknown>({ ...layer, noValue: undefined });\n const reducer = { initial: (): unknown => initial(base), reduce };\n const build =\n finish === undefined\n ? (indexes: Int32Array): unknown =>\n reduceValues(compiled, indexes, reducer)\n : (indexes: Int32Array): unknown =>\n finish(reduceValues(compiled, indexes, reducer));\n return { build, compiled };\n}\n\n/**\n * Compiles a recipe from the merged layers of the recipes it composes and\n * its own. With `kind.combine`, it combines the bases and the values of\n * each option and compiles them as one config. Without it, its accumulator\n * starts from the first base, and the other bases are reduced into it\n * first; when no option or compound variant has more than one value, it\n * compiles them as one config.\n */\nfunction compileMergedLayers(\n kind: LooseRecipeKind,\n merged: MergedLayers<unknown>,\n): CompiledRecipe {\n const { combine } = kind;\n if (combine !== undefined) {\n const combined = (values: readonly unknown[]): unknown =>\n combineValues(values, combine);\n return compileLayer(\n kind,\n layerOfMerged(merged, combined(merged.bases), combined),\n );\n }\n const [base, ...otherBases] = merged.bases;\n const kindOfBases =\n otherBases.length === 0\n ? kind\n : {\n ...kind,\n initial: (): unknown =>\n reduceEach(kind.initial(base), otherBases, kind.reduce),\n };\n return isSingleValued(merged)\n ? compileLayer(kindOfBases, layerOfMerged(merged, base, firstValue))\n : compileValueLists(kindOfBases, merged, base);\n}\n\n/** Whether no option or compound variant has more than one value. */\nfunction isSingleValued(merged: MergedLayers<unknown>): boolean {\n const optionValues = Object.values(merged.variants).flatMap((options) =>\n Object.values(options),\n );\n return (\n optionValues.every((values) => hasOneValueAtMost(values)) &&\n merged.compoundVariants.every(({ value }) => hasOneValueAtMost(value))\n );\n}\n\n/**\n * Returns the merged layers as one config with `base`, the value of each\n * option and compound variant made by `valueOf` from its values.\n */\nfunction layerOfMerged(\n merged: MergedLayers<unknown>,\n base: unknown,\n valueOf: (values: readonly unknown[]) => unknown,\n): Layer<unknown> {\n return {\n base,\n compoundVariants: merged.compoundVariants.map(({ variants, value }) => ({\n value: valueOf(value),\n variants,\n })),\n defaultVariants: merged.defaultVariants,\n slots: [],\n variants: Object.fromEntries(\n Object.entries(merged.variants).map(\n ([name, options]: readonly [\n string,\n Readonly<Record<string, readonly unknown[]>>,\n ]) => [\n name,\n Object.fromEntries(\n Object.entries(options).map(\n ([option, values]: readonly [string, readonly unknown[]]) => [\n option,\n valueOf(values),\n ],\n ),\n ),\n ],\n ),\n ),\n };\n}\n\nfunction hasOneValueAtMost(values: readonly unknown[]): boolean {\n return values.length <= 1;\n}\n\n/** Compiles a recipe whose options or compound variants have value lists. */\nfunction compileValueLists(\n kind: LooseRecipeKind,\n merged: MergedLayers<unknown>,\n base: unknown,\n): CompiledRecipe {\n const { initial, reduce, finish = asResult } = kind;\n const compiled = compileVariants<readonly unknown[]>({\n ...merged,\n noValue: noValues,\n });\n const reducer = { initial: (): unknown => initial(base), reduce };\n const build = (indexes: Int32Array): unknown =>\n finish(reduceValueLists(compiled, indexes, reducer));\n return { build, compiled };\n}\n\nexport { compileLayer, compileMergedLayers };\nexport type { LooseKindRecipeConfig, LooseRecipeKind };\n","import type { CompiledVariants, SelectedVariants } from \"./variants.js\";\nimport { noProps, select, undeclared } from \"./variants.js\";\n\n/** How a selector computes its results. */\ninterface SelectorOptions {\n /** Whether to cache the result of each declared selection. */\n readonly cache: boolean;\n}\n\n/** Whether `results` holds `result`, read at `key`, even if it is undefined. */\nfunction isCached<Result>(\n results: ReadonlyMap<number, Result>,\n key: number,\n result: Result | undefined,\n): result is Result {\n return result !== undefined || results.has(key);\n}\n\n/**\n * Returns a function that builds the result of a selection from the option\n * index of each variant, and treats a missing selection as an empty one.\n * With `options.cache`, it builds the result of each declared selection\n * once and caches it by the selection's key.\n */\nfunction createSelector<Result>(\n compiled: CompiledVariants<unknown>,\n build: (indexes: Int32Array) => Result,\n options: SelectorOptions,\n): (selected?: SelectedVariants | null) => Result {\n const indexes = new Int32Array(compiled.names.length);\n const results =\n options.cache && compiled.isCacheable\n ? new Map<number, Result>()\n : undefined;\n\n return (selected) => {\n const key = select(compiled, selected ?? noProps, indexes);\n if (key !== undeclared && results !== undefined) {\n const cachedResult = results.get(key);\n if (isCached(results, key, cachedResult)) {\n return cachedResult;\n }\n }\n const result = build(indexes);\n if (key !== undeclared) {\n results?.set(key, result);\n }\n return result;\n };\n}\n\nexport { createSelector };\nexport type { SelectorOptions };\n","import type {\n Composable,\n ComposableKindRecipe,\n ComposedDefaultedName,\n ComposedKindRecipe,\n ComposedVariants,\n} from \"./composition.js\";\nimport type {\n KindCompoundCondition,\n KindDefaultVariants,\n RecipeFunction,\n RecipeKind,\n VariantKey,\n} from \"./types.js\";\nimport type { LooseKindRecipeConfig, LooseRecipeKind } from \"./build-recipe.js\";\nimport type {\n LooseVariants,\n SelectedVariants,\n WithVariantKeys,\n} from \"./variants.js\";\nimport { compileLayer, compileMergedLayers } from \"./build-recipe.js\";\nimport { createRegistry, layerOf, mergeLayers } from \"./compose.js\";\nimport { createSelector } from \"./selector.js\";\nimport { withVariantKeys } from \"./variants.js\";\n\nconst recipes = createRegistry<unknown>(\"recipe\");\n\n/**\n * The variants of a {@link KindRecipeConfig}: for each variant name, the\n * value of each of its options.\n *\n * @typeParam Value - The value of an option.\n */\ntype KindVariants<Value> = LooseVariants<Value>;\n\n/**\n * A value added when several variants have particular options at the same\n * time.\n *\n * @typeParam Variants - The variant definitions of the recipe.\n * @typeParam Value - The value of an option.\n */\ninterface KindCompoundVariant<Variants, Value> {\n /**\n * The options that must all be selected for\n * {@link KindCompoundVariant.value} to apply.\n */\n readonly variants: KindCompoundCondition<Variants>;\n /** The value added when the condition matches. */\n readonly value: Value;\n}\n\n/**\n * The configuration of a recipe made from a {@link RecipeKind}.\n *\n * @typeParam Value - The value of an option.\n * @typeParam Variants - The variant definitions, keyed by variant name.\n * @typeParam DefaultedName - The names of the variants that have a default.\n * @typeParam Composed - The types of the recipes that the recipe composes.\n */\ninterface KindRecipeConfig<\n Value,\n Variants extends KindVariants<Value>,\n DefaultedName extends keyof ComposedVariants<Composed, Variants>,\n Composed extends readonly ComposableKindRecipe<Value>[] = readonly [],\n> {\n /**\n * Recipes whose config the recipe adds to its own, in order, as if it\n * were written in one config: their bases first, the values of each of\n * their options before its own, and their compound variants first. A\n * recipe composed several times counts once.\n */\n readonly composes?: Composed | undefined;\n /** The value that the values of every selection are added to. */\n readonly base?: Value | undefined;\n /** For each variant name, the value of each of its options. */\n readonly variants: Variants;\n /**\n * Values added when several variants have particular options at the same\n * time, applied in order after the values of the variants' options.\n */\n readonly compoundVariants?:\n | readonly KindCompoundVariant<\n NoInfer<ComposedVariants<Composed, Variants>>,\n Value\n >[]\n | undefined;\n /** The option each variant uses when the recipe is called without it. */\n readonly defaultVariants?:\n | KindDefaultVariants<ComposedVariants<Composed, Variants>, DefaultedName>\n | undefined;\n /** Whether the recipe caches its results. Defaults to the kind's `cache`. */\n readonly cache?: boolean | undefined;\n}\n\n/**\n * A recipe made from a {@link RecipeKind}: a function that returns the\n * result of a selection of variants, with the names of those variants in\n * `variantKeys`.\n *\n * @typeParam Selection - The variants the recipe accepts.\n * @typeParam Result - What the recipe returns.\n * @typeParam Composition - What the recipe passes on to the recipes that\n * compose it, a `RecipeComposition`. Without it, the type does not\n * allow composing the recipe.\n */\ntype KindRecipe<Selection, Result, Composition = unknown> = RecipeFunction<\n Selection,\n Result\n> & {\n /**\n * The names of the recipe's variants, in the order of\n * `Object.keys(config.variants)`, after those of the recipes it\n * composes.\n */\n readonly variantKeys: readonly VariantKey<Selection>[];\n} & Composable<Composition>;\n\n/**\n * Creates recipes of one kind, the function that {@link createRecipeKind}\n * returns.\n *\n * @typeParam Value - The value of an option.\n * @typeParam Result - What a recipe of this kind returns.\n * @param config - The base value, variants, compound variants, and default\n * variants of the recipe, and whether it caches its results.\n * @returns The recipe.\n */\ntype CreateKindRecipe<Value, Result> = <\n const Variants extends KindVariants<Value>,\n const DefaultedName extends keyof ComposedVariants<Composed, Variants> =\n never,\n const Composed extends readonly ComposableKindRecipe<Value>[] = readonly [],\n>(\n config: KindRecipeConfig<Value, Variants, DefaultedName, Composed>,\n) => ComposedKindRecipe<\n ComposedVariants<Composed, Variants>,\n ComposedDefaultedName<Composed, DefaultedName>,\n Value,\n Result,\n undefined\n>;\n\ntype LooseKindRecipe = WithVariantKeys<\n (selection?: SelectedVariants | null) => unknown\n>;\n\n/**\n * Creates a kind of recipe from how it turns the values of a selection into\n * its result, and returns the function that creates recipes of that kind.\n * A recipe maps a selection of variants to a result, as `cva` of\n * `@lynstack/class-recipe` does for class names, for values of any type,\n * such as style objects.\n *\n * @remarks\n * For each selection, a recipe reduces the value of each variant's selected\n * option and the value of each matching compound variant, with\n * `kind.reduce`, into the accumulator that `kind.initial` returns for the\n * recipe's base, then passes it to `kind.finish`. A variant whose only\n * options are `\"true\"` and `\"false\"` defaults to `false`, an option that the\n * config does not declare adds no value, and properties of the selection\n * that are not variants are ignored. A recipe's `variantKeys` property lists\n * the names of its variants.\n *\n * With the cache, a recipe builds the result of each declared selection\n * once, and calling it again with the same variants returns the same\n * result. Freeze an object result in `kind.finish` so that callers cannot\n * change a result that later calls share. A selection with an undeclared\n * option is built on every call. A recipe keeps up to one result for each\n * combination of declared options; set `cache: false` in the config of a\n * recipe whose variants come from untrusted input, which overrides the kind.\n *\n * When the variant names are not known at compile time, as in a library\n * that passes on a config it received, a recipe accepts any selection.\n *\n * A recipe composes the recipes listed in its config's `composes`, of any\n * kind whose values have its type, as if their configs and its own were one:\n * the accumulator starts from the first base, and the other bases are\n * reduced first; every option of each is declared, with its values reduced\n * in the order of the recipes; their compound variants come before its own;\n * and a variant's default is the last one given. A recipe composed several\n * times counts once. With `kind.combine`, the bases, and the values that\n * the recipes give one option, are combined into one when the recipe is\n * created, so that it reduces one value for each.\n *\n * @typeParam Value - The value of an option, inferred from the `value`\n * parameter of `kind.reduce` or the `base` parameter of `kind.initial`.\n * @typeParam Accumulator - What the values are reduced to, inferred from\n * `kind.initial`.\n * @typeParam Result - What a recipe returns, inferred from `kind.finish`,\n * or the accumulator without it.\n * @param kind - How a recipe turns the values of a selection into its\n * result, and whether it caches the result.\n * @returns The function that creates recipes of this kind.\n *\n * @example\n * ```ts\n * type Style = Readonly<Record<string, string | number>>;\n *\n * const styleRecipe = createRecipeKind({\n * initial: (base?: Style): Record<string, string | number> => ({ ...base }),\n * reduce: (style, value: Style) => Object.assign(style, value),\n * combine: (first, second) => ({ ...first, ...second }),\n * finish: (style): Style => Object.freeze(style),\n * });\n *\n * const text = styleRecipe({\n * base: { color: \"black\" },\n * variants: {\n * size: { sm: { fontSize: 12 }, lg: { fontSize: 24 } },\n * muted: { true: { opacity: 0.6 } },\n * },\n * compoundVariants: [\n * { variants: { size: \"lg\", muted: true }, value: { fontWeight: 300 } },\n * ],\n * defaultVariants: { size: \"sm\" },\n * });\n *\n * text(); // => { color: \"black\", fontSize: 12 }\n *\n * text({ size: \"lg\", muted: true });\n * // => { color: \"black\", fontSize: 24, opacity: 0.6, fontWeight: 300 }\n *\n * text.variantKeys; // => [\"size\", \"muted\"]\n *\n * const heading = styleRecipe({\n * composes: [text],\n * base: { fontWeight: 700 },\n * variants: { size: { xl: { fontSize: 32 } } },\n * });\n *\n * heading({ size: \"xl\" });\n * // => { color: \"black\", fontWeight: 700, fontSize: 32 }\n * ```\n */\nfunction createRecipeKind<Value, Accumulator, Result = Accumulator>(\n kind: RecipeKind<Value, Accumulator, Result>,\n): CreateKindRecipe<Value, Result>;\n\nfunction createRecipeKind(kind: LooseRecipeKind): unknown {\n const kindCache = kind.cache ?? true;\n\n return (config: LooseKindRecipeConfig): LooseKindRecipe => {\n const own = layerOf(config, []);\n const layers = recipes.layersOf(config.composes ?? [], own);\n const { compiled, build } =\n layers.length === 1\n ? compileLayer(kind, own)\n : compileMergedLayers(kind, mergeLayers(layers));\n const options = { cache: config.cache ?? kindCache };\n const recipe = createSelector(compiled, build, options);\n return recipes.withLayers(withVariantKeys(recipe, compiled), layers);\n };\n}\n\nexport { createRecipeKind };\nexport type {\n CreateKindRecipe,\n KindCompoundVariant,\n KindRecipe,\n KindRecipeConfig,\n KindVariants,\n};\n","import type { CompiledVariants, Compound } from \"./variants.js\";\nimport { combineValues } from \"./compose.js\";\nimport { matches } from \"./reduce-values.js\";\nimport { noOption } from \"./variants.js\";\n\n/** Values for some slots, keyed by slot name, as the runtime takes them. */\ntype LooseSlotValues = Readonly<Record<string, unknown>>;\n\n/** How the values of each slot are reduced, as a recipe kind gives it. */\ninterface SlotsKind {\n readonly initial: (base: unknown) => unknown;\n readonly reduce: (accumulator: unknown, value: unknown) => unknown;\n readonly combine?: ((first: unknown, second: unknown) => unknown) | undefined;\n readonly finish?: ((accumulator: unknown) => unknown) | undefined;\n}\n\n/** The slots of a slot recipe and their bases, in the order of its layers. */\ninterface SlotsConfig {\n readonly slots: readonly string[];\n readonly bases: readonly LooseSlotValues[];\n}\n\n/**\n * The values that one option or compound variant adds to its slots: the\n * index of each slot it gives a value, and that value at the same position.\n * Slots without a value are left out, so reducing skips them.\n */\ninterface SlotEntries {\n readonly slots: readonly number[];\n readonly values: readonly unknown[];\n}\n\n/**\n * The accumulator of each slot while a selection is reduced, which\n * {@link addEntries} updates in place.\n */\ninterface SlotAccumulators {\n readonly length: number;\n [slot: number]: unknown;\n}\n\nfunction valueOfSlot(\n values: LooseSlotValues | undefined,\n slot: string,\n): unknown {\n return values !== undefined && Object.hasOwn(values, slot)\n ? values[slot]\n : undefined;\n}\n\n/**\n * Returns the values of each slot in a list of values for some slots, in\n * the order of the list, combined into one with `combine` when the kind\n * has it.\n */\nfunction slotValuesOf(\n slots: readonly string[],\n valuesList: readonly LooseSlotValues[],\n combine: SlotsKind[\"combine\"],\n): readonly (readonly unknown[])[] {\n return slots.map((slot) => {\n const values = valuesList.flatMap((each) =>\n listOf(valueOfSlot(each, slot)),\n );\n return combine === undefined\n ? values\n : listOf(combineValues(values, combine));\n });\n}\n\n/**\n * Returns the entries of the values of each slot, by slot index, or\n * undefined when no slot has one.\n */\nfunction entriesBySlot(\n valuesBySlot: readonly (readonly unknown[])[],\n): SlotEntries | undefined {\n const slotIndexes = valuesBySlot.flatMap((values, slot) =>\n values.map(() => slot),\n );\n return slotIndexes.length === 0\n ? undefined\n : { slots: slotIndexes, values: valuesBySlot.flat() };\n}\n\nfunction listOf(value: unknown): readonly unknown[] {\n return value === undefined ? [] : [value];\n}\n\n/** Turns the slot values of each option and compound variant into entries. */\nfunction compileEntries(\n compiled: CompiledVariants<readonly LooseSlotValues[]>,\n slots: readonly string[],\n combine: SlotsKind[\"combine\"],\n): CompiledVariants<SlotEntries | undefined> {\n const entriesOf = (\n valuesList: readonly LooseSlotValues[],\n ): SlotEntries | undefined =>\n entriesBySlot(slotValuesOf(slots, valuesList, combine));\n return {\n ...compiled,\n compounds: compiled.compounds\n .map((compound): Compound<SlotEntries | undefined> => ({\n conditions: compound.conditions,\n value: entriesOf(compound.value),\n }))\n .filter((compound) => compound.value !== undefined),\n valuesByIndex: compiled.valuesByIndex.map((valuesOfVariant) =>\n valuesOfVariant.map((values) => entriesOf(values)),\n ),\n };\n}\n\n/** Reduces the values of `added` into the accumulators of their slots. */\nfunction addEntries(\n accumulators: SlotAccumulators,\n added: SlotEntries,\n reduce: SlotsKind[\"reduce\"],\n): void {\n const { slots, values } = added;\n for (let entry = 0; entry < slots.length; entry += 1) {\n const slot = slots[entry] ?? 0;\n accumulators[slot] = reduce(accumulators[slot], values[entry]);\n }\n}\n\n/**\n * The names of a slot recipe's slots, and an object with a property for\n * each, which results copy so that assigning a slot named `__proto__` sets\n * that property instead of the prototype.\n */\ninterface SlotNames {\n readonly names: readonly string[];\n readonly template: Readonly<Record<string, unknown>>;\n}\n\n/** Returns the frozen result of each slot, keyed by slot name. */\nfunction resultsBySlot(\n slots: SlotNames,\n accumulators: SlotAccumulators,\n finish: (accumulator: unknown) => unknown,\n): Readonly<Record<string, unknown>> {\n const { names } = slots;\n const results: Record<string, unknown> = { ...slots.template };\n for (let slot = 0; slot < names.length; slot += 1) {\n results[names[slot] ?? \"\"] = finish(accumulators[slot]);\n }\n return Object.freeze(results);\n}\n\nconst asResult = (accumulator: unknown): unknown => accumulator;\n\n/**\n * Returns the function that builds the result of a selection for each slot\n * of a slot recipe: each slot reduces its own values with `kind`, and the\n * results are frozen in an object keyed by slot name.\n *\n * It reduces the values in a loop of its own instead of through\n * `reduceValues`, which recipes share, so that its calls to `kind.reduce`\n * see only the reducers of slot recipes and can be inlined.\n */\nfunction createSlotsBuilder(\n kind: SlotsKind,\n compiled: CompiledVariants<readonly LooseSlotValues[]>,\n config: SlotsConfig,\n): (indexes: Int32Array) => Readonly<Record<string, unknown>> {\n const { initial, reduce, combine, finish = asResult } = kind;\n const names = [...config.slots];\n const slotNames: SlotNames = {\n names,\n template: Object.fromEntries(names.map((slot) => [slot, undefined])),\n };\n const basesBySlot = slotValuesOf(names, config.bases, combine);\n const bases = basesBySlot.map(([base]: readonly unknown[]) => base);\n const otherBases = entriesBySlot(\n basesBySlot.map(([, ...others]: readonly unknown[]) => others),\n );\n const { valuesByIndex, compounds } = compileEntries(compiled, names, combine);\n\n /** Returns the accumulator of each slot, with every base reduced. */\n const initialAccumulators = (): SlotAccumulators => {\n const accumulators = bases.map((base) => initial(base));\n if (otherBases !== undefined) {\n addEntries(accumulators, otherBases, reduce);\n }\n return accumulators;\n };\n\n return (indexes) => {\n const accumulators = initialAccumulators();\n for (let variant = 0; variant < valuesByIndex.length; variant += 1) {\n const added = valuesByIndex[variant]?.[indexes[variant] ?? noOption];\n if (added !== undefined) {\n addEntries(accumulators, added, reduce);\n }\n }\n for (const compound of compounds) {\n if (compound.value !== undefined && matches(compound, indexes)) {\n addEntries(accumulators, compound.value, reduce);\n }\n }\n return resultsBySlot(slotNames, accumulators, finish);\n };\n}\n\nexport { createSlotsBuilder };\nexport type { LooseSlotValues, SlotsKind };\n","import type {\n ComposableKindSlotRecipe,\n ComposedDefaultedName,\n ComposedKindRecipe,\n ComposedSlot,\n ComposedVariants,\n} from \"./composition.js\";\nimport type {\n KindCompoundCondition,\n KindDefaultVariants,\n RecipeKind,\n} from \"./types.js\";\nimport type { LooseSlotValues, SlotsKind } from \"./slots.js\";\nimport type { SelectedVariants, WithVariantKeys } from \"./variants.js\";\nimport { compileVariants, withVariantKeys } from \"./variants.js\";\nimport { createRegistry, layerOf, mergeLayers } from \"./compose.js\";\nimport type { KindVariants } from \"./recipe-kind.js\";\nimport { createSelector } from \"./selector.js\";\nimport { createSlotsBuilder } from \"./slots.js\";\n\nconst slotRecipes = createRegistry<LooseSlotValues>(\"slot recipe\");\n\nconst noSlotValues: readonly LooseSlotValues[] = Object.freeze([]);\n\n/**\n * Values for some of a slot recipe's slots, keyed by slot name.\n *\n * @typeParam Slot - The names of the slots.\n * @typeParam Value - The value of a slot.\n */\ntype SlotValues<Slot extends string, Value> = Readonly<\n Partial<Record<Slot, Value | undefined>>\n>;\n\n/**\n * The variants of a {@link KindSlotRecipeConfig}: for each variant name,\n * the values of each slot for each of its options.\n *\n * @typeParam Value - The value of a slot.\n */\ntype KindSlotVariants<Value> = KindVariants<SlotValues<string, Value>>;\n\n/**\n * Rejects the slots of each option's values that `Slot` does not name,\n * unless the option's slot names are not known at compile time.\n */\ntype NoUnknownSlots<Variants, Slot extends string> = {\n readonly [Name in keyof Variants]: {\n readonly [\n Option in keyof Variants[Name]\n ]: string extends keyof Variants[Name][Option]\n ? unknown\n : Readonly<\n Partial<Record<Exclude<keyof Variants[Name][Option], Slot>, never>>\n >;\n };\n};\n\n/**\n * Values added to some slots when several variants have particular options\n * at the same time.\n *\n * @typeParam Variants - The variant definitions of the slot recipe.\n * @typeParam Slot - The names of the slots.\n * @typeParam Value - The value of a slot.\n */\ninterface KindSlotCompoundVariant<Variants, Slot extends string, Value> {\n /**\n * The options that must all be selected for\n * {@link KindSlotCompoundVariant.value} to apply.\n */\n readonly variants: KindCompoundCondition<Variants>;\n /** The value added to each slot when the condition matches. */\n readonly value: SlotValues<Slot, Value>;\n}\n\n/**\n * The configuration of a slot recipe made from a {@link RecipeKind}.\n *\n * @typeParam Slot - The names of the slots.\n * @typeParam Value - The value of a slot.\n * @typeParam Variants - The variant definitions, keyed by variant name.\n * @typeParam DefaultedName - The names of the variants that have a default.\n */\ninterface KindSlotRecipeConfig<\n Slot extends string,\n Value,\n Variants extends KindSlotVariants<Value>,\n DefaultedName extends keyof ComposedVariants<Composed, Variants>,\n Composed extends readonly ComposableKindSlotRecipe<Value>[] = readonly [],\n> {\n /**\n * Slot recipes whose config the recipe adds to its own, in order, as if\n * it were written in one config: their slots and bases first, the values\n * of each of their options before its own, and their compound variants\n * first. A slot recipe composed several times counts once.\n */\n readonly composes?: Composed | undefined;\n /**\n * The names of the slots, in the order of the recipe's result, after\n * those of the slot recipes it composes.\n */\n readonly slots: readonly Slot[];\n /** The value of each slot that the values of every selection are added to. */\n readonly base?:\n SlotValues<NoInfer<ComposedSlot<Composed, Slot>>, Value> | undefined;\n /** For each variant name, the values of each slot for each of its options. */\n readonly variants: Variants &\n NoUnknownSlots<Variants, NoInfer<ComposedSlot<Composed, Slot>>>;\n /**\n * Values added to some slots when several variants have particular\n * options at the same time, applied in order after the values of the\n * variants' options.\n */\n readonly compoundVariants?:\n | readonly KindSlotCompoundVariant<\n NoInfer<ComposedVariants<Composed, Variants>>,\n NoInfer<ComposedSlot<Composed, Slot>>,\n Value\n >[]\n | undefined;\n /** The option each variant uses when the recipe is called without it. */\n readonly defaultVariants?:\n | KindDefaultVariants<ComposedVariants<Composed, Variants>, DefaultedName>\n | undefined;\n /** Whether the recipe caches its results. Defaults to the kind's `cache`. */\n readonly cache?: boolean | undefined;\n}\n\n/**\n * Creates slot recipes of one kind, the function that\n * {@link createSlotRecipeKind} returns.\n *\n * @typeParam Value - The value of a slot.\n * @typeParam Result - What a recipe of this kind returns for each slot.\n * @param config - The slot recipes it composes, and the slots, base values,\n * variants, compound variants, and default variants of the slot recipe,\n * and whether it caches its results.\n * @returns The slot recipe.\n */\ntype CreateKindSlotRecipe<Value, Result> = <\n const Slot extends string,\n const Variants extends KindSlotVariants<Value>,\n const DefaultedName extends keyof ComposedVariants<Composed, Variants> =\n never,\n const Composed extends readonly ComposableKindSlotRecipe<Value>[] =\n readonly [],\n>(\n config: KindSlotRecipeConfig<Slot, Value, Variants, DefaultedName, Composed>,\n) => ComposedKindRecipe<\n ComposedVariants<Composed, Variants>,\n ComposedDefaultedName<Composed, DefaultedName>,\n Value,\n Readonly<Record<ComposedSlot<Composed, Slot>, Result>>,\n readonly ComposedSlot<Composed, Slot>[]\n>;\n\ninterface LooseRecipeKind extends SlotsKind {\n readonly cache?: boolean | undefined;\n}\n\ninterface LooseSlotRecipeConfig {\n readonly composes?: readonly unknown[] | undefined;\n readonly slots: readonly string[];\n readonly base?: LooseSlotValues | undefined;\n readonly variants: KindSlotVariants<unknown>;\n readonly compoundVariants?:\n | readonly {\n readonly variants: SelectedVariants;\n readonly value: LooseSlotValues;\n }[]\n | undefined;\n readonly defaultVariants?: SelectedVariants | undefined;\n readonly cache?: boolean | undefined;\n}\n\ntype LooseSlotRecipe = WithVariantKeys<\n (selection?: SelectedVariants | null) => Readonly<Record<string, unknown>>\n>;\n\n/**\n * Creates a kind of slot recipe from how it turns the values of each slot\n * into that slot's result, and returns the function that creates slot\n * recipes of that kind. A slot recipe maps a selection of variants to the\n * result of each of several slots, such as the class names or the styles of\n * the elements of a component, as `sva` of `@lynstack/class-recipe` does.\n *\n * @remarks\n * A slot recipe reduces the values of each slot as a recipe of the same\n * kind from {@link createRecipeKind} would: `kind.initial` returns the\n * accumulator for the slot's base value, `kind.reduce` adds the slot's\n * value of each variant's selected option, in the order of `variants`, and\n * of each matching compound variant, in the order of `compoundVariants`,\n * and `kind.finish` turns the accumulator into the slot's result. A slot\n * without values gets the result of its accumulator for an `undefined`\n * base, so every slot is in the result.\n *\n * The result is a frozen object of each slot's result, keyed by slot name\n * in the order of `slots`. With the cache, a slot recipe builds it once for\n * each declared selection and returns the same object for the same\n * variants. The `cache` of a slot recipe's config overrides the kind's.\n * Variants, default variants, boolean variants, and undeclared\n * options behave as in {@link createRecipeKind}, and the recipe's\n * `variantKeys` property lists the names of its variants.\n *\n * A slot recipe composes the slot recipes listed in `composes` as a recipe\n * composes recipes, and has the slots of each, theirs first. With\n * `kind.combine`, the values of each slot are combined as a recipe's are.\n *\n * @typeParam Value - The value of a slot, inferred from the `value`\n * parameter of `kind.reduce` or the `base` parameter of `kind.initial`.\n * @typeParam Accumulator - What the values of a slot are reduced to,\n * inferred from `kind.initial`.\n * @typeParam Result - What a slot recipe returns for each slot, inferred\n * from `kind.finish`, or the accumulator without it.\n * @param kind - How a slot recipe turns the values of each slot into its\n * result, and whether it caches its results. The same kind can create\n * recipes with {@link createRecipeKind}.\n * @returns The function that creates slot recipes of this kind.\n *\n * @example\n * ```ts\n * type Style = Readonly<Record<string, string | number>>;\n *\n * const styleKind = {\n * initial: (base?: Style): Record<string, string | number> => ({ ...base }),\n * reduce: (style: Record<string, string | number>, value: Style) =>\n * Object.assign(style, value),\n * finish: (style: Record<string, string | number>): Style =>\n * Object.freeze(style),\n * };\n *\n * const slotStyleRecipe = createSlotRecipeKind(styleKind);\n *\n * const card = slotStyleRecipe({\n * slots: [\"root\", \"title\"],\n * base: { root: { padding: 16 }, title: { fontSize: 18 } },\n * variants: {\n * tone: {\n * light: { root: { backgroundColor: \"white\" } },\n * dark: { root: { backgroundColor: \"black\" }, title: { color: \"white\" } },\n * },\n * },\n * compoundVariants: [\n * { variants: { tone: \"dark\" }, value: { title: { fontWeight: 600 } } },\n * ],\n * defaultVariants: { tone: \"light\" },\n * });\n *\n * card();\n * // => { root: { padding: 16, backgroundColor: \"white\" }, title: { fontSize: 18 } }\n *\n * card({ tone: \"dark\" });\n * // => {\n * // root: { padding: 16, backgroundColor: \"black\" },\n * // title: { fontSize: 18, color: \"white\", fontWeight: 600 },\n * // }\n *\n * card.variantKeys; // => [\"tone\"]\n *\n * const dialog = slotStyleRecipe({\n * composes: [card],\n * slots: [\"footer\"],\n * variants: { tone: { dark: { footer: { borderColor: \"white\" } } } },\n * });\n *\n * dialog({ tone: \"dark\" }).footer; // => { borderColor: \"white\" }\n * ```\n */\nfunction createSlotRecipeKind<Value, Accumulator, Result = Accumulator>(\n kind: RecipeKind<Value, Accumulator, Result>,\n): CreateKindSlotRecipe<Value, Result>;\n\nfunction createSlotRecipeKind(kind: LooseRecipeKind): unknown {\n const kindCache = kind.cache ?? true;\n\n return (config: LooseSlotRecipeConfig): LooseSlotRecipe => {\n const own = layerOf(config, config.slots);\n const layers = slotRecipes.layersOf(config.composes ?? [], own);\n const merged = mergeLayers(layers);\n const compiled = compileVariants<readonly LooseSlotValues[]>({\n ...merged,\n noValue: noSlotValues,\n });\n const build = createSlotsBuilder(kind, compiled, merged);\n const options = { cache: config.cache ?? kindCache };\n const recipe = createSelector(compiled, build, options);\n return slotRecipes.withLayers(withVariantKeys(recipe, compiled), layers);\n };\n}\n\nexport { createSlotRecipeKind };\nexport type {\n CreateKindSlotRecipe,\n KindSlotCompoundVariant,\n KindSlotRecipeConfig,\n KindSlotVariants,\n SlotValues,\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lynstack/recipe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Fast, type-safe recipes for values of any type: define how a kind of recipe combines values, such as class names or styles, then create recipes of that kind.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"recipe",
|