@yahoo/uds-create-config 4.10.2 → 4.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/configs/CanvasConfig.d.ts +165 -77
- package/dist/configs/documentation.d.ts +1 -1
- package/dist/configs/react-native-system.d.ts +111 -52
- package/dist/configs/system.d.ts +121 -87
- package/dist/configs/system.js +4 -0
- package/dist/entities/documentation/Documentation.d.ts +1 -1
- package/dist/entities/native/NativeModifier.d.ts +1 -1
- package/dist/entities/native/NativeSettings.d.ts +1 -1
- package/dist/entities/native/NativeStyleProperty.d.ts +2 -2
- package/dist/entities/native/NativeToken.d.ts +4 -3
- package/dist/entities/system/CanvasRole.d.ts +1 -1
- package/dist/entities/system/Component.d.ts +23 -13
- package/dist/entities/system/Component.js +41 -15
- package/dist/entities/system/Composite.d.ts +1 -1
- package/dist/entities/system/Device.d.ts +1 -1
- package/dist/entities/system/File.d.ts +2 -2
- package/dist/entities/system/Font.d.ts +1 -1
- package/dist/entities/system/GlobalStyle.d.ts +1 -1
- package/dist/entities/system/Guidance.d.ts +1 -1
- package/dist/entities/system/GuidanceStyle.d.ts +1 -1
- package/dist/entities/system/Icon.d.ts +63 -11
- package/dist/entities/system/Icon.js +70 -14
- package/dist/entities/system/LinkedSystem.d.ts +1 -1
- package/dist/entities/system/Modifier.d.ts +2 -2
- package/dist/entities/system/Motion.d.ts +1 -1
- package/dist/entities/system/Package.d.ts +1 -1
- package/dist/entities/system/Settings.d.ts +1 -1
- package/dist/entities/system/StyleProperty.d.ts +12 -19
- package/dist/entities/system/StyleProperty.js +7 -10
- package/dist/entities/system/Token.d.ts +4 -3
- package/dist/entities/system/defineComponent.d.ts +21 -6
- package/dist/entities/system/defineComponent.js +7 -12
- package/dist/entities/system/glyph-identity.d.ts +7 -0
- package/dist/entities/system/glyph-identity.js +153 -0
- package/dist/entities/system/icon-domain-invariants.d.ts +11 -0
- package/dist/entities/system/icon-domain-invariants.js +178 -0
- package/dist/entities/system/icon-library.d.ts +93 -34
- package/dist/entities/system/icon-library.js +218 -50
- package/dist/entities/system/icon-metadata.d.ts +10 -4
- package/dist/entities/system/icon-metadata.js +45 -5
- package/dist/entities/system/runtime-modules.js +5 -5
- package/dist/entities/system/style-bag.js +14 -2
- package/dist/framework/Config.d.ts +1 -1
- package/dist/framework/Config.js +34 -5
- package/dist/framework/defineConfig.d.ts +6 -3
- package/dist/framework/defineEntity.d.ts +54 -15
- package/dist/framework/defineEntity.js +56 -2
- package/dist/framework/projections.d.ts +40 -2
- package/dist/framework/projections.js +99 -5
- package/dist/framework/prop-surface.d.ts +6 -5
- package/dist/framework/prop-surface.js +12 -14
- package/dist/framework/registry.d.ts +17 -1
- package/dist/framework/registry.js +2 -1
- package/dist/framework/render-spec.d.ts +6 -5
- package/dist/framework/render-spec.js +16 -11
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/framework/signature.js +5 -1
- package/dist/framework/utils/group.d.ts +15 -1
- package/dist/framework/utils/ref-grammar.d.ts +35 -0
- package/dist/framework/utils/ref-grammar.js +76 -0
- package/dist/framework/utils/refs.d.ts +13 -45
- package/dist/framework/utils/refs.js +31 -94
- package/dist/framework/value-domain.js +53 -8
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -5
- package/dist/migrations/2.0.0/v1-artifact.d.ts +62 -45
- package/dist/migrations/2.0.0/v1-artifact.js +5 -7
- package/dist/migrations/20260921044042_icon_library_without_component.d.ts +19 -0
- package/dist/migrations/20260921044042_icon_library_without_component.js +86 -0
- package/dist/migrations/20260922013625_icon_libraries.d.ts +18 -0
- package/dist/migrations/20260922013625_icon_libraries.js +8 -0
- package/dist/renderer/UdsRenderer.d.ts +8 -1
- package/dist/renderer/UdsRenderer.js +7 -3
- package/dist/renderer/glyphEntries.d.ts +32 -0
- package/dist/renderer/glyphEntries.js +48 -0
- package/dist/renderer/glyphRenderable.d.ts +10 -0
- package/dist/renderer/glyphRenderable.js +40 -0
- package/dist/renderer/icon-library.d.ts +18 -11
- package/dist/renderer/index.d.ts +4 -2
- package/dist/renderer/index.js +4 -2
- package/dist/renderer/resolveGlyph.d.ts +47 -0
- package/dist/renderer/resolveGlyph.js +78 -0
- package/dist/renderer/wrapRegistry.d.ts +13 -4
- package/dist/renderer/wrapRegistry.js +7 -5
- package/dist/renderer/wrappers/component-refs.js +6 -2
- package/dist/renderer/wrappers/glyph-props.d.ts +15 -0
- package/dist/renderer/wrappers/glyph-props.js +65 -0
- package/dist/spec/glyph-jsx.d.ts +41 -0
- package/dist/spec/{asset-jsx.js → glyph-jsx.js} +7 -25
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +2 -2
- package/dist/spec/jsxToSpec.d.ts +10 -4
- package/dist/spec/jsxToSpec.js +21 -4
- package/dist/spec/specToJsx.d.ts +20 -9
- package/dist/spec/specToJsx.js +77 -16
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/renderer/assetEntries.d.ts +0 -37
- package/dist/renderer/assetEntries.js +0 -38
- package/dist/renderer/assetRenderable.d.ts +0 -25
- package/dist/renderer/assetRenderable.js +0 -19
- package/dist/spec/asset-jsx.d.ts +0 -61
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Ref } from "../../framework/utils/
|
|
2
|
-
import {
|
|
1
|
+
import { Ref } from "../../framework/utils/ref-grammar.js";
|
|
2
|
+
import { Ref as Ref$1 } from "../../framework/utils/refs.js";
|
|
3
3
|
import { EntityClass } from "../../framework/defineEntity.js";
|
|
4
4
|
import { Config } from "../../framework/Config.js";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -80,9 +80,9 @@ type FormValue<K extends StylePropValueKind> = z.infer<ReturnType<Forms[K]>>;
|
|
|
80
80
|
* `true` is the bare prop, an empty leaf so the class is its prefix; `false` is its absence and
|
|
81
81
|
* authors nothing. A numeric alias is a leaf once it is a string: `6` names `.uds-grid-cols-6`.
|
|
82
82
|
*/
|
|
83
|
-
declare const STYLE_PROP_VALUE_LEAVES: { readonly [K in StylePropValueKind]: (entry: FormValue<K>, members: (target: Ref) => readonly RefMember[]) => StylePropLeaf[] };
|
|
83
|
+
declare const STYLE_PROP_VALUE_LEAVES: { readonly [K in StylePropValueKind]: (entry: FormValue<K>, members: (target: Ref$1) => readonly RefMember[]) => StylePropLeaf[] };
|
|
84
84
|
/** The leaves one stored entry stands for; none for a value no form admits. */
|
|
85
|
-
declare function leavesOfStylePropValue(entry: unknown, members: (target: Ref) => readonly RefMember[]): StylePropLeaf[];
|
|
85
|
+
declare function leavesOfStylePropValue(entry: unknown, members: (target: Ref$1) => readonly RefMember[]): StylePropLeaf[];
|
|
86
86
|
/** The `values` field's member schema against `ctx`: the union of every form. With no context it is
|
|
87
87
|
* the storage shape; with a config and the owner's properties it is also the judge. */
|
|
88
88
|
declare function stylePropValueSchema(ctx?: StylePropValueCtx): z.ZodType<StylePropValue>;
|
|
@@ -97,9 +97,13 @@ declare function classifyStylePropValue({
|
|
|
97
97
|
config: Config;
|
|
98
98
|
entry: StylePropValue;
|
|
99
99
|
}): StylePropValueKind;
|
|
100
|
-
/**
|
|
101
|
-
*
|
|
102
|
-
|
|
100
|
+
/**
|
|
101
|
+
* `StyleProperty.ref('bg')` keeps the path literal in its type, so a component prop declared as the
|
|
102
|
+
* ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered `bg`
|
|
103
|
+
* values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather than
|
|
104
|
+
* the name's.
|
|
105
|
+
*/
|
|
106
|
+
declare const StyleProperty: EntityClass<z.ZodObject<{
|
|
103
107
|
properties: z.ZodArray<z.ZodString>;
|
|
104
108
|
values: z.ZodOptional<z.ZodArray<z.ZodType<StylePropValue, unknown, z.core.$ZodTypeInternals<StylePropValue, unknown>>>>;
|
|
105
109
|
responsive: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -125,18 +129,7 @@ declare const StylePropertyEntity: EntityClass<z.ZodObject<{
|
|
|
125
129
|
data: Record<string, unknown>;
|
|
126
130
|
} | undefined;
|
|
127
131
|
};
|
|
128
|
-
}, Record<never, never>, false
|
|
129
|
-
/**
|
|
130
|
-
* `StyleProperty.ref('bg')` with the path kept literal in its type, so a component prop declared as
|
|
131
|
-
* the ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered
|
|
132
|
-
* `bg` values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather
|
|
133
|
-
* than the name's. The value is the base ref unchanged; only the type narrows.
|
|
134
|
-
*/
|
|
135
|
-
declare const StyleProperty: Omit<typeof StylePropertyEntity, "ref"> & {
|
|
136
|
-
ref<const Path extends PathOf<"style-property">>(path: Path): ReturnType<typeof StylePropertyEntity.ref> & {
|
|
137
|
-
readonly __ref: `style-property:${Path}`;
|
|
138
|
-
};
|
|
139
|
-
};
|
|
132
|
+
}, Record<never, never>, false, Record<never, never>, Record<never, never>>;
|
|
140
133
|
/** `flexDirection` → `flex-direction`. A name already spelled as CSS comes back unchanged. */
|
|
141
134
|
declare function toCssPropertyName(property: string): string;
|
|
142
135
|
/**
|
|
@@ -406,9 +406,13 @@ function freePath(config, path) {
|
|
|
406
406
|
for (let n = 2; config.resolve("style-property", candidate); n += 1) candidate = `${path}${n}`;
|
|
407
407
|
return candidate;
|
|
408
408
|
}
|
|
409
|
-
/**
|
|
410
|
-
*
|
|
411
|
-
|
|
409
|
+
/**
|
|
410
|
+
* `StyleProperty.ref('bg')` keeps the path literal in its type, so a component prop declared as the
|
|
411
|
+
* ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered `bg`
|
|
412
|
+
* values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather than
|
|
413
|
+
* the name's.
|
|
414
|
+
*/
|
|
415
|
+
const StyleProperty = defineEntity({
|
|
412
416
|
kind: "style-property",
|
|
413
417
|
label: "Style property",
|
|
414
418
|
labelPlural: "Style properties",
|
|
@@ -429,13 +433,6 @@ const StylePropertyEntity = defineEntity({
|
|
|
429
433
|
}
|
|
430
434
|
}
|
|
431
435
|
});
|
|
432
|
-
/**
|
|
433
|
-
* `StyleProperty.ref('bg')` with the path kept literal in its type, so a component prop declared as
|
|
434
|
-
* the ref is typed by the property it points at: `bg: StyleProperty.ref('bg')` offers the registered
|
|
435
|
-
* `bg` values, and `columns: StyleProperty.ref('gridTemplateColumns')` offers the target's rather
|
|
436
|
-
* than the name's. The value is the base ref unchanged; only the type narrows.
|
|
437
|
-
*/
|
|
438
|
-
const StyleProperty = Object.assign(StylePropertyEntity, {});
|
|
439
436
|
/** `flexDirection` → `flex-direction`. A name already spelled as CSS comes back unchanged. */
|
|
440
437
|
function toCssPropertyName(property) {
|
|
441
438
|
return property.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Ref } from "../../framework/utils/
|
|
1
|
+
import { Ref } from "../../framework/utils/ref-grammar.js";
|
|
2
|
+
import { Ref as Ref$1 } from "../../framework/utils/refs.js";
|
|
2
3
|
import { SubEntityClass } from "../../framework/defineSubEntity.js";
|
|
3
4
|
import { EntityClass } from "../../framework/defineEntity.js";
|
|
4
5
|
import { DerivedColor, Gradient } from "./color.js";
|
|
@@ -6,7 +7,7 @@ import { z } from "zod";
|
|
|
6
7
|
|
|
7
8
|
//#region src/entities/system/Token.d.ts
|
|
8
9
|
/** An override's `on`: a modifier ref, or a list of them that all hold at once. */
|
|
9
|
-
type OverrideCondition = Ref | Ref[];
|
|
10
|
+
type OverrideCondition = Ref$1 | Ref$1[];
|
|
10
11
|
/** The modifier paths an override's `on` names, in the order written. */
|
|
11
12
|
declare function overrideModifiers(on: unknown): string[];
|
|
12
13
|
/**
|
|
@@ -62,6 +63,6 @@ declare const Token: EntityClass<z.ZodObject<{
|
|
|
62
63
|
readonly create: {
|
|
63
64
|
readonly description: `Create a token at \`<group>/<name>\` \u2014 \`data.value\` is the CSS value. The first path segment is its group, and a style property takes a whole group as one ref (\`${string}\`), so put the tokens one prop should offer in one group rather than listing values on the prop.`;
|
|
64
65
|
};
|
|
65
|
-
}, Record<never, never>, false
|
|
66
|
+
}, Record<never, never>, false, Record<never, never>, Record<never, never>>;
|
|
66
67
|
//#endregion
|
|
67
68
|
export { OverrideCondition, Token, TokenBody, defineOverride, overrideCondition, overrideKey, overrideKeyModifiers, overrideModifiers };
|
|
@@ -3,7 +3,7 @@ import { RegisteredModifiers, RegisteredPaths, RegisteredStyleProps } from "../.
|
|
|
3
3
|
import { Authored } from "../../framework/authoring.js";
|
|
4
4
|
import { StyleValue } from "./style-bag.js";
|
|
5
5
|
import { VariantValues } from "./Component.js";
|
|
6
|
-
import { AllHTMLAttributes, ComponentPropsWithRef, ElementType, JSX, ReactNode, Ref } from "react";
|
|
6
|
+
import { AllHTMLAttributes, ComponentPropsWithRef, ComponentType, ElementType, JSX, ReactNode, Ref } from "react";
|
|
7
7
|
|
|
8
8
|
//#region src/entities/system/defineComponent.d.ts
|
|
9
9
|
/**
|
|
@@ -162,7 +162,7 @@ interface ForwardDecl<Path extends string = string> {
|
|
|
162
162
|
*/
|
|
163
163
|
type PropDecl<Path extends string = string> = SlotDecl | ForwardDecl<Path> | {
|
|
164
164
|
type: 'variant';
|
|
165
|
-
value: readonly string[] | VariantValues | Ref$1;
|
|
165
|
+
value: readonly string[] | VariantValues | Ref$1 | readonly Ref$1[];
|
|
166
166
|
required?: boolean;
|
|
167
167
|
when?: PropCondition;
|
|
168
168
|
} | {
|
|
@@ -231,8 +231,9 @@ interface StyleRuleInput<L extends LayerMap> {
|
|
|
231
231
|
/** Prop → value, all of which must hold. Absent means unconditional. A value is whatever the prop
|
|
232
232
|
* it names offers, so a boolean prop's condition is a boolean. */
|
|
233
233
|
readonly when?: Record<string, string | number | boolean>;
|
|
234
|
-
/** Per layer — and only a layer this component declared.
|
|
235
|
-
|
|
234
|
+
/** Per layer — and only a layer this component declared. A glyph prop of a layer component takes
|
|
235
|
+
* the glyph component; register stores its member. */
|
|
236
|
+
readonly layers: Partial<Record<keyof L & string, Record<string, StyleValue | GlyphComponent>>>;
|
|
236
237
|
}
|
|
237
238
|
/** The TS type one prop declaration produces on the rendered component. */
|
|
238
239
|
type ValueOf<D> = D extends readonly (infer Member)[] ? Member : D extends {
|
|
@@ -243,6 +244,14 @@ type ValueOf<D> = D extends readonly (infer Member)[] ? Member : D extends {
|
|
|
243
244
|
__ref: `style-property:${infer Target}`;
|
|
244
245
|
};
|
|
245
246
|
} ? StylePropValue<Target> : ValueOfDeclaration<D>;
|
|
247
|
+
/**
|
|
248
|
+
* A glyph as a value: the component an icon library exports. Its `displayName` is the member it is,
|
|
249
|
+
* which is what register stores (`icon:phosphor/XIcon`) and what the renderer resolves back to the
|
|
250
|
+
* component. Sizing is the placing component's business, so the one prop every glyph takes is `size`.
|
|
251
|
+
*/
|
|
252
|
+
type GlyphComponent = ComponentType<{
|
|
253
|
+
size?: number;
|
|
254
|
+
}>;
|
|
246
255
|
/**
|
|
247
256
|
* The members a ref-delegated domain offers, read off the registered paths: a variant whose domain
|
|
248
257
|
* is `icon:phosphor` takes every registered `phosphor/<Name>` as `<Name>`, so a misspelled glyph is
|
|
@@ -262,7 +271,13 @@ type StylePropValue<Target extends string> = Target extends keyof RegisteredStyl
|
|
|
262
271
|
type ValueOfDeclaration<D> = D extends {
|
|
263
272
|
type: 'variant';
|
|
264
273
|
value: infer V;
|
|
265
|
-
} ? V extends readonly
|
|
274
|
+
} ? V extends readonly {
|
|
275
|
+
__ref: `icon:${string}`;
|
|
276
|
+
}[] ? GlyphComponent : V extends readonly Ref$1[] ? string : V extends readonly (infer Member)[] ? Member : V extends VariantValues ? keyof V & string : V extends {
|
|
277
|
+
__ref: `${string}#${string}`;
|
|
278
|
+
} ? string : V extends {
|
|
279
|
+
__ref: `icon:${string}`;
|
|
280
|
+
} ? GlyphComponent : V extends {
|
|
266
281
|
__ref: `${infer Kind}:${infer Path}`;
|
|
267
282
|
} ? RegisteredMembers<Kind, Path> : string : D extends {
|
|
268
283
|
type: 'string';
|
|
@@ -690,4 +705,4 @@ interface DefineComponent {
|
|
|
690
705
|
declare function definePropsFirst<const P extends PropMap<LayerPath>>(props: P & NoInfer<{ [K in keyof P]: Exact<P[K], AuthoredProp<LayerPath>> }>): ComponentBuilder<LayerMap, P>;
|
|
691
706
|
declare const defineComponent: DefineComponent;
|
|
692
707
|
//#endregion
|
|
693
|
-
export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, LayerElement, LayerMap, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, StyleRuleInput, defineComponent };
|
|
708
|
+
export { AsInput, AuthoredComponent, AuthoredProp, ComponentBuilder, ComponentPropsOf, GlyphComponent, LayerElement, LayerMap, PhantomCallProps, PhantomReadProps, PropCondition, PropDecl, PropMap, RegisteredMembers, RenderArgs, StyleRuleInput, defineComponent };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { canonicalCondition, isRef, kindOf, memberRef, pathOf } from "../../framework/utils/refs.js";
|
|
2
|
+
import { isComponentType } from "./glyph-identity.js";
|
|
2
3
|
import "../../framework/preview-store.js";
|
|
3
4
|
import { Component, canonicalWhen, layerRef } from "./Component.js";
|
|
4
5
|
//#region src/entities/system/defineComponent.ts
|
|
@@ -44,15 +45,14 @@ function build(body) {
|
|
|
44
45
|
* only one of them runs for any given config: the build reads `.preview()` from source with ts-morph,
|
|
45
46
|
* this runs when a config is registered, and a reader can't tell which made the config it holds.
|
|
46
47
|
*
|
|
47
|
-
* A component handed as a
|
|
48
|
+
* A component handed as a value (`icon: PlusIcon`) stores as the bare ref the registry keys it under,
|
|
48
49
|
* so the receiving component gets the component back rather than a node.
|
|
49
50
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* export
|
|
54
|
-
*
|
|
55
|
-
* which is why ~a third of the element-valued preview props in a real system only survive a build.
|
|
51
|
+
* OPEN: a foreign component (a Phosphor glyph) is dropped. Its export is known once its library has
|
|
52
|
+
* handed `register` its namespace (`glyphIdentityOf`), but a preview entry's props are stored open
|
|
53
|
+
* (`Preview.fields`) and never re-bound to a member, so a ref written here would reach the build as
|
|
54
|
+
* a package export the package row never lists. An absent default renders nothing; the build fills
|
|
55
|
+
* the prop from the import specifier (`extractPreviewProps`).
|
|
56
56
|
*/
|
|
57
57
|
function storableProps(props) {
|
|
58
58
|
if (props === void 0) return void 0;
|
|
@@ -68,11 +68,6 @@ function storableProps(props) {
|
|
|
68
68
|
function isReactElement(value) {
|
|
69
69
|
return typeof value === "object" && value !== null && "$$typeof" in value && "props" in value;
|
|
70
70
|
}
|
|
71
|
-
/** A component TYPE handed as a value: a function, or the object `forwardRef` / `memo` return. */
|
|
72
|
-
function isComponentType(value) {
|
|
73
|
-
if (typeof value === "function") return true;
|
|
74
|
-
return typeof value === "object" && value !== null && "$$typeof" in value && !("props" in value);
|
|
75
|
-
}
|
|
76
71
|
/** A component handed as a value, as the ref that names it — `undefined` for one this path can't name. */
|
|
77
72
|
function storableComponentRef(value) {
|
|
78
73
|
return isComponentElement(value) && value.path ? Component.ref(value.path) : void 0;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { isRef, kindOf, memberOf, pathOf } from "../../framework/utils/refs.js";
|
|
2
|
+
import { spellValues } from "../../framework/value-domain.js";
|
|
3
|
+
import { memberLeaves } from "../../framework/projections.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
//#region src/entities/system/glyph-identity.ts
|
|
6
|
+
const PACKAGE = "package";
|
|
7
|
+
/**
|
|
8
|
+
* Identities by component reference, for the whole process. A component is one object however many
|
|
9
|
+
* configs import it, and which module exports it under which name is a fact about the module, so the
|
|
10
|
+
* map is keyed on the object and not on any config. It is filled while a config file is evaluated
|
|
11
|
+
* (`Icon.group({ glyphs })` runs before `register` does) and read by the open-bag pass, which has no
|
|
12
|
+
* config in hand.
|
|
13
|
+
*/
|
|
14
|
+
const identities = /* @__PURE__ */ new WeakMap();
|
|
15
|
+
/** A component type handed as a value: a function, or the object `forwardRef` / `memo` return. An
|
|
16
|
+
* element (which also carries `$$typeof`) has `props`; a type does not. */
|
|
17
|
+
function isComponentType(value) {
|
|
18
|
+
if (typeof value === "function") return true;
|
|
19
|
+
return typeof value === "object" && value !== null && "$$typeof" in value && !("props" in value);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Record which export each of `names` is in `namespace`, for the components among them.
|
|
23
|
+
*
|
|
24
|
+
* Only the listed names are recorded, so an alias the package also exports (Phosphor's deprecated
|
|
25
|
+
* `X` beside `XIcon`, the same function) resolves to the listed name. A name the namespace does not
|
|
26
|
+
* export as a component is skipped: a virtualized package answers a marker object for every key,
|
|
27
|
+
* and a marker is not a component.
|
|
28
|
+
*
|
|
29
|
+
* One object may be listed under several names of one package: Phosphor keeps a renamed glyph's old
|
|
30
|
+
* name as a second `*Icon` export of the same function (`ArchiveBoxIcon` is `BoxArrowDownIcon`).
|
|
31
|
+
* Both are members, and an author writing either holds the same reference, so one of them has to be
|
|
32
|
+
* the stored name. The name the component carries as its `displayName` wins when it is one of the
|
|
33
|
+
* listed names, since that is the one the package calls current; otherwise the first listed stays.
|
|
34
|
+
* The reference decides which names are in question; `displayName` only picks among them, so a
|
|
35
|
+
* library whose display names are not export names (Tabler) is unaffected.
|
|
36
|
+
*
|
|
37
|
+
* Recording one object under a second package is refused. The map is shared by every config in the
|
|
38
|
+
* process, so a stub component two test fixtures reuse under two libraries would otherwise bind to
|
|
39
|
+
* whichever recorded last. Recording the same identity again is a no-op.
|
|
40
|
+
*/
|
|
41
|
+
function recordGlyphIdentities({ namespace, names, specifier }) {
|
|
42
|
+
for (const name of names) {
|
|
43
|
+
const value = namespace[name];
|
|
44
|
+
if (!isComponentType(value)) continue;
|
|
45
|
+
const prior = identities.get(value);
|
|
46
|
+
if (prior && prior.specifier !== specifier) throw new Error(`A glyph component is already recorded as \`${prior.name}\` of \`${prior.specifier}\`; it cannot also be \`${name}\` of \`${specifier}\`. Each library's glyphs have to be distinct objects.`);
|
|
47
|
+
if (prior && prior.name !== name && displayNameOf(value) !== name) continue;
|
|
48
|
+
identities.set(value, {
|
|
49
|
+
specifier,
|
|
50
|
+
name
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** The identity a component was recorded under, or `undefined` for one no library handed over. */
|
|
55
|
+
function glyphIdentityOf(value) {
|
|
56
|
+
return isComponentType(value) ? identities.get(value) : void 0;
|
|
57
|
+
}
|
|
58
|
+
/** An identity as the ref that spells it: `package:<specifier>#<name>`, the same spelling a
|
|
59
|
+
* virtualized package's marker carries for a member. */
|
|
60
|
+
function glyphPackageRefOf(identity) {
|
|
61
|
+
return { __ref: `${PACKAGE}:${identity.specifier}#${identity.name}` };
|
|
62
|
+
}
|
|
63
|
+
/** The identity a `package:<specifier>#<name>` ref spells, or `undefined` for any other value. */
|
|
64
|
+
function glyphIdentityOfRef(value) {
|
|
65
|
+
if (!isRef(value) || kindOf(value) !== PACKAGE) return void 0;
|
|
66
|
+
const name = memberOf(value);
|
|
67
|
+
return name === void 0 ? void 0 : {
|
|
68
|
+
specifier: pathOf(value),
|
|
69
|
+
name
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function displayNameOf(value) {
|
|
73
|
+
const named = value.displayName;
|
|
74
|
+
return typeof named === "string" ? named : void 0;
|
|
75
|
+
}
|
|
76
|
+
function spellComponent(value) {
|
|
77
|
+
const named = displayNameOf(value);
|
|
78
|
+
return named === void 0 ? "A glyph component" : `\`${named}\``;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The open-bag form of an authored glyph: the imported component, read as the package export it is.
|
|
82
|
+
*
|
|
83
|
+
* The open bag runs before any owner is known, so this cannot bind the glyph to a library; it writes
|
|
84
|
+
* the export as a `package:` ref and the owner-bound pass binds that to the member. A component no
|
|
85
|
+
* library recorded is refused here, since nothing later could name it either: the library did not
|
|
86
|
+
* hand `register` its namespace, the package is not installed (the namespace is then a marker with
|
|
87
|
+
* no components), or the value is a wrapper (`memo(StarIcon)`) rather than the export.
|
|
88
|
+
*/
|
|
89
|
+
const glyphPackageRef = z.custom(isComponentType).transform((value, ctx) => {
|
|
90
|
+
const identity = glyphIdentityOf(value);
|
|
91
|
+
if (!identity) {
|
|
92
|
+
ctx.addIssue({
|
|
93
|
+
code: "custom",
|
|
94
|
+
message: `${spellComponent(value)} is not a glyph of any icon library this config declares. Declare the library's exports on its group (\`Icon.group({ glyphs: <Namespace>, … })\`) and check the package is installed; a wrapped glyph (\`memo(StarIcon)\`) is a different object from the export and cannot be bound.`
|
|
95
|
+
});
|
|
96
|
+
return z.NEVER;
|
|
97
|
+
}
|
|
98
|
+
return glyphPackageRefOf(identity);
|
|
99
|
+
});
|
|
100
|
+
glyphPackageRef._zod.toJSONSchema = () => ({ not: {} });
|
|
101
|
+
/**
|
|
102
|
+
* The owner-bound form of a glyph for one library: a component or its `package:` ref, answered as
|
|
103
|
+
* the member name so the domain binds it like a written leaf. Refused, naming the reason, when the
|
|
104
|
+
* export belongs to another module than the library draws from or is not one of its members.
|
|
105
|
+
*/
|
|
106
|
+
function glyphOfLibrary({ library, module, leaves }) {
|
|
107
|
+
return z.custom((value) => isComponentType(value) || glyphIdentityOfRef(value) !== void 0, { error: () => `a glyph component of the library (one of ${spellValues(leaves)})` }).transform((value, ctx) => {
|
|
108
|
+
const identity = isRef(value) ? glyphIdentityOfRef(value) : glyphIdentityOf(value);
|
|
109
|
+
if (!identity) {
|
|
110
|
+
ctx.addIssue({
|
|
111
|
+
code: "custom",
|
|
112
|
+
message: `${spellComponent(value)} is not a glyph of any icon library this config declares.`
|
|
113
|
+
});
|
|
114
|
+
return z.NEVER;
|
|
115
|
+
}
|
|
116
|
+
if (identity.specifier !== module) {
|
|
117
|
+
ctx.addIssue({
|
|
118
|
+
code: "custom",
|
|
119
|
+
message: `\`${identity.name}\` is an export of \`${identity.specifier}\`, which the library \`${library}\` does not draw from${module === void 0 ? " (it names no package)" : ` (its package is \`${module}\`)`}.`
|
|
120
|
+
});
|
|
121
|
+
return z.NEVER;
|
|
122
|
+
}
|
|
123
|
+
if (!leaves.includes(identity.name)) {
|
|
124
|
+
ctx.addIssue({
|
|
125
|
+
code: "custom",
|
|
126
|
+
message: `\`${identity.name}\` is not a member of the library \`${library}\`${leaves.length === 0 ? ", which has no members; the package may not be installed" : ""}.`
|
|
127
|
+
});
|
|
128
|
+
return z.NEVER;
|
|
129
|
+
}
|
|
130
|
+
return identity.name;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The glyph form for a key that reaches a library through a forward (`icon` → `icon/name`): the
|
|
135
|
+
* component or its `package:` ref, answered as the bare member name. The forwarded key stores the
|
|
136
|
+
* leaf rather than a member ref, and the emitter and the renderer resolve it through the same
|
|
137
|
+
* libraries (`glyphPropsOf`), taking the first that holds it. Each library's arm refuses by cause,
|
|
138
|
+
* so a glyph of a library the key does not reach says so.
|
|
139
|
+
*/
|
|
140
|
+
function glyphLeafOf({ config, libraries }) {
|
|
141
|
+
const arms = libraries.map((library) => {
|
|
142
|
+
const named = config.groupMeta("icon", library)?.package;
|
|
143
|
+
return glyphOfLibrary({
|
|
144
|
+
library,
|
|
145
|
+
module: isRef(named) ? pathOf(named) : void 0,
|
|
146
|
+
leaves: memberLeaves(config, `icon:${library}`)
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
if (arms.length === 1) return arms[0];
|
|
150
|
+
return z.union(arms);
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
export { glyphLeafOf, glyphOfLibrary, glyphPackageRef, isComponentType, recordGlyphIdentities };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Config } from '../../framework/Config';
|
|
2
|
+
/**
|
|
3
|
+
* A prop whose domain is a list of icon-library refs resolves a value by leaf alone, so one leaf
|
|
4
|
+
* held by two of its libraries would draw whichever the host looked up first. This refuses that
|
|
5
|
+
* state, naming the component, the prop and both libraries.
|
|
6
|
+
*
|
|
7
|
+
* Declared from both `Component` and `Icon`, since `assertInvariants` runs only the patched kind's
|
|
8
|
+
* rules: a component binding two libraries is a component write, and a glyph added to one of them
|
|
9
|
+
* later is an icon write, and either can create the collision.
|
|
10
|
+
*/
|
|
11
|
+
export declare function iconDomainInvariants(config: Config): string[];
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { isRef, kindOf, memberOf, pathOf } from "../../framework/utils/refs.js";
|
|
2
|
+
import { collectBag, isPlainRecord } from "./style-bag.js";
|
|
3
|
+
import { createSliceMemo } from "../../framework/memo.js";
|
|
4
|
+
import { glyphLibrariesOf, memberLeaves, refListOf } from "../../framework/projections.js";
|
|
5
|
+
//#region src/entities/system/icon-domain-invariants.ts
|
|
6
|
+
/**
|
|
7
|
+
* A prop whose domain is a list of icon-library refs resolves a value by leaf alone, so one leaf
|
|
8
|
+
* held by two of its libraries would draw whichever the host looked up first. This refuses that
|
|
9
|
+
* state, naming the component, the prop and both libraries.
|
|
10
|
+
*
|
|
11
|
+
* Declared from both `Component` and `Icon`, since `assertInvariants` runs only the patched kind's
|
|
12
|
+
* rules: a component binding two libraries is a component write, and a glyph added to one of them
|
|
13
|
+
* later is an icon write, and either can create the collision.
|
|
14
|
+
*/
|
|
15
|
+
function iconDomainInvariants(config) {
|
|
16
|
+
return [...collisions(config), ...unboundGlyphs(config)];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A glyph authored as its component leaves the open-bag pass as a `package:` export ref, and the
|
|
20
|
+
* owner-bound pass binds that to the member when the key's domain is a library drawing from that
|
|
21
|
+
* module. One left in a bag is a glyph nothing bound, and this refuses it by what went wrong: no
|
|
22
|
+
* library draws from its module, the library does but does not list the export (a library with no
|
|
23
|
+
* members is usually an uninstalled package), or the library lists it and the key's domain is not
|
|
24
|
+
* that library. Only the open-bag pass writes a `package:` ref into a bag value, so a leftover is
|
|
25
|
+
* always one of these. Without the check the ref reaches the build as a package export the package
|
|
26
|
+
* row never lists, and fails there with the wrong cause.
|
|
27
|
+
*/
|
|
28
|
+
const unboundGlyphs = createSliceMemo({
|
|
29
|
+
kinds: ["component", "icon"],
|
|
30
|
+
compute: (config) => {
|
|
31
|
+
const problems = [];
|
|
32
|
+
const librariesByModule = librariesByModuleOf(config);
|
|
33
|
+
const leafSetOf = leafSetsOf(config);
|
|
34
|
+
for (const item of config.list("component")) {
|
|
35
|
+
const body = config.resolve("component", item.path)?.toJSON();
|
|
36
|
+
for (const site of glyphBagSites({
|
|
37
|
+
config,
|
|
38
|
+
path: item.path,
|
|
39
|
+
body
|
|
40
|
+
})) for (const [key, value] of Object.entries(site.bag)) {
|
|
41
|
+
const glyph = packageExportOf(value);
|
|
42
|
+
if (!glyph) continue;
|
|
43
|
+
const holders = librariesByModule.get(glyph.specifier) ?? [];
|
|
44
|
+
const at = `Component "${item.path}" key "${key}"`;
|
|
45
|
+
if (holders.length === 0) {
|
|
46
|
+
problems.push(`${at} holds the glyph \`${glyph.name}\` of \`${glyph.specifier}\`, and no icon library draws from that package; declare the library, or hand its exports to the one that does (\`Icon.group({ glyphs })\`).`);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const listing = holders.filter((library) => leafSetOf(`icon:${library}`).has(glyph.name));
|
|
50
|
+
if (listing.length === 0) {
|
|
51
|
+
const empty = holders.every((library) => leafSetOf(`icon:${library}`).size === 0);
|
|
52
|
+
problems.push(`${at} holds \`${glyph.name}\`, which is not a member of ${spellLibraries(holders)}${empty ? "; the library has no members, so the package may not be installed" : ""}.`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
problems.push(`${at} holds \`${glyph.name}\`, a glyph of ${spellLibraries(listing)}, which that key does not draw from; a glyph belongs on a prop whose domain names its library, or a forward onto one.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return problems;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* A component's bags with the element each one is props for: a rule's layer bags with the layer's
|
|
63
|
+
* element, an anatomy node's props with the node's element, `defaultProps` with the component itself.
|
|
64
|
+
* A bag whose element is a component ref that resolves to nothing is left out: the owner-bound pass
|
|
65
|
+
* had no declaration to bind against, and the dangling element is the ref graph's complaint, not a
|
|
66
|
+
* glyph on the wrong prop.
|
|
67
|
+
*/
|
|
68
|
+
function glyphBagSites({ config, path, body }) {
|
|
69
|
+
const owner = body;
|
|
70
|
+
const sites = [];
|
|
71
|
+
const keep = () => {};
|
|
72
|
+
for (const rule of Object.values(owner?.styles ?? {})) {
|
|
73
|
+
if (!isPlainRecord(rule?.layers)) continue;
|
|
74
|
+
for (const [layer, bag] of Object.entries(rule.layers)) collectBag(bag, keep, sites, owner?.layers?.[layer]);
|
|
75
|
+
}
|
|
76
|
+
for (const node of Object.values(owner?.anatomy ?? {})) {
|
|
77
|
+
if (!isPlainRecord(node)) continue;
|
|
78
|
+
collectBag(node.props, keep, sites, node.element);
|
|
79
|
+
}
|
|
80
|
+
collectBag(owner?.defaultProps, keep, sites, { __ref: `component:${path}` });
|
|
81
|
+
return sites.filter((site) => !(isRef(site.element) && kindOf(site.element) === "component" && !config.resolveRef(site.element.__ref)));
|
|
82
|
+
}
|
|
83
|
+
/** The library paths drawing from each package specifier, off every icon group's `package` ref. */
|
|
84
|
+
function librariesByModuleOf(config) {
|
|
85
|
+
const byModule = /* @__PURE__ */ new Map();
|
|
86
|
+
for (const group of config.groups("icon")) {
|
|
87
|
+
const named = group.meta.package;
|
|
88
|
+
if (!isRef(named)) continue;
|
|
89
|
+
const specifier = pathOf(named);
|
|
90
|
+
byModule.set(specifier, [...byModule.get(specifier) ?? [], group.path]);
|
|
91
|
+
}
|
|
92
|
+
return byModule;
|
|
93
|
+
}
|
|
94
|
+
/** The export a `package:<specifier>#<name>` ref spells, or `undefined` for any other value. */
|
|
95
|
+
function packageExportOf(value) {
|
|
96
|
+
if (!isRef(value) || kindOf(value) !== "package") return void 0;
|
|
97
|
+
const name = memberOf(value);
|
|
98
|
+
return name === void 0 ? void 0 : {
|
|
99
|
+
specifier: pathOf(value),
|
|
100
|
+
name
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function spellLibraries(libraries) {
|
|
104
|
+
const named = libraries.map((library) => `"${library}"`);
|
|
105
|
+
return libraries.length === 1 ? `the library ${named[0]}` : `the libraries ${named.join(", ")}`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Keyed on the two slices the check reads, so the second kind's run within one registration is a
|
|
109
|
+
* hit, and a write to any other kind never pays for it. A library's leaf set is keyed on the icon
|
|
110
|
+
* slice alone: a component edit re-walks the list-valued props against sets that are already built.
|
|
111
|
+
*/
|
|
112
|
+
const collisions = createSliceMemo({
|
|
113
|
+
kinds: ["component", "icon"],
|
|
114
|
+
compute: (config) => {
|
|
115
|
+
const problems = [];
|
|
116
|
+
const leafSetOf = leafSetsOf(config);
|
|
117
|
+
for (const item of config.list("component")) {
|
|
118
|
+
const body = config.resolve("component", item.path)?.toJSON();
|
|
119
|
+
for (const [prop, decl] of Object.entries(body?.props ?? {})) {
|
|
120
|
+
const refs = listedLibraryRefsOf(decl);
|
|
121
|
+
if (!refs) continue;
|
|
122
|
+
const heldBy = /* @__PURE__ */ new Map();
|
|
123
|
+
const shared = /* @__PURE__ */ new Map();
|
|
124
|
+
for (const target of refs) for (const leaf of leafSetOf(target)) {
|
|
125
|
+
const other = heldBy.get(leaf);
|
|
126
|
+
if (other === void 0) {
|
|
127
|
+
heldBy.set(leaf, target);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (other === target) continue;
|
|
131
|
+
const pair = `${other} + ${target}`;
|
|
132
|
+
shared.set(pair, [...shared.get(pair) ?? [], leaf]);
|
|
133
|
+
}
|
|
134
|
+
for (const [pair, leaves] of shared) {
|
|
135
|
+
const [first, second] = pair.split(" + ");
|
|
136
|
+
problems.push(`Component "${item.path}" prop "${prop}" binds icon libraries "${first}" and "${second}", which both ship ${spellLeaves(leaves)}; a leaf has to name one glyph.`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return problems;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
/** One leaf set per referenced library, filled on demand and kept while the icon slice stands. */
|
|
144
|
+
const leafSetsOf = createSliceMemo({
|
|
145
|
+
kinds: ["icon"],
|
|
146
|
+
compute: (config) => {
|
|
147
|
+
const sets = /* @__PURE__ */ new Map();
|
|
148
|
+
return (target) => {
|
|
149
|
+
const hit = sets.get(target);
|
|
150
|
+
if (hit) return hit;
|
|
151
|
+
const built = new Set(memberLeaves(config, target));
|
|
152
|
+
sets.set(target, built);
|
|
153
|
+
return built;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
/** The colliding names, capped so two libraries sharing hundreds refuse in one sentence. */
|
|
158
|
+
function spellLeaves(leaves) {
|
|
159
|
+
const shown = leaves.slice(0, 8).map((leaf) => `"${leaf}"`);
|
|
160
|
+
const rest = leaves.length - shown.length;
|
|
161
|
+
const named = rest > 0 ? `${shown.join(", ")} and ${rest} more` : shown.join(", ");
|
|
162
|
+
return leaves.length === 1 ? `a glyph named ${named}` : `glyphs named ${named}`;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The icon-library refs of a variant prop whose domain is a list of refs, else `undefined`. Each
|
|
166
|
+
* element is put through `glyphLibrariesOf` on its own, so the ref comes back whole: the leaf sets
|
|
167
|
+
* are read at the source the ref names, which a group path alone cannot say. A facet ref
|
|
168
|
+
* (`icon:a#variants`) is not a library, and a ref of another kind is left to that kind's rules.
|
|
169
|
+
*/
|
|
170
|
+
function listedLibraryRefsOf(decl) {
|
|
171
|
+
if (decl === null || typeof decl !== "object") return void 0;
|
|
172
|
+
const { type, value } = decl;
|
|
173
|
+
if (type !== "variant") return void 0;
|
|
174
|
+
const listed = refListOf(value)?.filter((target) => glyphLibrariesOf({ __ref: target }).length > 0);
|
|
175
|
+
return listed && listed.length > 0 ? listed : void 0;
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
178
|
+
export { iconDomainInvariants };
|