@yahoo/uds-create-config 2.44.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/ai-lanes.d.ts +23 -0
- package/dist/ai-lanes.js +34 -0
- package/dist/authoring/define-component.d.ts +85 -0
- package/dist/configs/CanvasConfig.d.ts +3237 -0
- package/dist/configs/CanvasConfig.js +332 -0
- package/dist/configs/build-options.d.ts +10 -0
- package/dist/configs/build-options.js +68 -0
- package/dist/configs/platform.d.ts +15 -0
- package/dist/configs/platform.js +17 -0
- package/dist/configs/react-native-system.d.ts +3026 -0
- package/dist/configs/react-native-system.js +64 -0
- package/dist/configs/system.d.ts +2771 -0
- package/dist/configs/system.js +74 -0
- package/dist/css/properties.generated.d.ts +14 -0
- package/dist/css/properties.generated.js +6260 -0
- package/dist/css/value-type.d.ts +48 -0
- package/dist/css/value-type.js +131 -0
- package/dist/css/values.d.ts +33 -0
- package/dist/css/values.js +116 -0
- package/dist/entities/native/NativeModifier.d.ts +25 -0
- package/dist/entities/native/NativeModifier.js +41 -0
- package/dist/entities/native/NativeSettings.d.ts +44 -0
- package/dist/entities/native/NativeSettings.js +39 -0
- package/dist/entities/native/NativeStyleProperty.d.ts +355 -0
- package/dist/entities/native/NativeStyleProperty.js +28 -0
- package/dist/entities/native/NativeToken.d.ts +31 -0
- package/dist/entities/native/NativeToken.js +49 -0
- package/dist/entities/system/CanvasRole.d.ts +34 -0
- package/dist/entities/system/CanvasRole.js +45 -0
- package/dist/entities/system/Component.d.ts +1287 -0
- package/dist/entities/system/Component.js +1840 -0
- package/dist/entities/system/Composite.d.ts +12 -0
- package/dist/entities/system/Composite.js +26 -0
- package/dist/entities/system/Device.d.ts +11 -0
- package/dist/entities/system/Device.js +18 -0
- package/dist/entities/system/File.d.ts +72 -0
- package/dist/entities/system/File.js +136 -0
- package/dist/entities/system/Font.d.ts +48 -0
- package/dist/entities/system/Font.js +56 -0
- package/dist/entities/system/GlobalStyle.d.ts +26 -0
- package/dist/entities/system/GlobalStyle.js +54 -0
- package/dist/entities/system/Guidance.d.ts +85 -0
- package/dist/entities/system/Guidance.js +59 -0
- package/dist/entities/system/GuidanceStyle.d.ts +14 -0
- package/dist/entities/system/GuidanceStyle.js +8 -0
- package/dist/entities/system/Icon.d.ts +46 -0
- package/dist/entities/system/Icon.js +46 -0
- package/dist/entities/system/LinkedSystem.d.ts +504 -0
- package/dist/entities/system/LinkedSystem.js +814 -0
- package/dist/entities/system/Modifier.d.ts +92 -0
- package/dist/entities/system/Modifier.js +120 -0
- package/dist/entities/system/Motion.d.ts +28 -0
- package/dist/entities/system/Motion.js +48 -0
- package/dist/entities/system/Operation.d.ts +100 -0
- package/dist/entities/system/Operation.js +236 -0
- package/dist/entities/system/Package.d.ts +42 -0
- package/dist/entities/system/Package.js +75 -0
- package/dist/entities/system/Settings.d.ts +94 -0
- package/dist/entities/system/Settings.js +166 -0
- package/dist/entities/system/StyleProperty.d.ts +135 -0
- package/dist/entities/system/StyleProperty.js +438 -0
- package/dist/entities/system/Token.d.ts +67 -0
- package/dist/entities/system/Token.js +129 -0
- package/dist/entities/system/Tool.d.ts +134 -0
- package/dist/entities/system/Tool.js +124 -0
- package/dist/entities/system/color.d.ts +64 -0
- package/dist/entities/system/color.js +123 -0
- package/dist/entities/system/component-module.d.ts +63 -0
- package/dist/entities/system/component-module.js +258 -0
- package/dist/entities/system/copy-plan.d.ts +98 -0
- package/dist/entities/system/copy-plan.js +451 -0
- package/dist/entities/system/defineComponent.d.ts +428 -0
- package/dist/entities/system/defineComponent.js +337 -0
- package/dist/entities/system/element.d.ts +101 -0
- package/dist/entities/system/element.js +171 -0
- package/dist/entities/system/icon-library.d.ts +64 -0
- package/dist/entities/system/icon-library.js +112 -0
- package/dist/entities/system/icon-metadata.d.ts +72 -0
- package/dist/entities/system/icon-metadata.js +111 -0
- package/dist/entities/system/link-declaration.d.ts +27 -0
- package/dist/entities/system/link-declaration.js +64 -0
- package/dist/entities/system/link-plan.d.ts +160 -0
- package/dist/entities/system/link-plan.js +770 -0
- package/dist/entities/system/runtime-modules.d.ts +25 -0
- package/dist/entities/system/runtime-modules.js +38 -0
- package/dist/entities/system/style-bag.d.ts +20 -0
- package/dist/entities/system/style-bag.js +246 -0
- package/dist/entities/system/token-index.d.ts +89 -0
- package/dist/entities/system/token-index.js +112 -0
- package/dist/framework/Config.d.ts +2472 -0
- package/dist/framework/Config.js +5023 -0
- package/dist/framework/Entity.d.ts +127 -0
- package/dist/framework/Entity.js +125 -0
- package/dist/framework/authoring.d.ts +24 -0
- package/dist/framework/authoring.js +7 -0
- package/dist/framework/change-hooks.d.ts +119 -0
- package/dist/framework/change-hooks.js +131 -0
- package/dist/framework/changes.d.ts +88 -0
- package/dist/framework/changes.js +246 -0
- package/dist/framework/class-names.d.ts +260 -0
- package/dist/framework/class-names.js +630 -0
- package/dist/framework/coalesce.d.ts +25 -0
- package/dist/framework/coalesce.js +60 -0
- package/dist/framework/config-op.d.ts +76 -0
- package/dist/framework/config-op.js +43 -0
- package/dist/framework/config-operation-kind.d.ts +14 -0
- package/dist/framework/config-operation-kind.js +14 -0
- package/dist/framework/config-ops.d.ts +34 -0
- package/dist/framework/config-ops.js +187 -0
- package/dist/framework/defineConfig.d.ts +196 -0
- package/dist/framework/defineConfig.js +100 -0
- package/dist/framework/defineDerivedEntity.d.ts +74 -0
- package/dist/framework/defineDerivedEntity.js +83 -0
- package/dist/framework/defineEntity.d.ts +318 -0
- package/dist/framework/defineEntity.js +183 -0
- package/dist/framework/defineSubEntity.d.ts +148 -0
- package/dist/framework/defineSubEntity.js +121 -0
- package/dist/framework/derived-mutations.d.ts +20 -0
- package/dist/framework/derived-mutations.js +38 -0
- package/dist/framework/entity-search.d.ts +29 -0
- package/dist/framework/entity-search.js +117 -0
- package/dist/framework/inline-sources.d.ts +49 -0
- package/dist/framework/inline-sources.js +388 -0
- package/dist/framework/layer-styles.d.ts +25 -0
- package/dist/framework/layer-styles.js +49 -0
- package/dist/framework/memo.d.ts +48 -0
- package/dist/framework/memo.js +84 -0
- package/dist/framework/overlay.d.ts +49 -0
- package/dist/framework/overlay.js +17 -0
- package/dist/framework/plan.d.ts +49 -0
- package/dist/framework/plan.js +73 -0
- package/dist/framework/projections.d.ts +480 -0
- package/dist/framework/projections.js +974 -0
- package/dist/framework/prop-surface.d.ts +132 -0
- package/dist/framework/prop-surface.js +278 -0
- package/dist/framework/ref-graph.d.ts +71 -0
- package/dist/framework/ref-graph.js +158 -0
- package/dist/framework/ref-integrity.d.ts +55 -0
- package/dist/framework/ref-integrity.js +171 -0
- package/dist/framework/registered.d.ts +95 -0
- package/dist/framework/registry-dir.d.ts +42 -0
- package/dist/framework/registry-dir.js +48 -0
- package/dist/framework/registry.d.ts +300 -0
- package/dist/framework/registry.js +104 -0
- package/dist/framework/rejection.d.ts +58 -0
- package/dist/framework/rejection.js +111 -0
- package/dist/framework/rename-inference.d.ts +26 -0
- package/dist/framework/rename-inference.js +50 -0
- package/dist/framework/render-spec.d.ts +296 -0
- package/dist/framework/render-spec.js +809 -0
- package/dist/framework/schema-version.d.ts +140 -0
- package/dist/framework/schema-version.js +214 -0
- package/dist/framework/schemas.d.ts +109 -0
- package/dist/framework/schemas.js +615 -0
- package/dist/framework/session.d.ts +196 -0
- package/dist/framework/session.js +367 -0
- package/dist/framework/signature.d.ts +15 -0
- package/dist/framework/signature.js +83 -0
- package/dist/framework/snapshot.d.ts +115 -0
- package/dist/framework/snapshot.js +277 -0
- package/dist/framework/source-integrity.d.ts +226 -0
- package/dist/framework/source-integrity.js +478 -0
- package/dist/framework/sources.d.ts +211 -0
- package/dist/framework/sources.js +89 -0
- package/dist/framework/utils/compiled-schema.d.ts +23 -0
- package/dist/framework/utils/compiled-schema.js +46 -0
- package/dist/framework/utils/content-key.d.ts +15 -0
- package/dist/framework/utils/content-key.js +33 -0
- package/dist/framework/utils/enumerated.d.ts +13 -0
- package/dist/framework/utils/enumerated.js +19 -0
- package/dist/framework/utils/field-path.d.ts +95 -0
- package/dist/framework/utils/field-path.js +136 -0
- package/dist/framework/utils/field-schema.d.ts +55 -0
- package/dist/framework/utils/field-schema.js +183 -0
- package/dist/framework/utils/group.d.ts +37 -0
- package/dist/framework/utils/group.js +13 -0
- package/dist/framework/utils/package-path.d.ts +37 -0
- package/dist/framework/utils/package-path.js +61 -0
- package/dist/framework/utils/refs.d.ts +194 -0
- package/dist/framework/utils/refs.js +403 -0
- package/dist/framework/validate-spec.d.ts +27 -0
- package/dist/framework/validate-spec.js +45 -0
- package/dist/framework/value-domain.d.ts +26 -0
- package/dist/framework/value-domain.js +202 -0
- package/dist/framework/views-facade.d.ts +15 -0
- package/dist/framework/views-facade.js +19 -0
- package/dist/index.d.ts +90 -42
- package/dist/index.js +86 -37
- package/dist/migrations/2.0.0/raw-css.d.ts +11 -0
- package/dist/migrations/2.0.0/raw-css.js +72 -0
- package/dist/migrations/2.0.0/v1-artifact.d.ts +1559 -0
- package/dist/migrations/2.0.0/v1-artifact.js +1018 -0
- package/dist/migrations/20260908171448_settings_as_kind.d.ts +22 -0
- package/dist/migrations/20260908171448_settings_as_kind.js +89 -0
- package/dist/migrations/20260908194245_visible_state_pointer.d.ts +19 -0
- package/dist/migrations/20260908194245_visible_state_pointer.js +115 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.d.ts +18 -0
- package/dist/migrations/20260909183001_icon_knockout_variants.js +8 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.d.ts +13 -0
- package/dist/migrations/20260910212453_linked_system_link_ops.js +8 -0
- package/dist/migrations/index.d.ts +2 -0
- package/dist/migrations/index.js +2 -0
- package/dist/react-native/style-keys.generated.d.ts +10 -0
- package/dist/react-native/style-keys.generated.js +167 -0
- package/dist/renderer/RendererErrorBoundary.d.ts +32 -5
- package/dist/renderer/RendererErrorBoundary.js +59 -10
- package/dist/renderer/UdsRenderer.d.ts +11 -10
- package/dist/renderer/UdsRenderer.js +1 -1
- package/dist/renderer/assetEntries.d.ts +37 -0
- package/dist/renderer/assetEntries.js +39 -0
- package/dist/renderer/assetRenderable.d.ts +14 -6
- package/dist/renderer/assetRenderable.js +8 -2
- package/dist/renderer/componentEntry.d.ts +58 -0
- package/dist/renderer/componentEntry.js +71 -0
- package/dist/renderer/icon-library.d.ts +20 -0
- package/dist/renderer/index.d.ts +7 -7
- package/dist/renderer/index.js +5 -5
- package/dist/renderer/primitives/SlotRenderer.d.ts +1 -1
- package/dist/renderer/primitives/SlotRenderer.js +1 -1
- package/dist/renderer/runtimeComponent.d.ts +33 -0
- package/dist/renderer/runtimeComponent.js +113 -0
- package/dist/renderer/spec-content.d.ts +43 -0
- package/dist/renderer/spec-content.js +95 -0
- package/dist/renderer/wrapRegistry.d.ts +22 -37
- package/dist/renderer/wrapRegistry.js +10 -20
- package/dist/renderer/wrappers/event-bridge.js +6 -5
- package/dist/renderer/wrappers/hex-normalize.js +6 -5
- package/dist/renderer/wrappers/html-aliases.d.ts +7 -2
- package/dist/renderer/wrappers/html-aliases.js +36 -19
- package/dist/renderer/wrappers/inline-styles.js +7 -6
- package/dist/spec/asset-jsx.d.ts +18 -5
- package/dist/spec/asset-jsx.js +27 -16
- package/dist/spec/collapse-text-labels.d.ts +5 -15
- package/dist/spec/collapse-text-labels.js +26 -19
- package/dist/spec/empty-node-slots.d.ts +56 -34
- package/dist/spec/empty-node-slots.js +87 -47
- package/dist/spec/index.d.ts +21 -10
- package/dist/spec/index.js +21 -10
- package/dist/spec/jsxToSpec.d.ts +14 -9
- package/dist/spec/jsxToSpec.js +20 -3
- package/dist/spec/preview-elements.d.ts +63 -0
- package/dist/spec/preview-elements.js +89 -0
- package/dist/spec/rewrite-refs.d.ts +58 -0
- package/dist/spec/rewrite-refs.js +250 -0
- package/dist/spec/specToJsx.js +18 -29
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +39 -52
- package/dist/AssetGroup.d.ts +0 -77
- package/dist/AssetGroup.js +0 -125
- package/dist/Component.d.ts +0 -333
- package/dist/Component.js +0 -1061
- package/dist/ComponentGroup.d.ts +0 -22
- package/dist/ComponentGroup.js +0 -51
- package/dist/CompositeStyle.d.ts +0 -30
- package/dist/CompositeStyle.js +0 -59
- package/dist/Config.d.ts +0 -528
- package/dist/Config.js +0 -1712
- package/dist/Mode.d.ts +0 -41
- package/dist/Mode.js +0 -81
- package/dist/Modifier.d.ts +0 -51
- package/dist/Modifier.js +0 -97
- package/dist/MotionDef.d.ts +0 -81
- package/dist/MotionDef.js +0 -212
- package/dist/Props.d.ts +0 -332
- package/dist/Props.js +0 -35
- package/dist/Provider.d.ts +0 -20
- package/dist/Provider.js +0 -14
- package/dist/StyleProp.d.ts +0 -145
- package/dist/StyleProp.js +0 -239
- package/dist/Token.d.ts +0 -76
- package/dist/Token.js +0 -133
- package/dist/TokenGroup.d.ts +0 -33
- package/dist/TokenGroup.js +0 -69
- package/dist/asset-kind.d.ts +0 -55
- package/dist/asset-kind.js +0 -29
- package/dist/asset-value.d.ts +0 -23
- package/dist/asset-value.js +0 -40
- package/dist/brands.d.ts +0 -30
- package/dist/brands.js +0 -20
- package/dist/captureCallerPath.d.ts +0 -48
- package/dist/captureCallerPath.js +0 -95
- package/dist/colorExpressions.d.ts +0 -131
- package/dist/colorExpressions.js +0 -148
- package/dist/component-referrers.d.ts +0 -30
- package/dist/component-referrers.js +0 -40
- package/dist/config-diagnostics.d.ts +0 -21
- package/dist/config-diagnostics.js +0 -12
- package/dist/defineAssetGroup.d.ts +0 -200
- package/dist/defineAssetGroup.js +0 -323
- package/dist/defineProvider.d.ts +0 -29
- package/dist/defineProvider.js +0 -60
- package/dist/element-marker.d.ts +0 -63
- package/dist/element-marker.js +0 -113
- package/dist/entity-utils.d.ts +0 -56
- package/dist/entity-utils.js +0 -105
- package/dist/extension.d.ts +0 -55
- package/dist/extension.js +0 -214
- package/dist/factories.d.ts +0 -863
- package/dist/factories.js +0 -475
- package/dist/font-face.d.ts +0 -25
- package/dist/font-face.js +0 -72
- package/dist/foreign-component-name.d.ts +0 -21
- package/dist/foreign-component-name.js +0 -42
- package/dist/interpolate.d.ts +0 -20
- package/dist/interpolate.js +0 -10
- package/dist/jsx/__fixtures__/cross-component-preview.d.ts +0 -3
- package/dist/jsx/__fixtures__/cross-component-preview.js +0 -15
- package/dist/jsx/jsx-dev-runtime.d.ts +0 -15
- package/dist/jsx/jsx-dev-runtime.js +0 -11
- package/dist/jsx/jsx-runtime.d.ts +0 -48
- package/dist/jsx/jsx-runtime.js +0 -305
- package/dist/linked-system-pins.d.ts +0 -30
- package/dist/linked-system-pins.js +0 -45
- package/dist/linkedNodePrefixes.d.ts +0 -73
- package/dist/linkedNodePrefixes.js +0 -99
- package/dist/markers.d.ts +0 -235
- package/dist/markers.js +0 -70
- package/dist/motion-constants.d.ts +0 -37
- package/dist/motion-constants.js +0 -61
- package/dist/native/NativeConfig.d.ts +0 -56
- package/dist/native/NativeConfig.js +0 -162
- package/dist/native/assets.d.ts +0 -20
- package/dist/native/assets.js +0 -17
- package/dist/native/components.d.ts +0 -139
- package/dist/native/components.js +0 -67
- package/dist/native/index.d.ts +0 -18
- package/dist/native/index.js +0 -17
- package/dist/native/modes.d.ts +0 -10
- package/dist/native/modes.js +0 -11
- package/dist/native/types.d.ts +0 -95
- package/dist/native/types.js +0 -0
- package/dist/native/validation.d.ts +0 -8
- package/dist/native/validation.js +0 -885
- package/dist/native/values.d.ts +0 -7
- package/dist/native/values.js +0 -15
- package/dist/native.d.ts +0 -12
- package/dist/native.js +0 -10
- package/dist/refs.d.ts +0 -204
- package/dist/refs.js +0 -129
- package/dist/registry-aliases.d.ts +0 -33
- package/dist/registry-aliases.js +0 -35
- package/dist/registry-dir.d.ts +0 -22
- package/dist/registry-dir.js +0 -10
- package/dist/renderer/makeRegistry.d.ts +0 -34
- package/dist/renderer/makeRegistry.js +0 -52
- package/dist/renderer/makeUdsRenderer.d.ts +0 -13
- package/dist/renderer/makeUdsRenderer.js +0 -51
- package/dist/renderer/wrappers/component-slots.d.ts +0 -41
- package/dist/renderer/wrappers/component-slots.js +0 -66
- package/dist/renderer/wrappers/slot-resolution.d.ts +0 -25
- package/dist/renderer/wrappers/slot-resolution.js +0 -68
- package/dist/renderer/wrappers/void-elements.d.ts +0 -37
- package/dist/renderer/wrappers/void-elements.js +0 -40
- package/dist/spec/layer-props.d.ts +0 -52
- package/dist/spec/layer-props.js +0 -149
- package/dist/spec/preview-controls.d.ts +0 -44
- package/dist/spec/preview-controls.js +0 -139
- package/dist/spec/slot-refs.d.ts +0 -39
- package/dist/spec/slot-refs.js +0 -56
- package/dist/token-override-rows.d.ts +0 -68
- package/dist/token-override-rows.js +0 -233
- package/dist/tokenValueType.d.ts +0 -34
- package/dist/tokenValueType.js +0 -138
- package/dist/types/css-properties.d.ts +0 -233
- package/dist/types/css-properties.js +0 -14
- package/dist/types/css-property-keywords.d.ts +0 -156
- package/dist/types/css-property-keywords.js +0 -616
- package/dist/types/css-values.d.ts +0 -63
- package/dist/types/css-values.js +0 -16
- package/dist/types.d.ts +0 -1138
- package/dist/types.js +0 -12
- package/dist/units.d.ts +0 -14
- package/dist/units.js +0 -16
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.js +0 -4
|
@@ -0,0 +1,1018 @@
|
|
|
1
|
+
import { isRef, pathOf, splitRef } from "../../framework/utils/refs.js";
|
|
2
|
+
import { alpha, darken, lighten, mix } from "../../entities/system/color.js";
|
|
3
|
+
import { isCssProperty, kebabCase } from "../../css/values.js";
|
|
4
|
+
import { Composite } from "../../entities/system/Composite.js";
|
|
5
|
+
import { Modifier } from "../../entities/system/Modifier.js";
|
|
6
|
+
import { StyleProperty } from "../../entities/system/StyleProperty.js";
|
|
7
|
+
import { Component, canonicalWhen } from "../../entities/system/Component.js";
|
|
8
|
+
import { Icon } from "../../entities/system/Icon.js";
|
|
9
|
+
import { stableStringify } from "../../framework/utils/content-key.js";
|
|
10
|
+
import { CanvasRole } from "../../entities/system/CanvasRole.js";
|
|
11
|
+
import { Font } from "../../entities/system/Font.js";
|
|
12
|
+
import { GlobalStyle, globalStyleName } from "../../entities/system/GlobalStyle.js";
|
|
13
|
+
import { Guidance } from "../../entities/system/Guidance.js";
|
|
14
|
+
import { Motion } from "../../entities/system/Motion.js";
|
|
15
|
+
import { Token, overrideKey } from "../../entities/system/Token.js";
|
|
16
|
+
import { danglingLocalRefs } from "../../framework/ref-integrity.js";
|
|
17
|
+
import { parseRawCss } from "./raw-css.js";
|
|
18
|
+
import { System } from "../../configs/system.js";
|
|
19
|
+
//#region src/migrations/2.0.0/v1-artifact.ts
|
|
20
|
+
/**
|
|
21
|
+
* The v1 → config-v2 reader, for `uds migrate` and nothing else.
|
|
22
|
+
*
|
|
23
|
+
* A v1 system's authoring vocabulary is gone, so its `uds.config.ts` cannot be imported — but what
|
|
24
|
+
* that config DESCRIBED survives in the flat `config.json` its last build wrote. This reads that
|
|
25
|
+
* artifact into a config-v2 `System`, which `uds migrate` then renders back out as v2 source.
|
|
26
|
+
*
|
|
27
|
+
* **Runtime code must not come through here.** Every stored artifact is the config-v2 wire form and
|
|
28
|
+
* `Config.fromJSON` is the one door to it; this exists for the one-time port of a repo that never
|
|
29
|
+
* built on the new version. It is also best-effort in a way a runtime path must not be: the few old
|
|
30
|
+
* shapes config-v2 doesn't model (color expressions, component `preview`) pass through or drop
|
|
31
|
+
* rather than failing the load, so a config read through here is the
|
|
32
|
+
* starting point for a migration, not a faithful copy of a system.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Default authoring guidance seeded onto a system that declares none — the stop-gap that fills
|
|
36
|
+
* the "guidance gap" until a design system authors its own `guidance` entities. These are the
|
|
37
|
+
* design-quality rules whose absence the ai-eval judges flagged; the pipeline derives them via
|
|
38
|
+
* `ctx.guidance()`, keeping guidance on the system rather than hardcoded in the flow.
|
|
39
|
+
*/
|
|
40
|
+
const DEFAULT_GUIDANCE = {
|
|
41
|
+
completeness: Guidance.group({
|
|
42
|
+
label: "Completeness",
|
|
43
|
+
description: "How much of the request to build, and with what content.",
|
|
44
|
+
items: { "build-everything": Guidance.item({ text: ["Build the ENTIRE request — every section and item, with REALISTIC content (real labels, numbers, names). Never leave empty rows/containers or use placeholders like \"Product Name\"."] }) }
|
|
45
|
+
}),
|
|
46
|
+
hierarchy: Guidance.group({
|
|
47
|
+
label: "Hierarchy",
|
|
48
|
+
description: "How to rank elements by importance.",
|
|
49
|
+
items: { "size-text-by-role": Guidance.item({ text: ["Size text by role (page title > section heading > body > caption); never make a label larger than its value."] }) }
|
|
50
|
+
}),
|
|
51
|
+
semantics: Guidance.group({
|
|
52
|
+
label: "Semantics",
|
|
53
|
+
description: "How to match components and prop values to intent.",
|
|
54
|
+
items: { "match-props-to-intent": Guidance.item({ text: ["Pick the right component for each job and match prop values to INTENT, using the names the reference lists: the emphasis/primary variant for the main action, the destructive variant for a delete, and contrasting colors for positive vs negative figures."] }) }
|
|
55
|
+
}),
|
|
56
|
+
accessibility: Guidance.group({
|
|
57
|
+
label: "Accessibility",
|
|
58
|
+
description: "The bar every generated design is held to.",
|
|
59
|
+
items: {
|
|
60
|
+
"contrast-in-every-mode": Guidance.item({ text: ["Target WCAG 2.1 AA contrast in every color mode. On colored or photographic backgrounds use neutral text roles, and check the pair each token RESOLVES to per mode rather than how its base value looks."] }),
|
|
61
|
+
"protect-image-text": Guidance.item({ text: ["When the request places copy over a background image or layered media, keep the overlay. Make the text fully opaque and put a strong enough surface, gradient, or scrim behind it to reach WCAG 2.1 AA."] })
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
/** Markers that compute a colour rather than name one — a VALUE in config-v2, not a ref. */
|
|
66
|
+
const COLOR_OPS = new Set([
|
|
67
|
+
"darken",
|
|
68
|
+
"lighten",
|
|
69
|
+
"alpha",
|
|
70
|
+
"mix"
|
|
71
|
+
]);
|
|
72
|
+
/** Gradient markers, by the CSS function each names. */
|
|
73
|
+
const GRADIENTS = {
|
|
74
|
+
"linear-gradient": "linear",
|
|
75
|
+
"radial-gradient": "radial",
|
|
76
|
+
"conic-gradient": "conic"
|
|
77
|
+
};
|
|
78
|
+
/** Old marker `{ __kind, ref }` → the config-v2 ref kind that fronts it. */
|
|
79
|
+
const REF_KIND = {
|
|
80
|
+
token: "token",
|
|
81
|
+
tokenGroup: "token",
|
|
82
|
+
styleProperty: "style-property",
|
|
83
|
+
composite: "composite",
|
|
84
|
+
assetGroup: "font",
|
|
85
|
+
component: "component",
|
|
86
|
+
mode: "mode"
|
|
87
|
+
};
|
|
88
|
+
/** A converted element that points at another component (`{ __ref: 'component:Box' }`) rather
|
|
89
|
+
* than naming a native tag. */
|
|
90
|
+
const isConfigRef = (v) => typeof v === "object" && v !== null && "__ref" in v;
|
|
91
|
+
const isMarker = (v) => typeof v === "object" && v !== null && "__kind" in v;
|
|
92
|
+
/** Deep-convert a value: old `{ __kind:'token', ref }` markers become `{ __ref:'token:…' }`,
|
|
93
|
+
* objects/arrays recurse, primitives pass through. Handles style bags, variant maps, etc. */
|
|
94
|
+
/**
|
|
95
|
+
* Asset library name → the config-v2 kind that fronts it, for the config being loaded.
|
|
96
|
+
*
|
|
97
|
+
* Module-scoped because `convertValue` is called from a dozen places and threading a resolver
|
|
98
|
+
* through all of them would obscure them; a load is synchronous, so the map is only ever the one in
|
|
99
|
+
* flight. Without it every `assetGroup('icons')` marker resolved to `font:icons` — a slot that
|
|
100
|
+
* accepts an ICON library, typed as a font.
|
|
101
|
+
*/
|
|
102
|
+
let assetKinds = {};
|
|
103
|
+
/**
|
|
104
|
+
* What the reader could not carry, by a fixed code so a caller can tell an intended drop from a loss.
|
|
105
|
+
* `loss` is the code's verdict: a hole never held anything, where an unrecognized marker did.
|
|
106
|
+
*/
|
|
107
|
+
const V1_DIAGNOSTIC_CODES = {
|
|
108
|
+
/** A prop the artifact stores as `null`: a declaration that named no marker. */
|
|
109
|
+
"prop-hole-dropped": { loss: false },
|
|
110
|
+
/** A prop marker config-v2 has no type for. */
|
|
111
|
+
"prop-unrecognized": { loss: true },
|
|
112
|
+
/** A layer value that is neither a tag, a component ref, nor an inline component body. */
|
|
113
|
+
"layer-unrecognized": { loss: true },
|
|
114
|
+
/** A style bag addressed to a layer the component does not declare. */
|
|
115
|
+
"style-layer-undeclared": { loss: true },
|
|
116
|
+
/** A component that took content without declaring `children`; the slot is declared for it. */
|
|
117
|
+
"children-slot-inferred": { loss: false },
|
|
118
|
+
/** CSS whose syntax or ordered entries cannot be represented by a global style. */
|
|
119
|
+
"raw-css-unparsed": { loss: true },
|
|
120
|
+
/** A selector modifier v1 also guarded with a media query; a v2 modifier activates one way. The
|
|
121
|
+
* guard only kept `:hover` off touch devices, which is accepted rather than carried. */
|
|
122
|
+
"modifier-media-dropped": { loss: false },
|
|
123
|
+
/** A component with layers and no `root`; the first layer is renamed, and the render must follow. */
|
|
124
|
+
"root-layer-renamed": { loss: true },
|
|
125
|
+
/** A ref the artifact spells to something it never declares; v1 emitted nothing, v2 refuses. A
|
|
126
|
+
* loss when carried; a note under `dropUnresolved`, since what is removed painted nothing. */
|
|
127
|
+
"ref-unresolved": { loss: true },
|
|
128
|
+
/** A style bag key that is neither a style prop, a CSS property, nor a prop of the layer's
|
|
129
|
+
* element. v1 wrote it as a declaration no browser parses, so it painted nothing. */
|
|
130
|
+
"style-key-unrecognized": { loss: false }
|
|
131
|
+
};
|
|
132
|
+
/** Module-scoped for the same reason `assetKinds` is: one load is in flight at a time. */
|
|
133
|
+
let diagnose = () => {};
|
|
134
|
+
function report(code, path, detail) {
|
|
135
|
+
diagnose({
|
|
136
|
+
code,
|
|
137
|
+
path,
|
|
138
|
+
detail,
|
|
139
|
+
loss: V1_DIAGNOSTIC_CODES[code].loss
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The config-v2 kind an asset marker fronts, or `undefined` when it isn't one.
|
|
144
|
+
*
|
|
145
|
+
* v1 had two spellings for the same split-in-two model: `assetGroup` names a whole LIBRARY (its ref
|
|
146
|
+
* IS the library), `asset` names one MEMBER of one (`fonts/yas`), and neither says which class it
|
|
147
|
+
* holds — that lived on the group's `assetKind`. So both resolve through the same lookup, differing
|
|
148
|
+
* only in where the library name sits in the ref.
|
|
149
|
+
*
|
|
150
|
+
* The member spelling is what a `font-family` token's value used, and it had no entry at all: it
|
|
151
|
+
* fell through to `REF_KIND[undefined]`, converted to `undefined`, and took the token's required
|
|
152
|
+
* `value` with it — so a system whose fonts are referenced by token, which is every system that
|
|
153
|
+
* declares fonts seriously, was REJECTED at load rather than loaded without the edge.
|
|
154
|
+
*/
|
|
155
|
+
function assetLibraryKind(value) {
|
|
156
|
+
if (!value.ref) return void 0;
|
|
157
|
+
if (value.__kind === "assetGroup") return assetKinds[value.ref] ?? REF_KIND.assetGroup;
|
|
158
|
+
if (value.__kind === "asset") {
|
|
159
|
+
const library = value.ref.slice(0, value.ref.indexOf("/"));
|
|
160
|
+
return assetKinds[library] ?? REF_KIND.assetGroup;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* A v1 colour-function marker as a config-v2 derived colour.
|
|
165
|
+
*
|
|
166
|
+
* Built through the AUTHORING HELPERS rather than as object literals, so there's one constructor for
|
|
167
|
+
* the stored shape and this can't drift from what a person writing `darken(...)` produces. The old
|
|
168
|
+
* model spelled shading and blending as separate markers with different amount units (`darken`'s 10
|
|
169
|
+
* is a percent, `mix`'s is the string `'20%'`, `alpha`'s is a 0–1 fraction); the helpers take each
|
|
170
|
+
* unit as authored and normalize, so that knowledge lives in one place too.
|
|
171
|
+
*
|
|
172
|
+
* The result is validated on the way into the store like any other body, so a conversion that
|
|
173
|
+
* produced the wrong shape fails here instead of surfacing as a colour that doesn't render.
|
|
174
|
+
*/
|
|
175
|
+
function convertColor(marker) {
|
|
176
|
+
const raw = marker;
|
|
177
|
+
const amount = typeof raw.amount === "string" ? Number.parseFloat(raw.amount) : raw.amount ?? 0;
|
|
178
|
+
if (marker.__kind === "mix") return mix(convertValue(raw.from), convertValue(raw.to), amount, raw.colorSpace ?? "oklch");
|
|
179
|
+
const color = convertValue(raw.color);
|
|
180
|
+
if (marker.__kind === "alpha") return alpha(color, amount);
|
|
181
|
+
return marker.__kind === "lighten" ? lighten(color, amount) : darken(color, amount);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The CSS a bare number meant for v1's grid props. Their `transform` lived in source, which the
|
|
185
|
+
* artifact never recorded, so the number is all that arrived.
|
|
186
|
+
*/
|
|
187
|
+
const GRID_NUMERIC_CSS = {
|
|
188
|
+
"grid-template-columns": (n) => `repeat(${n}, minmax(0, 1fr))`,
|
|
189
|
+
"grid-template-rows": (n) => `repeat(${n}, minmax(0, 1fr))`,
|
|
190
|
+
"grid-column": (n) => `span ${n} / span ${n}`,
|
|
191
|
+
"grid-row": (n) => `span ${n} / span ${n}`
|
|
192
|
+
};
|
|
193
|
+
/** A style prop's values, with each bare number aliased to the CSS v1 painted for it. */
|
|
194
|
+
function withGridAliases(properties, values) {
|
|
195
|
+
const css = properties.length === 1 ? GRID_NUMERIC_CSS[properties[0]] : void 0;
|
|
196
|
+
if (!css) return values;
|
|
197
|
+
return values.map((entry) => typeof entry === "number" ? {
|
|
198
|
+
alias: entry,
|
|
199
|
+
value: css(entry)
|
|
200
|
+
} : entry);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* v1 resolved a leaf two entries offered by the LAST entry; v2 by the first. A literal listed after a
|
|
204
|
+
* group holding a token of the same leaf (`[tokenGroup('letterSpacing'), 'normal']` with
|
|
205
|
+
* `letterSpacing/normal`) painted the literal, so it moves ahead of that group.
|
|
206
|
+
*/
|
|
207
|
+
function withV1Precedence(values, tokenRecord) {
|
|
208
|
+
const out = [];
|
|
209
|
+
for (const entry of values) {
|
|
210
|
+
const leaf = typeof entry === "string" || typeof entry === "number" ? String(entry) : void 0;
|
|
211
|
+
const shadowed = leaf === void 0 ? -1 : out.findIndex((prior) => isRef(prior) && `${pathOf(prior)}/${leaf}` in tokenRecord);
|
|
212
|
+
if (shadowed === -1) out.push(entry);
|
|
213
|
+
else out.splice(shadowed, 0, entry);
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
function convertValue(value) {
|
|
218
|
+
if (isMarker(value)) {
|
|
219
|
+
if (COLOR_OPS.has(value.__kind)) return convertColor(value);
|
|
220
|
+
const gradientKind = GRADIENTS[value.__kind];
|
|
221
|
+
if (gradientKind) {
|
|
222
|
+
const raw = value;
|
|
223
|
+
return {
|
|
224
|
+
gradient: gradientKind,
|
|
225
|
+
...raw.angle ? { angle: raw.angle } : {},
|
|
226
|
+
stops: (raw.stops ?? []).map((stop) => ({
|
|
227
|
+
color: convertValue(stop.color),
|
|
228
|
+
...stop.at ? { at: stop.at } : {}
|
|
229
|
+
})),
|
|
230
|
+
...raw.interpolation ? { space: raw.interpolation } : {},
|
|
231
|
+
...raw.repeating ? { repeating: raw.repeating } : {}
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const kind = value.ref ? assetLibraryKind(value) ?? REF_KIND[value.__kind] : REF_KIND[value.__kind];
|
|
235
|
+
if (kind && value.ref) return { __ref: `${kind}:${value.ref}` };
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (Array.isArray(value)) return value.map(convertValue);
|
|
239
|
+
if (value && typeof value === "object") {
|
|
240
|
+
const out = {};
|
|
241
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
242
|
+
const converted = convertValue(nested);
|
|
243
|
+
if (converted !== void 0) out[key] = converted;
|
|
244
|
+
}
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
return value;
|
|
248
|
+
}
|
|
249
|
+
/** An old `fallback` (a comma list OR an array) → the list config-v2 stores. */
|
|
250
|
+
function fallbackList(fallback) {
|
|
251
|
+
if (Array.isArray(fallback)) return fallback.filter((f) => f.length > 0);
|
|
252
|
+
if (typeof fallback === "string") return fallback.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Old `declarations[]` → config-v2's keyed `files`. Every entry converts, not just the first: a
|
|
257
|
+
* family's weights live in its files, and keeping one dropped the rest.
|
|
258
|
+
*
|
|
259
|
+
* Field naming differs by source (an upload writes `url`, the v3 declaration map writes
|
|
260
|
+
* `src`/`fontWeight`/`fontStyle`), so both spellings are read. The key prefers the PostScript name —
|
|
261
|
+
* it identifies the exact file — then a `<weight>-<style>` slug, then the index, so it stays
|
|
262
|
+
* meaningful and stable rather than positional.
|
|
263
|
+
*/
|
|
264
|
+
function convertFontFiles(declarations) {
|
|
265
|
+
const files = {};
|
|
266
|
+
declarations.forEach((declaration, index) => {
|
|
267
|
+
const src = declaration.src ?? declaration.url;
|
|
268
|
+
if (!src) return;
|
|
269
|
+
const weight = declaration.weight ?? declaration.fontWeight;
|
|
270
|
+
const style = declaration.style ?? declaration.fontStyle;
|
|
271
|
+
let key = declaration.postscriptName;
|
|
272
|
+
if (!key && weight !== void 0) key = style ? `${weight}-${style}` : String(weight);
|
|
273
|
+
files[key ?? String(index)] = {
|
|
274
|
+
src,
|
|
275
|
+
...declaration.format !== void 0 ? { format: declaration.format } : {},
|
|
276
|
+
...weight !== void 0 ? { weight } : {},
|
|
277
|
+
...style !== void 0 ? { style } : {},
|
|
278
|
+
...declaration.postscriptName !== void 0 ? { postscriptName: declaration.postscriptName } : {}
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
return files;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Old modes and modifiers as one `modifier` record.
|
|
285
|
+
*
|
|
286
|
+
* A mode option becomes an item under a group typed `mode`, and the old modifier that rode it is
|
|
287
|
+
* absorbed into that item: the option lands at the MODIFIER's name, since that is what every bag key
|
|
288
|
+
* and override in the old config spells. A fragment modifier lands under a `state` group, except a
|
|
289
|
+
* pseudo-element or a child combinator, which addresses a part of the element and stays plain.
|
|
290
|
+
*/
|
|
291
|
+
function buildModifiers(studio) {
|
|
292
|
+
const record = {};
|
|
293
|
+
const optionNames = /* @__PURE__ */ new Map();
|
|
294
|
+
for (const [key, def] of Object.entries(studio.modifiers ?? {})) {
|
|
295
|
+
const selector = def.selector;
|
|
296
|
+
if (isMarker(selector) && selector.__kind === "mode" && selector.ref) optionNames.set(selector.ref, modifierName(key));
|
|
297
|
+
}
|
|
298
|
+
for (const [group, options] of Object.entries(studio.modes ?? {})) {
|
|
299
|
+
record[group] = Modifier.group({
|
|
300
|
+
type: "mode",
|
|
301
|
+
label: group
|
|
302
|
+
});
|
|
303
|
+
for (const [option, def] of Object.entries(options)) {
|
|
304
|
+
const leaf = optionNames.get(`${group}/${option}`) ?? option;
|
|
305
|
+
record[`${group}/${leaf}`] = def.css ? Modifier.item({
|
|
306
|
+
type: "selector",
|
|
307
|
+
value: def.css
|
|
308
|
+
}) : Modifier.item({
|
|
309
|
+
type: "media-query",
|
|
310
|
+
value: def.media ?? ""
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
let stateGroup = false;
|
|
315
|
+
for (const [key, def] of Object.entries(studio.modifiers ?? {})) {
|
|
316
|
+
const selector = def.selector;
|
|
317
|
+
if (isMarker(selector) && selector.__kind === "mode") continue;
|
|
318
|
+
const value = typeof selector === "string" ? selector : String(selector);
|
|
319
|
+
const name = modifierName(key);
|
|
320
|
+
if (def.media) report("modifier-media-dropped", name, `v1 wrapped \`${value}\` in \`@media ${def.media}\`; a v2 modifier is a selector or a query, not both`);
|
|
321
|
+
if (addressesPart(value)) {
|
|
322
|
+
record[name] = Modifier.item({
|
|
323
|
+
type: "selector",
|
|
324
|
+
value
|
|
325
|
+
});
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
if (!stateGroup) {
|
|
329
|
+
record.state = Modifier.group({
|
|
330
|
+
type: "state",
|
|
331
|
+
label: "State"
|
|
332
|
+
});
|
|
333
|
+
stateGroup = true;
|
|
334
|
+
}
|
|
335
|
+
record[`state/${name}`] = Modifier.item({
|
|
336
|
+
type: "selector",
|
|
337
|
+
value
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return record;
|
|
341
|
+
}
|
|
342
|
+
/** A pseudo-element or a child/sibling combinator styles a part of the element rather than the
|
|
343
|
+
* element under a condition, so it is a plain modifier rather than a state. */
|
|
344
|
+
const addressesPart = (value) => /^\s*(::|>|~|\+)/.test(value);
|
|
345
|
+
/** A modifier's config-v2 name. Old configs key modifiers by their style-bag form (`_dark`), but
|
|
346
|
+
* the underscore is bag SYNTAX, not identity — so the entity is `dark`, which is what a ref points
|
|
347
|
+
* at and what codegen already looks up after stripping a bag key's `_`. */
|
|
348
|
+
const modifierName = (key) => key.replace(/^_/, "");
|
|
349
|
+
/**
|
|
350
|
+
* A token body: base value + `_<modifier>` overrides folded into `overrides`, each naming the
|
|
351
|
+
* modifier it applies under. Whether that modifier activates by wrapping (a mode) or by composing
|
|
352
|
+
* a selector is the MODIFIER's business, so this needs no mode lookup of its own.
|
|
353
|
+
*
|
|
354
|
+
* An old override may NEST — `_ocean: { value: '#0e7490', _dark: '#22d3ee' }` is one value for the
|
|
355
|
+
* ocean theme and another for ocean-and-dark-together. The nesting was v1 shorthand for a compound
|
|
356
|
+
* condition, which config-v2 spells as an override naming both modifiers in `on`, keyed by the pair.
|
|
357
|
+
*/
|
|
358
|
+
function buildTokenBody(raw) {
|
|
359
|
+
const body = {};
|
|
360
|
+
const overrides = {};
|
|
361
|
+
const put = (names, value) => {
|
|
362
|
+
const on = names.length === 1 ? Modifier.ref(names[0]) : names.map((name) => Modifier.ref(name));
|
|
363
|
+
overrides[overrideKey(on)] = {
|
|
364
|
+
on,
|
|
365
|
+
value: convertValue(value)
|
|
366
|
+
};
|
|
367
|
+
};
|
|
368
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
369
|
+
if (key === "value" || key === "type") {
|
|
370
|
+
body[key] = convertValue(value);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (!key.startsWith("_")) continue;
|
|
374
|
+
const name = modifierName(key);
|
|
375
|
+
if (!isOverrideBlock(value)) {
|
|
376
|
+
put([name], value);
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if ("value" in value) put([name], value.value);
|
|
380
|
+
for (const [innerKey, innerValue] of Object.entries(value)) {
|
|
381
|
+
if (!innerKey.startsWith("_")) continue;
|
|
382
|
+
put([name, modifierName(innerKey)], innerValue);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (Object.keys(overrides).length > 0) body.overrides = overrides;
|
|
386
|
+
return body;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* An override written as a BLOCK — `{ value }`, `{ _dark }`, or both — rather than as the value
|
|
390
|
+
* itself. A token value is a literal, a ref or a computed-colour marker, so an object carrying
|
|
391
|
+
* `value` or a `_<modifier>` key is the block form and never a value that merely looks like one.
|
|
392
|
+
*/
|
|
393
|
+
function isOverrideBlock(value) {
|
|
394
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
395
|
+
if (isMarker(value) || isConfigRef(value)) return false;
|
|
396
|
+
return Object.keys(value).some((key) => key === "value" || key.startsWith("_"));
|
|
397
|
+
}
|
|
398
|
+
/** A v1 layer written as a component body with no marker: `{ layers, props?, defaultProps? }`. */
|
|
399
|
+
function isInlineComponent(layer) {
|
|
400
|
+
return typeof layer === "object" && layer !== null && !isMarker(layer) && typeof layer.layers === "object";
|
|
401
|
+
}
|
|
402
|
+
/** The v1 component body a layer renders, or `undefined` for a tag. */
|
|
403
|
+
function componentOfLayer(layer, components) {
|
|
404
|
+
if (isInlineComponent(layer)) return layer;
|
|
405
|
+
if (isMarker(layer) && layer.__kind === "component" && layer.ref) return components[layer.ref];
|
|
406
|
+
}
|
|
407
|
+
/** The v1 declaration of `prop` on `body`, following `extendsFrom` since the v1 build flattened it. */
|
|
408
|
+
function v1PropOf(body, prop, components, seen = /* @__PURE__ */ new Set()) {
|
|
409
|
+
const own = body.props?.[prop];
|
|
410
|
+
if (own !== void 0 || seen.has(body)) return own;
|
|
411
|
+
const parent = typeof body.extendsFrom === "string" ? components[body.extendsFrom] : void 0;
|
|
412
|
+
if (!parent) return void 0;
|
|
413
|
+
return v1PropOf(parent, prop, components, seen.add(body));
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Whether a v1 route lands on content, decided from the artifact: the config's own `routesContent`
|
|
417
|
+
* asks a registered config, and every component here registers in one call. A tag carries content
|
|
418
|
+
* only through `children`; a component carries it through a prop it declares as a slot.
|
|
419
|
+
*/
|
|
420
|
+
function routeCarriesContent(ctx, layer, into) {
|
|
421
|
+
const target = componentOfLayer(ctx.layers[layer], ctx.components);
|
|
422
|
+
if (!target) return into === "children";
|
|
423
|
+
const routed = v1PropOf(target, into, ctx.components);
|
|
424
|
+
if (routed === void 0) return into === "children";
|
|
425
|
+
return isMarker(routed) && routed.__kind === "slot";
|
|
426
|
+
}
|
|
427
|
+
function convertProp(name, prop, ctx) {
|
|
428
|
+
switch (prop.__kind) {
|
|
429
|
+
case "slot": {
|
|
430
|
+
const text = prop.__valueType === "string";
|
|
431
|
+
const accepts = prop.__accepts?.length ? prop.__accepts.map((entry) => typeof entry === "string" ? Component.ref(entry) : convertValue(entry)) : void 0;
|
|
432
|
+
if (prop.layer) {
|
|
433
|
+
const layer = String(prop.layer);
|
|
434
|
+
const into = typeof prop.layerProp === "string" ? prop.layerProp : "children";
|
|
435
|
+
const out = {
|
|
436
|
+
type: "forward",
|
|
437
|
+
target: { __ref: `layer:${layer}` },
|
|
438
|
+
into
|
|
439
|
+
};
|
|
440
|
+
if (routeCarriesContent(ctx, layer, into)) {
|
|
441
|
+
if (text) out.text = true;
|
|
442
|
+
if (accepts) out.accepts = accepts;
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
decl: out,
|
|
446
|
+
rules: []
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
const out = { type: "slot" };
|
|
450
|
+
if (text) out.text = true;
|
|
451
|
+
if (accepts) out.accepts = accepts;
|
|
452
|
+
return {
|
|
453
|
+
decl: out,
|
|
454
|
+
rules: []
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
case "variant": {
|
|
458
|
+
const values = prop.values;
|
|
459
|
+
if (Array.isArray(values)) return {
|
|
460
|
+
decl: {
|
|
461
|
+
type: "variant",
|
|
462
|
+
value: values.map(String)
|
|
463
|
+
},
|
|
464
|
+
rules: []
|
|
465
|
+
};
|
|
466
|
+
const map = values ?? {};
|
|
467
|
+
return {
|
|
468
|
+
decl: {
|
|
469
|
+
type: "variant",
|
|
470
|
+
value: Object.keys(map)
|
|
471
|
+
},
|
|
472
|
+
rules: Object.entries(map).map(([value, bags]) => ({
|
|
473
|
+
when: { [name]: value },
|
|
474
|
+
layers: bagsToLayers(bags)
|
|
475
|
+
}))
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
case "styleProp":
|
|
479
|
+
case "styleProperty": return {
|
|
480
|
+
decl: {
|
|
481
|
+
type: "styleProperty",
|
|
482
|
+
value: { __ref: `style-property:${prop.ref}` }
|
|
483
|
+
},
|
|
484
|
+
rules: []
|
|
485
|
+
};
|
|
486
|
+
case "composite": return {
|
|
487
|
+
decl: {
|
|
488
|
+
type: "composite",
|
|
489
|
+
value: { __ref: `composite:${prop.ref}` }
|
|
490
|
+
},
|
|
491
|
+
rules: []
|
|
492
|
+
};
|
|
493
|
+
case "motion": return {
|
|
494
|
+
decl: { type: "motion" },
|
|
495
|
+
rules: []
|
|
496
|
+
};
|
|
497
|
+
case "boolean": {
|
|
498
|
+
const branches = prop.values ?? {};
|
|
499
|
+
return {
|
|
500
|
+
decl: { type: "boolean" },
|
|
501
|
+
rules: Object.entries(branches).map(([branch, bags]) => ({
|
|
502
|
+
when: { [name]: branch === "true" },
|
|
503
|
+
layers: bagsToLayers(bags)
|
|
504
|
+
}))
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
case "string":
|
|
508
|
+
case "number": return {
|
|
509
|
+
decl: { type: prop.__kind },
|
|
510
|
+
rules: []
|
|
511
|
+
};
|
|
512
|
+
default:
|
|
513
|
+
report("prop-unrecognized", `${ctx.name}/${name}`, `prop marker ${JSON.stringify(prop.__kind)} has no config-v2 type`);
|
|
514
|
+
return { rules: [] };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/** The per-layer bags an old variant value / boolean branch carried, as a rule's `layers`. */
|
|
518
|
+
function bagsToLayers(raw) {
|
|
519
|
+
const converted = convertValue(raw);
|
|
520
|
+
if (!converted || typeof converted !== "object") return {};
|
|
521
|
+
return converted;
|
|
522
|
+
}
|
|
523
|
+
/** An old layer value (`{ __kind:'tag'|'component', ref }`) → the element it names. A layer IS
|
|
524
|
+
* its element now, so there's nothing to wrap. `undefined` for an unrecognized marker. */
|
|
525
|
+
function convertLayer(layer) {
|
|
526
|
+
if (typeof layer === "string") return layer;
|
|
527
|
+
if (!isMarker(layer)) return void 0;
|
|
528
|
+
if (layer.__kind === "tag") return layer.ref;
|
|
529
|
+
if (layer.__kind === "component") return { __ref: `component:${layer.ref}` };
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* A v1 component could declare layers and no `root`; v2 requires one, since it is where unmodelled
|
|
533
|
+
* props land. The first declared layer takes the name, in every place the body spells it — layers,
|
|
534
|
+
* bags, routes — and the rename is a loss because the render still says `layers.<old>`.
|
|
535
|
+
*/
|
|
536
|
+
function withRootLayer(name, raw) {
|
|
537
|
+
const layers = raw.layers;
|
|
538
|
+
if (!layers || "root" in layers) return raw;
|
|
539
|
+
const first = Object.keys(layers)[0];
|
|
540
|
+
if (first === void 0) return raw;
|
|
541
|
+
report("root-layer-renamed", `${name}/${first}`, `declares no root layer; \`${first}\` is renamed to \`root\`, and the render's \`layers.${first}\` has to follow`);
|
|
542
|
+
const renameKeys = (bags) => {
|
|
543
|
+
if (!bags || typeof bags !== "object" || Array.isArray(bags)) return bags;
|
|
544
|
+
return Object.fromEntries(Object.entries(bags).map(([key, value]) => [key === first ? "root" : key, value]));
|
|
545
|
+
};
|
|
546
|
+
const props = Object.fromEntries(Object.entries(raw.props ?? {}).map(([prop, decl]) => {
|
|
547
|
+
if (!decl) return [prop, decl];
|
|
548
|
+
const next = { ...decl };
|
|
549
|
+
if (next.layer === first) next.layer = "root";
|
|
550
|
+
if (next.values && typeof next.values === "object" && !Array.isArray(next.values)) next.values = Object.fromEntries(Object.entries(next.values).map(([value, bags]) => [value, renameKeys(bags)]));
|
|
551
|
+
return [prop, next];
|
|
552
|
+
}));
|
|
553
|
+
return {
|
|
554
|
+
...raw,
|
|
555
|
+
layers: renameKeys(layers),
|
|
556
|
+
...raw.props ? { props } : {},
|
|
557
|
+
...raw.base ? { base: renameKeys(raw.base) } : {},
|
|
558
|
+
...Array.isArray(raw.compoundProps) ? { compoundProps: raw.compoundProps.map((entry) => ({
|
|
559
|
+
...entry,
|
|
560
|
+
layers: renameKeys(entry.layers)
|
|
561
|
+
})) } : {}
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* An old component → the three steps: `layers` (name → element), `props` (the public API), and
|
|
566
|
+
* `styles` (condition → what to apply). Old config spread its styling across four shapes that
|
|
567
|
+
* differ only in condition — a top-level `base`, a variant value's bags, a boolean branch's bags,
|
|
568
|
+
* and `compoundProps` — so the conversion funnels all four into one rule collection keyed by
|
|
569
|
+
* `canonicalWhen`. Two sources landing on the same condition merge per layer rather than
|
|
570
|
+
* clobbering, which is how a `base` and a `compoundProps` entry on the same layer used to stack.
|
|
571
|
+
*/
|
|
572
|
+
function convertComponent({ name, raw: given, parent, components }) {
|
|
573
|
+
const out = {};
|
|
574
|
+
const parts = {};
|
|
575
|
+
const parentName = parent && typeof given.extendsFrom === "string" ? given.extendsFrom : void 0;
|
|
576
|
+
const raw = parentName ? given : withRootLayer(name, given);
|
|
577
|
+
const rawLayers = raw.layers ?? {};
|
|
578
|
+
const layers = {};
|
|
579
|
+
for (const [layerName, layer] of Object.entries(rawLayers)) {
|
|
580
|
+
if (isInlineComponent(layer)) {
|
|
581
|
+
const path = `${name}/${name.slice(name.lastIndexOf("/") + 1)}${layerName.charAt(0).toUpperCase()}${layerName.slice(1)}`;
|
|
582
|
+
const hoisted = convertComponent({
|
|
583
|
+
name: path,
|
|
584
|
+
raw: layer,
|
|
585
|
+
components
|
|
586
|
+
});
|
|
587
|
+
parts[path] = hoisted.body;
|
|
588
|
+
Object.assign(parts, hoisted.parts);
|
|
589
|
+
layers[layerName] = Component.ref(path);
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const element = convertLayer(layer);
|
|
593
|
+
if (element !== void 0) {
|
|
594
|
+
layers[layerName] = element;
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
report("layer-unrecognized", `${name}/${layerName}`, `layer value ${JSON.stringify(layer)} names no tag or component`);
|
|
598
|
+
}
|
|
599
|
+
if (parentName) layers.root = Component.ref(parentName);
|
|
600
|
+
if (Object.keys(layers).length > 0) out.layers = layers;
|
|
601
|
+
const ctx = {
|
|
602
|
+
name,
|
|
603
|
+
layers: rawLayers,
|
|
604
|
+
components
|
|
605
|
+
};
|
|
606
|
+
const drafts = [];
|
|
607
|
+
const rawProps = raw.props;
|
|
608
|
+
const parentProps = parent?.props ?? {};
|
|
609
|
+
const props = {};
|
|
610
|
+
for (const [propName, prop] of Object.entries(rawProps ?? {})) {
|
|
611
|
+
if (!prop) {
|
|
612
|
+
report("prop-hole-dropped", `${name}/${propName}`, "declared with no marker; nothing to carry");
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (parentName && propName in parentProps && stableStringify(prop) === stableStringify(parentProps[propName])) continue;
|
|
616
|
+
const { decl, rules } = convertProp(propName, prop, ctx);
|
|
617
|
+
if (decl) props[propName] = decl;
|
|
618
|
+
drafts.push(...rules);
|
|
619
|
+
}
|
|
620
|
+
if (parentName) {
|
|
621
|
+
const parentCtx = {
|
|
622
|
+
name: parentName,
|
|
623
|
+
layers: parent?.layers ?? {},
|
|
624
|
+
components
|
|
625
|
+
};
|
|
626
|
+
for (const [propName, prop] of Object.entries(parentProps)) {
|
|
627
|
+
if (!prop || rawProps?.[propName]) continue;
|
|
628
|
+
if (convertProp(propName, prop, parentCtx).decl) props[propName] = null;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
if (rawProps || parentName) out.props = props;
|
|
632
|
+
const layerNames = Object.keys(layers);
|
|
633
|
+
const onlyElement = layers[layerNames[0]];
|
|
634
|
+
if (parentName) out.forwards = { root: "*" };
|
|
635
|
+
else if (layerNames.length === 1 && isConfigRef(onlyElement)) out.forwards = { [layerNames[0]]: "*" };
|
|
636
|
+
if (raw.base) drafts.unshift({
|
|
637
|
+
when: null,
|
|
638
|
+
layers: bagsToLayers(raw.base)
|
|
639
|
+
});
|
|
640
|
+
const compound = raw.compoundProps ?? [];
|
|
641
|
+
for (const entry of compound) drafts.push({
|
|
642
|
+
when: entry.when ?? null,
|
|
643
|
+
layers: bagsToLayers(entry.layers)
|
|
644
|
+
});
|
|
645
|
+
const styles = {};
|
|
646
|
+
for (const draft of drafts) {
|
|
647
|
+
const key = canonicalWhen(draft.when);
|
|
648
|
+
const targeted = {};
|
|
649
|
+
for (const [layerName, bag] of Object.entries(draft.layers)) {
|
|
650
|
+
if (layerName in layers) {
|
|
651
|
+
targeted[layerName] = bag;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
report("style-layer-undeclared", `${name}/${key}/${layerName}`, `a style bag targets layer "${layerName}", which the component does not declare`);
|
|
655
|
+
}
|
|
656
|
+
if (Object.keys(targeted).length === 0) continue;
|
|
657
|
+
const existing = styles[key];
|
|
658
|
+
if (!existing) {
|
|
659
|
+
styles[key] = {
|
|
660
|
+
when: draft.when,
|
|
661
|
+
layers: targeted
|
|
662
|
+
};
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
for (const [layerName, bag] of Object.entries(targeted)) existing.layers[layerName] = {
|
|
666
|
+
...existing.layers[layerName],
|
|
667
|
+
...bag
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
if (Object.keys(styles).length > 0) out.styles = styles;
|
|
671
|
+
if (raw.defaultProps) out.defaultProps = convertValue(raw.defaultProps);
|
|
672
|
+
const meta = raw.metadata;
|
|
673
|
+
if (meta?.label) out.label = meta.label;
|
|
674
|
+
if (meta?.description) out.description = meta.description;
|
|
675
|
+
return {
|
|
676
|
+
body: out,
|
|
677
|
+
parts
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* A v1 render took React's own `children` without declaring it, and the artifact still shows where
|
|
682
|
+
* that happened: another component routes content into the layer, or the component's own preview
|
|
683
|
+
* passes `children`. v2 refuses both against an undeclared prop, so the slot is declared.
|
|
684
|
+
*/
|
|
685
|
+
function inferChildrenSlots(record, raw) {
|
|
686
|
+
const bodies = record;
|
|
687
|
+
const evidence = /* @__PURE__ */ new Map();
|
|
688
|
+
const cite = (target, why) => {
|
|
689
|
+
if (!(target in bodies)) return;
|
|
690
|
+
if (bodies[target]?.props && "children" in bodies[target].props) return;
|
|
691
|
+
evidence.set(target, [...evidence.get(target) ?? [], why]);
|
|
692
|
+
};
|
|
693
|
+
for (const [name, body] of Object.entries(bodies)) {
|
|
694
|
+
for (const [prop, decl] of Object.entries(body.props ?? {})) {
|
|
695
|
+
if (decl?.type !== "forward" || !isConfigRef(decl.target)) continue;
|
|
696
|
+
if (decl.into !== "children") continue;
|
|
697
|
+
const layer = decl.target.__ref.slice(6);
|
|
698
|
+
const element = body.layers?.[layer];
|
|
699
|
+
if (!isConfigRef(element)) continue;
|
|
700
|
+
cite(element.__ref.slice(10), `${name}.${prop} routes into it`);
|
|
701
|
+
}
|
|
702
|
+
const preview = raw[name]?.preview;
|
|
703
|
+
if (preview?.defaultProps && "children" in preview.defaultProps) cite(name, "its preview passes children");
|
|
704
|
+
}
|
|
705
|
+
for (const [target, why] of evidence) {
|
|
706
|
+
const body = bodies[target];
|
|
707
|
+
body.props = {
|
|
708
|
+
...body.props,
|
|
709
|
+
children: { type: "slot" }
|
|
710
|
+
};
|
|
711
|
+
const shown = why.slice(0, 3).join("; ");
|
|
712
|
+
const more = why.length > 3 ? `; and ${why.length - 3} more` : "";
|
|
713
|
+
report("children-slot-inferred", `${target}/children`, `declared no children prop, but ${shown}${more}`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* The components a v1 artifact marks as parts of another, as the renames that move each to its
|
|
718
|
+
* owner path.
|
|
719
|
+
*
|
|
720
|
+
* v1 recorded the edge twice, `subcomponentOf: 'Card'` on the part and `subcomponents: ['Header']`
|
|
721
|
+
* on the owner, and either spelling alone counts. config-v2 has no field for it: `Card/Header` is a
|
|
722
|
+
* part of `Card` by being named that way. A part of a part lands under the owner's own owner path.
|
|
723
|
+
*
|
|
724
|
+
* The list is what a canvas conversion hands to `rewriteSpecRefs`, so a node whose type still names
|
|
725
|
+
* the bare component follows it to the owner path. `convert` runs the same list as component renames.
|
|
726
|
+
*/
|
|
727
|
+
function v1ComponentRenames(studio) {
|
|
728
|
+
const components = studio.components ?? {};
|
|
729
|
+
const ownerOf = {};
|
|
730
|
+
for (const [name, raw] of Object.entries(components)) {
|
|
731
|
+
const declared = raw.subcomponentOf;
|
|
732
|
+
if (typeof declared === "string" && declared in components) ownerOf[name] = declared;
|
|
733
|
+
const parts = Array.isArray(raw.subcomponents) ? raw.subcomponents : [];
|
|
734
|
+
for (const part of parts) {
|
|
735
|
+
if (typeof part !== "string") continue;
|
|
736
|
+
const bare = part.slice(part.lastIndexOf("/") + 1);
|
|
737
|
+
if (bare !== name && bare in components) ownerOf[bare] ??= name;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
const ownedPath = (name, seen) => {
|
|
741
|
+
const owner = ownerOf[name];
|
|
742
|
+
if (!owner || seen.has(name)) return name;
|
|
743
|
+
return `${ownedPath(owner, new Set(seen).add(name))}/${name}`;
|
|
744
|
+
};
|
|
745
|
+
return Object.keys(ownerOf).map((from) => ({
|
|
746
|
+
kind: "component",
|
|
747
|
+
from,
|
|
748
|
+
to: ownedPath(from, /* @__PURE__ */ new Set())
|
|
749
|
+
}));
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Read a v1 build artifact as a config-v2 `System`.
|
|
753
|
+
*
|
|
754
|
+
* One shape in, and only one: the flat pre-cutover bag. A config-v2 artifact does not come through
|
|
755
|
+
* here — `Config.fromJSON` reads it directly, and it refuses this shape outright rather than
|
|
756
|
+
* hydrating an empty config from fields it cannot see.
|
|
757
|
+
*/
|
|
758
|
+
function loadV1Artifact(studio, options = {}) {
|
|
759
|
+
diagnose = options.onDiagnostic ?? (() => {});
|
|
760
|
+
try {
|
|
761
|
+
return convert(studio, options);
|
|
762
|
+
} finally {
|
|
763
|
+
diagnose = () => {};
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
/** The pre-cutover conversion. */
|
|
767
|
+
function convert(studio, options) {
|
|
768
|
+
const settings = {
|
|
769
|
+
...studio.buildOptions?.registryDir ? { build: { registryDir: studio.buildOptions.registryDir } } : {},
|
|
770
|
+
css: {
|
|
771
|
+
prefix: studio.prefix ?? "uds",
|
|
772
|
+
...studio.buildOptions?.cssScope ? { scope: studio.buildOptions.cssScope } : {},
|
|
773
|
+
...studio.preflight !== void 0 ? { preflight: studio.preflight } : {}
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
assetKinds = Object.fromEntries(Object.entries(studio.assetGroups ?? {}).map(([name, meta]) => [name, meta.assetKind === "font" ? "font" : "icon"]));
|
|
777
|
+
const modifierRecord = buildModifiers(studio);
|
|
778
|
+
const tokenRecord = {};
|
|
779
|
+
for (const [group, meta] of Object.entries(studio.tokenGroups ?? {})) {
|
|
780
|
+
tokenRecord[group] = Token.group({
|
|
781
|
+
label: meta.label,
|
|
782
|
+
description: meta.description
|
|
783
|
+
});
|
|
784
|
+
for (const [tokenPath, raw] of Object.entries(meta.tokens ?? {})) tokenRecord[`${group}/${tokenPath}`] = Token.item(buildTokenBody(raw));
|
|
785
|
+
}
|
|
786
|
+
for (const [path, raw] of Object.entries(studio.tokens ?? {})) tokenRecord[path] = Token.item(buildTokenBody(raw));
|
|
787
|
+
const stylePropRecord = {};
|
|
788
|
+
for (const [name, sp] of Object.entries(studio.styleProps ?? {})) {
|
|
789
|
+
const properties = Array.isArray(sp.cssProperty) ? [...sp.cssProperty] : [sp.cssProperty];
|
|
790
|
+
stylePropRecord[name] = StyleProperty.item({
|
|
791
|
+
properties,
|
|
792
|
+
values: sp.values ? withV1Precedence(withGridAliases(properties, convertValue(sp.values).map((entry) => typeof entry === "boolean" ? {
|
|
793
|
+
alias: entry,
|
|
794
|
+
value: String(entry)
|
|
795
|
+
} : entry)), tokenRecord) : void 0,
|
|
796
|
+
arbitrary: Boolean(sp.arbitrary),
|
|
797
|
+
...sp.classPrefix !== void 0 ? { classPrefix: sp.classPrefix } : {},
|
|
798
|
+
...sp.responsive !== void 0 ? { responsive: sp.responsive } : {},
|
|
799
|
+
label: sp.metadata?.label
|
|
800
|
+
});
|
|
801
|
+
const sibling = sp.opacityPair?.as ?? sp.opacityPair?.ref;
|
|
802
|
+
if (sibling) stylePropRecord[sibling] = StyleProperty.item({
|
|
803
|
+
properties: [],
|
|
804
|
+
values: sp.opacityPair?.values ? convertValue([sp.opacityPair.values]) : void 0,
|
|
805
|
+
arbitrary: false,
|
|
806
|
+
opacityOf: StyleProperty.ref(name)
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
const compositeRecord = {};
|
|
810
|
+
for (const [name, cs] of Object.entries(studio.compositeStyles ?? {})) {
|
|
811
|
+
compositeRecord[name] = Composite.group({
|
|
812
|
+
label: cs.label,
|
|
813
|
+
description: cs.description
|
|
814
|
+
});
|
|
815
|
+
for (const [level, styles] of Object.entries(cs.styles)) compositeRecord[`${name}/${level}`] = Composite.item({ styles: convertValue(styles) });
|
|
816
|
+
}
|
|
817
|
+
const motionRecord = {};
|
|
818
|
+
for (const [name, m] of Object.entries(studio.motion ?? {})) motionRecord[name] = Motion.item(convertValue(m));
|
|
819
|
+
const componentRecord = {};
|
|
820
|
+
for (const [group, meta] of Object.entries(studio.componentGroups ?? {})) componentRecord[group] = Component.group({
|
|
821
|
+
icon: meta.icon,
|
|
822
|
+
label: meta.label,
|
|
823
|
+
description: meta.description
|
|
824
|
+
});
|
|
825
|
+
const components = studio.components ?? {};
|
|
826
|
+
for (const [name, raw] of Object.entries(components)) {
|
|
827
|
+
const { body, parts } = convertComponent({
|
|
828
|
+
name,
|
|
829
|
+
raw,
|
|
830
|
+
parent: typeof raw.extendsFrom === "string" ? components[raw.extendsFrom] : void 0,
|
|
831
|
+
components
|
|
832
|
+
});
|
|
833
|
+
componentRecord[name] = body;
|
|
834
|
+
Object.assign(componentRecord, parts);
|
|
835
|
+
}
|
|
836
|
+
inferChildrenSlots(componentRecord, components);
|
|
837
|
+
const fontRecord = {};
|
|
838
|
+
const iconRecord = {};
|
|
839
|
+
for (const [group, meta] of Object.entries(studio.assetGroups ?? {})) {
|
|
840
|
+
if (meta.assetKind === "font") fontRecord[group] = Font.group({
|
|
841
|
+
version: meta.version,
|
|
842
|
+
label: meta.label
|
|
843
|
+
});
|
|
844
|
+
else iconRecord[group] = Icon.group({
|
|
845
|
+
version: meta.version,
|
|
846
|
+
sizes: meta.sizes,
|
|
847
|
+
variants: meta.variants,
|
|
848
|
+
...meta.component ? { component: Component.ref(meta.component) } : {},
|
|
849
|
+
label: meta.label
|
|
850
|
+
});
|
|
851
|
+
const members = meta.members;
|
|
852
|
+
if (Array.isArray(members)) for (const name of members) iconRecord[`${group}/${name}`] = Icon.item({});
|
|
853
|
+
else if (members) for (const [name, font] of Object.entries(members)) fontRecord[`${group}/${name}`] = Font.item({
|
|
854
|
+
fontFamily: font.fontFamily ?? name,
|
|
855
|
+
files: convertFontFiles(font.declarations ?? []),
|
|
856
|
+
...fallbackList(font.fallback).length > 0 ? { fallbackStack: fallbackList(font.fallback) } : {},
|
|
857
|
+
...font.deprecated !== void 0 ? { deprecated: font.deprecated } : {},
|
|
858
|
+
...font.isVariableFont !== void 0 ? { isVariableFont: font.isVariableFont } : {}
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
const canvasRoleRecord = {};
|
|
862
|
+
for (const [role, entries] of Object.entries(studio.canvasRoles ?? {})) canvasRoleRecord[role] = CanvasRole.item({ entries: entries.map((entry) => ({
|
|
863
|
+
component: Component.ref(entry.component),
|
|
864
|
+
...entry.default !== void 0 ? { default: entry.default } : {},
|
|
865
|
+
...entry.defaultProps ? { defaultProps: entry.defaultProps } : {}
|
|
866
|
+
})) });
|
|
867
|
+
const globalStyleRecord = {};
|
|
868
|
+
const globalStyleNames = /* @__PURE__ */ new Set();
|
|
869
|
+
for (const [selector, declarations] of Object.entries(studio.globalStyles ?? {})) {
|
|
870
|
+
const name = globalStyleName(selector, globalStyleNames);
|
|
871
|
+
globalStyleNames.add(name);
|
|
872
|
+
globalStyleRecord[name] = GlobalStyle.item({
|
|
873
|
+
selector,
|
|
874
|
+
declarations
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
const rawCss = Array.isArray(studio.rawCss) ? studio.rawCss : [studio.rawCss ?? ""];
|
|
878
|
+
for (const [index, css] of rawCss.entries()) {
|
|
879
|
+
const { rules, unparsed } = parseRawCss(css);
|
|
880
|
+
for (const statement of unparsed) report("raw-css-unparsed", `rawCss/${index}`, statement);
|
|
881
|
+
for (const { selector, declarations } of rules) {
|
|
882
|
+
const name = globalStyleName(selector, globalStyleNames);
|
|
883
|
+
globalStyleNames.add(name);
|
|
884
|
+
globalStyleRecord[name] = GlobalStyle.item({
|
|
885
|
+
selector,
|
|
886
|
+
declarations
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
declareRawCssKeys(componentRecord, stylePropRecord);
|
|
891
|
+
const records = {
|
|
892
|
+
globalStyles: globalStyleRecord,
|
|
893
|
+
canvasRoles: canvasRoleRecord,
|
|
894
|
+
modifiers: modifierRecord,
|
|
895
|
+
tokens: tokenRecord,
|
|
896
|
+
styleProps: stylePropRecord,
|
|
897
|
+
composites: compositeRecord,
|
|
898
|
+
motion: motionRecord,
|
|
899
|
+
components: componentRecord,
|
|
900
|
+
fonts: fontRecord,
|
|
901
|
+
icons: iconRecord
|
|
902
|
+
};
|
|
903
|
+
const renames = v1ComponentRenames(studio);
|
|
904
|
+
let config = assemble({
|
|
905
|
+
settings,
|
|
906
|
+
records,
|
|
907
|
+
renames
|
|
908
|
+
});
|
|
909
|
+
const dangling = danglingLocalRefs(config);
|
|
910
|
+
for (const { from, ref } of dangling) diagnose({
|
|
911
|
+
code: "ref-unresolved",
|
|
912
|
+
path: from,
|
|
913
|
+
detail: `refers to ${ref}, which the artifact does not declare${options.dropUnresolved ? "; dropped, since v1 emitted nothing for it" : ""}`,
|
|
914
|
+
loss: !options.dropUnresolved
|
|
915
|
+
});
|
|
916
|
+
if (options.dropUnresolved && dangling.length > 0) {
|
|
917
|
+
pruneDanglingRefs({
|
|
918
|
+
records,
|
|
919
|
+
dangling,
|
|
920
|
+
renames
|
|
921
|
+
});
|
|
922
|
+
config = assemble({
|
|
923
|
+
settings,
|
|
924
|
+
records,
|
|
925
|
+
renames
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
return config;
|
|
929
|
+
}
|
|
930
|
+
/** The registered config: every record, then parts moved under their owners, then default guidance. */
|
|
931
|
+
function assemble({ settings, records, renames }) {
|
|
932
|
+
let config = new System().register({ settings }).register(records);
|
|
933
|
+
if (renames.length > 0) config = config.run(renames.map(({ from, to }) => ({
|
|
934
|
+
kind: "component",
|
|
935
|
+
operation: "rename",
|
|
936
|
+
path: from,
|
|
937
|
+
to
|
|
938
|
+
})));
|
|
939
|
+
if (config.list("guidance").length === 0) config = config.register({ guidance: DEFAULT_GUIDANCE });
|
|
940
|
+
return config;
|
|
941
|
+
}
|
|
942
|
+
/** Kind → the record `register` takes it under. Read at call time: `System` is a cycle away. */
|
|
943
|
+
function recordOfKind() {
|
|
944
|
+
return Object.fromEntries(Object.entries(System.entities).map(([accessor, entity]) => [entity.kind, accessor]));
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Remove each dangling ref from the body that holds it, in place: a slot's `accepts` loses the
|
|
948
|
+
* entry, a bag loses the key. The holder is named after the part renames, so the record key is the
|
|
949
|
+
* name before them.
|
|
950
|
+
*/
|
|
951
|
+
function pruneDanglingRefs({ records, dangling, renames }) {
|
|
952
|
+
const before = new Map(renames.map(({ from, to }) => [to, from]));
|
|
953
|
+
const recordOf = recordOfKind();
|
|
954
|
+
for (const { from, ref } of dangling) {
|
|
955
|
+
const { kind, path } = splitRef(from);
|
|
956
|
+
const body = records[recordOf[kind] ?? ""]?.[before.get(path) ?? path];
|
|
957
|
+
if (body) removeRef(body, ref);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function removeRef(node, ref) {
|
|
961
|
+
if (!node || typeof node !== "object") return;
|
|
962
|
+
if (Array.isArray(node)) {
|
|
963
|
+
for (let i = node.length - 1; i >= 0; i -= 1) {
|
|
964
|
+
const entry = node[i];
|
|
965
|
+
if (isRef(entry) && entry.__ref === ref) node.splice(i, 1);
|
|
966
|
+
else removeRef(entry, ref);
|
|
967
|
+
}
|
|
968
|
+
return;
|
|
969
|
+
}
|
|
970
|
+
for (const [key, value] of Object.entries(node)) if (isRef(value) && value.__ref === ref) delete node[key];
|
|
971
|
+
else removeRef(value, ref);
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* A v1 bag could set a CSS property the system declared no style prop for, and v1 wrote the key as a
|
|
975
|
+
* declaration (`boxShadow: 'inset …'` → `box-shadow: inset …`). v2 writes only what a style property
|
|
976
|
+
* declares, so each such key becomes an arbitrary-valued style property of the same name. A key that
|
|
977
|
+
* is neither CSS nor a prop of the layer's element painted nothing in v1 and is dropped with a note.
|
|
978
|
+
*/
|
|
979
|
+
function declareRawCssKeys(componentRecord, stylePropRecord) {
|
|
980
|
+
const bodyOf = (path) => componentRecord[path];
|
|
981
|
+
/** The props a layer's element takes: the component's own, and its root element's beneath it. */
|
|
982
|
+
const elementProps = (element, seen = /* @__PURE__ */ new Set()) => {
|
|
983
|
+
const out = /* @__PURE__ */ new Set();
|
|
984
|
+
if (!isRef(element)) return out;
|
|
985
|
+
const path = pathOf(element);
|
|
986
|
+
if (seen.has(path)) return out;
|
|
987
|
+
seen.add(path);
|
|
988
|
+
const body = bodyOf(path);
|
|
989
|
+
for (const [prop, decl] of Object.entries(body?.props ?? {})) if (decl !== null) out.add(prop);
|
|
990
|
+
for (const prop of elementProps(body?.layers?.root, seen)) out.add(prop);
|
|
991
|
+
return out;
|
|
992
|
+
};
|
|
993
|
+
const audit = (bag, accepted, at) => {
|
|
994
|
+
for (const [key, value] of Object.entries(bag)) {
|
|
995
|
+
if (key.startsWith("_")) {
|
|
996
|
+
if (value && typeof value === "object" && !Array.isArray(value)) audit(value, accepted, `${at}/${key}`);
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
if (key.startsWith("--") || key in stylePropRecord || accepted.has(key)) continue;
|
|
1000
|
+
if (isCssProperty(key)) {
|
|
1001
|
+
stylePropRecord[key] = StyleProperty.item({
|
|
1002
|
+
properties: [kebabCase(key)],
|
|
1003
|
+
arbitrary: true
|
|
1004
|
+
});
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
report("style-key-unrecognized", `${at}/${key}`, `names no style property, CSS property, or prop of the layer's element; v1 painted nothing for it`);
|
|
1008
|
+
delete bag[key];
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
for (const [name, body] of Object.entries(componentRecord)) {
|
|
1012
|
+
const { layers, styles } = body;
|
|
1013
|
+
if (!styles) continue;
|
|
1014
|
+
for (const [when, rule] of Object.entries(styles)) for (const [layer, bag] of Object.entries(rule.layers ?? {})) audit(bag, elementProps(layers?.[layer]), `${name}/${when}/${layer}`);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
//#endregion
|
|
1018
|
+
export { V1_DIAGNOSTIC_CODES, loadV1Artifact, v1ComponentRenames };
|