@yahoo/uds-create-config 5.65.0 → 5.66.1
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/system.js +2 -0
- package/dist/entities/system/Component.d.ts +25 -0
- package/dist/entities/system/Component.js +80 -0
- package/dist/framework/Config.d.ts +1 -1
- package/dist/framework/Config.js +2 -2
- package/dist/framework/schema-version.d.ts +1 -1
- package/dist/framework/schema-version.js +5 -5
- package/dist/index.js +1 -1
- package/dist/migrations/20261030134149_component_map_props.d.ts +7 -0
- package/dist/migrations/20261030134149_component_map_props.js +8 -0
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +3 -3
- package/dist/spec/prop-mapping.d.ts +49 -1
- package/dist/spec/prop-mapping.js +87 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
package/dist/configs/system.js
CHANGED
|
@@ -33,6 +33,7 @@ import { conditionalDefaultsMigration } from "../migrations/20261023152030_condi
|
|
|
33
33
|
import { timingValueMigration } from "../migrations/20261026125912_timing_value.js";
|
|
34
34
|
import { variantValuesListWithBodiesMigration } from "../migrations/20261027184442_variant_values_list_with_bodies.js";
|
|
35
35
|
import { lintIgnoreMigration } from "../migrations/20261027205147_lint_ignore.js";
|
|
36
|
+
import { componentMapPropsMigration } from "../migrations/20261030134149_component_map_props.js";
|
|
36
37
|
import { CanvasRole } from "../entities/system/CanvasRole.js";
|
|
37
38
|
import { GlobalStyle } from "../entities/system/GlobalStyle.js";
|
|
38
39
|
import { Guidance } from "../entities/system/Guidance.js";
|
|
@@ -199,5 +200,6 @@ if (!registeredSchemaMigrations().has(20261023152030)) registerSchemaMigrations(
|
|
|
199
200
|
if (!registeredSchemaMigrations().has(20261026125912)) registerSchemaMigrations(timingValueMigration);
|
|
200
201
|
if (!registeredSchemaMigrations().has(20261027184442)) registerSchemaMigrations(variantValuesListWithBodiesMigration);
|
|
201
202
|
if (!registeredSchemaMigrations().has(20261027205147)) registerSchemaMigrations(lintIgnoreMigration);
|
|
203
|
+
if (!registeredSchemaMigrations().has(20261030134149)) registerSchemaMigrations(componentMapPropsMigration);
|
|
202
204
|
//#endregion
|
|
203
205
|
export { System };
|
|
@@ -869,6 +869,31 @@ declare const ComponentEntity: EntityClass<z.ZodObject<{
|
|
|
869
869
|
}, z.core.$strip>;
|
|
870
870
|
}, z.core.$strip>;
|
|
871
871
|
};
|
|
872
|
+
readonly 'map-props': {
|
|
873
|
+
readonly input: z.ZodObject<{
|
|
874
|
+
path: z.ZodType<string, string | undefined, z.core.$ZodTypeInternals<string, string | undefined>>;
|
|
875
|
+
data: z.ZodObject<{
|
|
876
|
+
rules: z.ZodArray<z.ZodObject<{
|
|
877
|
+
when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
878
|
+
set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
879
|
+
unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
880
|
+
}, z.core.$strip>>;
|
|
881
|
+
}, z.core.$strip>;
|
|
882
|
+
}, z.core.$strip>;
|
|
883
|
+
};
|
|
884
|
+
readonly 'map-props-plan': {
|
|
885
|
+
readonly input: z.ZodObject<{
|
|
886
|
+
path: z.ZodType<string, string | undefined, z.core.$ZodTypeInternals<string, string | undefined>>;
|
|
887
|
+
data: z.ZodObject<{
|
|
888
|
+
rules: z.ZodArray<z.ZodObject<{
|
|
889
|
+
when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
890
|
+
set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
891
|
+
unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
892
|
+
}, z.core.$strip>>;
|
|
893
|
+
instances: z.ZodDefault<z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
894
|
+
}, z.core.$strip>;
|
|
895
|
+
}, z.core.$strip>;
|
|
896
|
+
};
|
|
872
897
|
}, {
|
|
873
898
|
/**
|
|
874
899
|
* What each layer can forward, for the layers that render a component with props: every prop its
|
|
@@ -24,6 +24,7 @@ import { slotAnatomyPropOf, slotTargetsOf, withoutTruthyTerm } from "../../frame
|
|
|
24
24
|
import { listSlots, namedSlots, slotHostedBy, slotOf, slotsFor } from "../../spec/slot.js";
|
|
25
25
|
import { cellAt } from "../../spec/cell.js";
|
|
26
26
|
import { instanceAt } from "../../spec/instance.js";
|
|
27
|
+
import { mapComponentProps, propMappingErrors, propMappingRulesSchema } from "../../spec/prop-mapping.js";
|
|
27
28
|
import { forwardOptions, forwardsWrite } from "./component-forwards.js";
|
|
28
29
|
import { assertComponentSourceInput } from "./component-source-fields.js";
|
|
29
30
|
import { checkStyleSet, styleSetEdits } from "./component-style-set.js";
|
|
@@ -2924,6 +2925,55 @@ element: refSchema })
|
|
|
2924
2925
|
data: { props: exampleProps(config, path) }
|
|
2925
2926
|
}),
|
|
2926
2927
|
handler: (input, config) => validateComponentProps(config, String(input.path), input.data.props ?? {})
|
|
2928
|
+
},
|
|
2929
|
+
"map-props": {
|
|
2930
|
+
input: z.object({
|
|
2931
|
+
path: pathInput("component"),
|
|
2932
|
+
data: z.object({ rules: propMappingRulesSchema })
|
|
2933
|
+
}),
|
|
2934
|
+
readOnly: false,
|
|
2935
|
+
approval: "reference-change",
|
|
2936
|
+
label: "Map props",
|
|
2937
|
+
description: "Move a component's instances onto its props as they are now, after its API changed: `variant: 'primary'` becomes `color: 'brand', prominence: 'solid'`. It changes nothing in the config. Where it runs decides what it moves: in Create, the instances in the branch's design files; in a checkout (`uds config edit`, the local UDS MCP), the call sites in the project's source. Refuses a rule that writes a prop or value the component doesn't take. Run it with `plan: true` first: the plan says what it would move, and writes nothing.",
|
|
2938
|
+
scope: "item",
|
|
2939
|
+
title: (entity) => `Map ${entity} props`,
|
|
2940
|
+
example: ({ config, path }) => {
|
|
2941
|
+
const { data, ...rest } = exampleMapping(config, path);
|
|
2942
|
+
return {
|
|
2943
|
+
...rest,
|
|
2944
|
+
data: { rules: data.rules }
|
|
2945
|
+
};
|
|
2946
|
+
},
|
|
2947
|
+
handler: (input, config) => {
|
|
2948
|
+
const errors = propMappingErrors(config, {
|
|
2949
|
+
component: String(input.path),
|
|
2950
|
+
rules: input.data.rules
|
|
2951
|
+
});
|
|
2952
|
+
if (errors.length > 0) rejectData(errors.join(" "));
|
|
2953
|
+
return config;
|
|
2954
|
+
}
|
|
2955
|
+
},
|
|
2956
|
+
"map-props-plan": {
|
|
2957
|
+
input: z.object({
|
|
2958
|
+
path: pathInput("component"),
|
|
2959
|
+
data: z.object({
|
|
2960
|
+
rules: propMappingRulesSchema,
|
|
2961
|
+
instances: z.array(z.record(z.string(), z.unknown())).default([]).describe("Instances' props as they are stored or written: each comes back with the rules applied.")
|
|
2962
|
+
})
|
|
2963
|
+
}),
|
|
2964
|
+
readOnly: true,
|
|
2965
|
+
label: "Plan a props mapping",
|
|
2966
|
+
description: "Try a mapping on some instances' props before `map-props` applies it: refuses a rule that writes a prop or value the component doesn't take, and returns each given instance's props with the rules applied. Writes nothing.",
|
|
2967
|
+
scope: "item",
|
|
2968
|
+
title: (entity) => `Plan mapping ${entity} props`,
|
|
2969
|
+
example: ({ config, path }) => exampleMapping(config, path),
|
|
2970
|
+
handler: (input, config) => {
|
|
2971
|
+
const data = input.data;
|
|
2972
|
+
return mapComponentProps(config, {
|
|
2973
|
+
component: String(input.path),
|
|
2974
|
+
rules: data.rules
|
|
2975
|
+
}, data.instances ?? []);
|
|
2976
|
+
}
|
|
2927
2977
|
}
|
|
2928
2978
|
},
|
|
2929
2979
|
invariants: (config) => [
|
|
@@ -3072,5 +3122,35 @@ function exampleProps(config, componentPath) {
|
|
|
3072
3122
|
}
|
|
3073
3123
|
return {};
|
|
3074
3124
|
}
|
|
3125
|
+
/** A runnable `map-props-plan` example: an instance holding the first variant prop's first value, and
|
|
3126
|
+
* a rule moving it onto the prop's second value (or the same one, for a prop with a single value).
|
|
3127
|
+
* A component with no variant gets the other common case, dropping a prop it no longer has.
|
|
3128
|
+
* `map-props` takes the same rules, without the instances. */
|
|
3129
|
+
function exampleMapping(config, componentPath) {
|
|
3130
|
+
const instance = exampleProps(config, componentPath);
|
|
3131
|
+
const [name, value] = Object.entries(instance)[0] ?? [];
|
|
3132
|
+
if (name === void 0 || value === void 0) return {
|
|
3133
|
+
path: componentPath,
|
|
3134
|
+
data: {
|
|
3135
|
+
rules: [{
|
|
3136
|
+
when: { variant: "primary" },
|
|
3137
|
+
unset: ["variant"]
|
|
3138
|
+
}],
|
|
3139
|
+
instances: [{ variant: "primary" }]
|
|
3140
|
+
}
|
|
3141
|
+
};
|
|
3142
|
+
const decl = (config.resolve("component", componentPath)?.toJSON())?.props?.[name];
|
|
3143
|
+
const next = (decl ? propValueDomain(config, decl)?.leaves ?? [] : []).find((leaf) => leaf !== value) ?? value;
|
|
3144
|
+
return {
|
|
3145
|
+
path: componentPath,
|
|
3146
|
+
data: {
|
|
3147
|
+
rules: [{
|
|
3148
|
+
when: { [name]: value },
|
|
3149
|
+
set: { [name]: next }
|
|
3150
|
+
}],
|
|
3151
|
+
instances: [instance]
|
|
3152
|
+
}
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3075
3155
|
//#endregion
|
|
3076
3156
|
export { Component, SLOT_CODE_FORMS, blankComponentBody, canonicalWhen, forwardedNames, layerRef, ownValues, ownsItsValues, storedStyleRules, valueRef };
|
|
@@ -74,7 +74,7 @@ interface RecordedSource {
|
|
|
74
74
|
* The dense-counter `2` a short-lived scheme stamped before the chain existed reads as the mint
|
|
75
75
|
* that replaced it (see `detectedWireVersion`).
|
|
76
76
|
*/
|
|
77
|
-
declare const SERIALIZED_CONFIG_VERSION =
|
|
77
|
+
declare const SERIALIZED_CONFIG_VERSION = 20261030134149;
|
|
78
78
|
interface SerializedConfig {
|
|
79
79
|
/** See {@link SERIALIZED_CONFIG_VERSION}. Always written; a stored artifact may lack it. */
|
|
80
80
|
readonly version: number;
|
package/dist/framework/Config.js
CHANGED
|
@@ -10,8 +10,8 @@ import { borrowedSlug, classifyOverlay, uniteEntries } from "./source-integrity.
|
|
|
10
10
|
import { kindBorrowable, titleCase } from "./registry.js";
|
|
11
11
|
import { isGroupBody } from "./utils/group.js";
|
|
12
12
|
import { authoredBag, cssVar, declaresCssVariable, pinBodyStyleNumbers, stylePropNumberUnit } from "./projections.js";
|
|
13
|
-
import { isAuthoringPhantom, phantomsIn } from "./utils/authoring-phantom.js";
|
|
14
13
|
import { addressOf, editOp, inputOf, opVerb } from "./config-op.js";
|
|
14
|
+
import { isAuthoringPhantom, phantomsIn } from "./utils/authoring-phantom.js";
|
|
15
15
|
import { configSchemas } from "./config-ops.js";
|
|
16
16
|
import { isAuthored } from "./authoring.js";
|
|
17
17
|
import { Entity } from "./Entity.js";
|
|
@@ -6362,7 +6362,7 @@ var Config = class Config {
|
|
|
6362
6362
|
* links from the provided sources. A declared `linkedKind` with no provided source throws. */
|
|
6363
6363
|
hydrateFrom(stored, options) {
|
|
6364
6364
|
const claimed = schemaVersionOf(stored);
|
|
6365
|
-
if (claimed >
|
|
6365
|
+
if (claimed > 20261030134149) throw new SchemaVersionTooNew(claimed);
|
|
6366
6366
|
const wire = upgradeSerializedConfig(stored);
|
|
6367
6367
|
if (!Array.isArray(wire.ownedKinds) || typeof wire.items !== "object" || wire.items === null) throw new ConfigFormatError(`Config "${stored.name ?? "system-config"}" cannot be hydrated: this is not a serialized config-v2 config (no "ownedKinds"/"items"). A pre-cutover build artifact converts through the registered cutover migration — import the config type's module (e.g. \`configs/system\`) before hydrating.`);
|
|
6368
6368
|
const leftover = wire.options;
|
|
@@ -15,7 +15,7 @@ import { Patch, SerializedConfig } from "./Config.js";
|
|
|
15
15
|
* sets this constant to its own older timestamp; the merge ref then carries a registered migration
|
|
16
16
|
* newer than the constant, and the assertion fails until the author re-mints.
|
|
17
17
|
*/
|
|
18
|
-
declare const CURRENT_SCHEMA_VERSION =
|
|
18
|
+
declare const CURRENT_SCHEMA_VERSION = 20261030134149;
|
|
19
19
|
/** What the walkers need to see of an envelope — loose on purpose, so a typed `SerializedConfig`
|
|
20
20
|
* and a raw old-shape record both flow in without casts. */
|
|
21
21
|
interface VersionStamped {
|
|
@@ -13,7 +13,7 @@ import { ConfigFormatError } from "./rejection.js";
|
|
|
13
13
|
* sets this constant to its own older timestamp; the merge ref then carries a registered migration
|
|
14
14
|
* newer than the constant, and the assertion fails until the author re-mints.
|
|
15
15
|
*/
|
|
16
|
-
const CURRENT_SCHEMA_VERSION =
|
|
16
|
+
const CURRENT_SCHEMA_VERSION = 20261030134149;
|
|
17
17
|
/** The mint that moved `options` into the `settings` kind — restated here, rather than imported
|
|
18
18
|
* from the migration that owns it, because that migration imports this module. */
|
|
19
19
|
const SETTINGS_AS_KIND_VERSION = 20260908171448;
|
|
@@ -45,7 +45,7 @@ function registerSchemaMigrations(...migrations) {
|
|
|
45
45
|
for (const migration of migrations) {
|
|
46
46
|
if (migration.version !== 1 && !isMintTimestamp(migration.version)) throw new Error(`Schema migration version ${migration.version} is not a mint timestamp — mint one with \`date +%Y%m%d%H%M%S\`, like a supabase migration.`);
|
|
47
47
|
if (REGISTERED.has(migration.version)) throw new Error(`A schema migration at version ${migration.version} is already registered.`);
|
|
48
|
-
if (migration.version >
|
|
48
|
+
if (migration.version > 20261030134149) throw new Error(`Schema migration ${migration.version} is newer than CURRENT_SCHEMA_VERSION (${CURRENT_SCHEMA_VERSION}) — a stale branch merged, or the mint forgot to bump the constant. Re-mint: set CURRENT_SCHEMA_VERSION to the newest migration version.`);
|
|
49
49
|
REGISTERED.set(migration.version, migration);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -102,7 +102,7 @@ function pendingFor(version, target, options) {
|
|
|
102
102
|
* Already-current input passes through untouched — same reference, so hot paths pay nothing.
|
|
103
103
|
*/
|
|
104
104
|
function upgradeSerializedConfig(json, options) {
|
|
105
|
-
const target = options?.target ??
|
|
105
|
+
const target = options?.target ?? 20261030134149;
|
|
106
106
|
const version = detectedWireVersion(json);
|
|
107
107
|
if (version > target) throw new SchemaVersionTooNew(version, target);
|
|
108
108
|
const raw = json;
|
|
@@ -160,7 +160,7 @@ function upgradedSources(json, options) {
|
|
|
160
160
|
* meaning under the current schema and replay skips it — reported by the caller, not swallowed here.
|
|
161
161
|
*/
|
|
162
162
|
function upgradePatch(patch, writtenAt, options) {
|
|
163
|
-
const target = options?.target ??
|
|
163
|
+
const target = options?.target ?? 20261030134149;
|
|
164
164
|
if (writtenAt > target) throw new SchemaVersionTooNew(writtenAt, target);
|
|
165
165
|
let current = patch;
|
|
166
166
|
for (const migration of pendingFor(writtenAt, target, options)) {
|
|
@@ -179,7 +179,7 @@ function upgradePatch(patch, writtenAt, options) {
|
|
|
179
179
|
* stamp would cause.
|
|
180
180
|
*/
|
|
181
181
|
function upgradeDraftEntries(draft, options) {
|
|
182
|
-
const target = options?.target ??
|
|
182
|
+
const target = options?.target ?? 20261030134149;
|
|
183
183
|
const entries = [];
|
|
184
184
|
const held = [];
|
|
185
185
|
const retired = [];
|
package/dist/index.js
CHANGED
|
@@ -60,6 +60,7 @@ import { arbitraryClassName, arbitraryNeedsClass, classLeaf, classSelector, clea
|
|
|
60
60
|
import { ANATOMY_MARKER, MOTION_FACTORY, anatomyElementComponent, anatomyTextProp, boundElementType, componentSpec, deriveSpec, entryKeyOf, expandComponent, expandSpec, instanceSpec, layerTextProp, liveStateProps, previewSpec, propInto, propsIntoLayer, readStatePointer, resolveValue, resolveVisibility, routeOf, routesOf, slotTargetsOf, specWithResolvedVisibility, statePointerProp, valueForwardsOf, visibilityStateProps, visibilityTerms, withoutTruthyTerm, wrappedElementKey } from "./framework/render-spec.js";
|
|
61
61
|
import { canvasCellAt, canvasDefaultCoordinate, cellAxes } from "./spec/cell.js";
|
|
62
62
|
import { instanceHolding } from "./spec/instance.js";
|
|
63
|
+
import { addressOf, editOp, inputOf, opOf, opVerb } from "./framework/config-op.js";
|
|
63
64
|
import { componentSourceDerivedFields } from "./entities/system/component-source-fields.js";
|
|
64
65
|
import { styleSetEdits } from "./entities/system/component-style-set.js";
|
|
65
66
|
import { convertLinkedTokenValue, prepareLinkedTokenBody } from "./entities/system/linked-token-values.js";
|
|
@@ -73,7 +74,6 @@ import { glyphExportNames, glyphType, iconCategories, iconLibraries, iconLibrary
|
|
|
73
74
|
import { glyphSourcesOf, iconResolver, nameIconsInTree } from "./capture/icon-resolver.js";
|
|
74
75
|
import { PLACED_NODE_KEY, specToNodePatches } from "./entities/canvas/node-patches.js";
|
|
75
76
|
import { Device } from "./entities/system/Device.js";
|
|
76
|
-
import { addressOf, editOp, inputOf, opOf, opVerb } from "./framework/config-op.js";
|
|
77
77
|
import { renderSignature, signatureOf } from "./framework/signature.js";
|
|
78
78
|
import { authoringSignatures } from "./framework/config-ops.js";
|
|
79
79
|
import { defineDerivedEntity } from "./framework/defineDerivedEntity.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** `component/map-props` is a new op, with `component/map-props-plan` beside it: a mapping of a
|
|
2
|
+
* component's old props onto its new ones, applied to the documents of whichever host runs it. It
|
|
3
|
+
* writes no patch, so no stored config or draft row carries it, and this is an identity hop. Retire
|
|
4
|
+
* it only when no supported artifact predates this schema. */
|
|
5
|
+
import type { SchemaMigration } from '../framework/schema-version';
|
|
6
|
+
export declare const COMPONENT_MAP_PROPS_VERSION = 20261030134149;
|
|
7
|
+
export declare const componentMapPropsMigration: SchemaMigration;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/migrations/20261030134149_component_map_props.ts
|
|
2
|
+
const COMPONENT_MAP_PROPS_VERSION = 20261030134149;
|
|
3
|
+
const componentMapPropsMigration = {
|
|
4
|
+
version: COMPONENT_MAP_PROPS_VERSION,
|
|
5
|
+
up: (json) => json
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { COMPONENT_MAP_PROPS_VERSION, componentMapPropsMigration };
|
package/dist/spec/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { AcceptsChildNodes, ChildNodesClassification, ClassifyChildNodes, buildA
|
|
|
7
7
|
import { CollapseTextLabelsResult, CollapsedTextLabel, collapseWrappedTextLabels } from "./collapse-text-labels.js";
|
|
8
8
|
import { GlyphJsxForm, GlyphNodeResolver, glyphJsxForm, glyphMemberOf, jsxIdentifier, parseGlyphType } from "./glyph-jsx.js";
|
|
9
9
|
import { JsxToSpecOptions, UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps } from "./jsxToSpec.js";
|
|
10
|
-
import { MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, propMappingErrors } from "./prop-mapping.js";
|
|
10
|
+
import { MappedInstance, MappedProps, MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn } from "./prop-mapping.js";
|
|
11
11
|
import { SpecToJsxOptions, SpecToJsxResult, specToJsx } from "./specToJsx.js";
|
|
12
12
|
|
|
13
13
|
//#region src/spec/index.d.ts
|
|
@@ -29,4 +29,4 @@ import { SpecToJsxOptions, SpecToJsxResult, specToJsx } from "./specToJsx.js";
|
|
|
29
29
|
* element, without changing the rendered layer hierarchy. */
|
|
30
30
|
declare const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
|
|
31
31
|
//#endregion
|
|
32
|
-
export { type AcceptsChildNodes, type Cell, type CellSlot, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GlyphJsxForm, type GlyphNodeResolver, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type MatchedValue, type PreviewElement, type PreviewRef, type PreviewSpecNode, type PreviewValue, type PropMapping, type PropMappingConflict, type PropMappingResult, type PropMappingRule, type RewritableElement, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type Slot, type SlotCell, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
|
|
32
|
+
export { type AcceptsChildNodes, type Cell, type CellSlot, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GlyphJsxForm, type GlyphNodeResolver, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type MappedInstance, type MappedProps, type MatchedValue, type PreviewElement, type PreviewRef, type PreviewSpecNode, type PreviewValue, type PropMapping, type PropMappingConflict, type PropMappingResult, type PropMappingRule, type RewritableElement, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type Slot, type SlotCell, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, mapComponentProps, onlyMapsProps, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
|
package/dist/spec/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { graftPreviewElements, graftSlotContent, hasPreviewElementDeep, isComponentRef, isPreviewElement, isPreviewRef, previewElementNodes, previewElementType } from "./preview-elements.js";
|
|
2
|
+
import { composeRenames, documentRenamesFromPatches, documentRenamesFromSource, rewriteSpecRefs, styleNumberReadingRenames } from "./rewrite-refs.js";
|
|
3
|
+
import { applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn } from "./prop-mapping.js";
|
|
2
4
|
import { glyphJsxForm, glyphMemberOf, jsxIdentifier, parseGlyphType } from "./glyph-jsx.js";
|
|
3
5
|
import { applyForcedModifiers, collectForcedModifierProps } from "./apply-forced-modifiers.js";
|
|
4
6
|
import { collapseWrappedTextLabels } from "./collapse-text-labels.js";
|
|
5
7
|
import { buildAcceptsChildNodes, buildClassifyChildNodes, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots } from "./empty-node-slots.js";
|
|
6
8
|
import { UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps } from "./jsxToSpec.js";
|
|
7
|
-
import { composeRenames, documentRenamesFromPatches, documentRenamesFromSource, rewriteSpecRefs, styleNumberReadingRenames } from "./rewrite-refs.js";
|
|
8
|
-
import { applyPropMapping, propMappingErrors } from "./prop-mapping.js";
|
|
9
9
|
import { specToJsx } from "./specToJsx.js";
|
|
10
10
|
//#region src/spec/index.ts
|
|
11
11
|
/**
|
|
@@ -26,4 +26,4 @@ import { specToJsx } from "./specToJsx.js";
|
|
|
26
26
|
* element, without changing the rendered layer hierarchy. */
|
|
27
27
|
const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
|
|
28
28
|
//#endregion
|
|
29
|
-
export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
|
|
29
|
+
export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, applyPropMapping, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, composeRenames, documentRenamesFromPatches, documentRenamesFromSource, glyphJsxForm, glyphMemberOf, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, mapComponentProps, onlyMapsProps, parseGlyphType, previewElementNodes, previewElementType, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, styleNumberReadingRenames, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { ConfigEdit } from "../framework/config-op.js";
|
|
1
2
|
import { Config } from "../framework/Config.js";
|
|
2
3
|
import { RewritableSpec } from "./rewrite-refs.js";
|
|
4
|
+
import { z } from "zod";
|
|
3
5
|
|
|
4
6
|
//#region src/spec/prop-mapping.d.ts
|
|
5
7
|
/** A stored value a rule can match: what a design file holds for a variant, string or flag. */
|
|
@@ -17,6 +19,31 @@ interface PropMapping {
|
|
|
17
19
|
readonly component: string;
|
|
18
20
|
readonly rules: readonly PropMappingRule[];
|
|
19
21
|
}
|
|
22
|
+
/** A rule as a tool takes it: the shape every surface that applies a mapping parses its input with. */
|
|
23
|
+
declare const propMappingRuleSchema: z.ZodObject<{
|
|
24
|
+
when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
25
|
+
set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
26
|
+
unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
/** The rules of a mapping, as a tool takes them. */
|
|
29
|
+
declare const propMappingRulesSchema: z.ZodArray<z.ZodObject<{
|
|
30
|
+
when: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
31
|
+
set: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
32
|
+
unset: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
33
|
+
}, z.core.$strip>>;
|
|
34
|
+
/**
|
|
35
|
+
* The mappings a batch of edits carries, in order: each `component/map-props` op as the mapping it
|
|
36
|
+
* applies. The op changes nothing in the config, so the host that ran the batch applies these to the
|
|
37
|
+
* documents it holds once the batch lands: Create to a branch's design files, a checkout to its call
|
|
38
|
+
* sites. An op whose rules don't parse is left out; running the batch refused it already.
|
|
39
|
+
*/
|
|
40
|
+
declare function propMappingsIn(edits: readonly ConfigEdit[]): PropMapping[];
|
|
41
|
+
/**
|
|
42
|
+
* Whether a batch is mappings alone: every edit a `component/map-props` op. Such a batch changes
|
|
43
|
+
* nothing in the config, so a read-only branch (`main`, a published version) still runs it: its
|
|
44
|
+
* design files take edits, and those are all it moves.
|
|
45
|
+
*/
|
|
46
|
+
declare function onlyMapsProps(edits: readonly ConfigEdit[]): boolean;
|
|
20
47
|
/** A bag two matching rules wrote different values into, left as it was. */
|
|
21
48
|
interface PropMappingConflict {
|
|
22
49
|
readonly element: string;
|
|
@@ -45,5 +72,26 @@ declare function propMappingErrors(config: Config, mapping: PropMapping): string
|
|
|
45
72
|
* first: this applies what it's given.
|
|
46
73
|
*/
|
|
47
74
|
declare function applyPropMapping(config: Config, spec: RewritableSpec, mapping: PropMapping): PropMappingResult;
|
|
75
|
+
/** One instance's props with a mapping tried on it. */
|
|
76
|
+
interface MappedInstance {
|
|
77
|
+
readonly props: Record<string, unknown>;
|
|
78
|
+
readonly changed: boolean;
|
|
79
|
+
/** The props two matching rules wrote different values to, which leave the instance as it was. */
|
|
80
|
+
readonly conflicts: readonly string[];
|
|
81
|
+
}
|
|
82
|
+
type MappedProps = {
|
|
83
|
+
readonly ok: false;
|
|
84
|
+
readonly errors: readonly string[];
|
|
85
|
+
} | {
|
|
86
|
+
readonly ok: true;
|
|
87
|
+
readonly instances: readonly MappedInstance[];
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* A mapping tried on some instances' props, writing nothing: the `component/map-props` op. Refused,
|
|
91
|
+
* with each reason, when the component can't take what it writes. Each bag is mapped as a design
|
|
92
|
+
* file's instance is, modifier blocks and layer overrides included, so what it returns is what
|
|
93
|
+
* applying the mapping to a document or a call site would store.
|
|
94
|
+
*/
|
|
95
|
+
declare function mapComponentProps(config: Config, mapping: PropMapping, instances: readonly Record<string, unknown>[]): MappedProps;
|
|
48
96
|
//#endregion
|
|
49
|
-
export { MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, propMappingErrors };
|
|
97
|
+
export { MappedInstance, MappedProps, MatchedValue, PropMapping, PropMappingConflict, PropMappingResult, PropMappingRule, applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn };
|
|
@@ -1,9 +1,67 @@
|
|
|
1
1
|
import { kindOf, pathOf } from "../framework/utils/refs.js";
|
|
2
2
|
import { propValueDomain, resolveComponentProps } from "../framework/projections.js";
|
|
3
3
|
import { surfaceProp } from "../framework/prop-surface.js";
|
|
4
|
+
import { editOp } from "../framework/config-op.js";
|
|
4
5
|
import { layerComponent, modifierBlock, nestedBag } from "./rewrite-refs.js";
|
|
6
|
+
import { z } from "zod";
|
|
5
7
|
//#region src/spec/prop-mapping.ts
|
|
6
8
|
/**
|
|
9
|
+
* Moving what instances of a component stored onto its new API: `variant: 'brand-secondary'` →
|
|
10
|
+
* `color: 'brand', prominence: 'outline'`, `size: 'xs'` → `size: 'sm'`.
|
|
11
|
+
*
|
|
12
|
+
* A rename carries a document across a change with one answer per name, so it can be recorded and
|
|
13
|
+
* replayed (`rewrite-refs.ts`). A reshaped API has no one answer: whether `brand-secondary` is an
|
|
14
|
+
* outline or a ghost is a design decision, so the mapping comes from the person (with an agent
|
|
15
|
+
* proposing it) and is applied here once. Nothing stores it.
|
|
16
|
+
*
|
|
17
|
+
* A mapping is a list of rules for one component. Every rule's `when` is matched against the bag
|
|
18
|
+
* as it was stored, and every rule that matches applies, so `variant` and `size` can be mapped by
|
|
19
|
+
* separate rules and a pair of them by one rule naming both. A rule removes only what its `unset`
|
|
20
|
+
* names: a prop a rule matches on may be one the new API still has.
|
|
21
|
+
*
|
|
22
|
+
* Positions follow the rename rewriter's: an element's own props, every modifier block nested in
|
|
23
|
+
* them (`_hover: { variant: … }` is the same element's props under a condition), and each
|
|
24
|
+
* `layerProps` bag whose layer renders the component.
|
|
25
|
+
*/
|
|
26
|
+
/** A rule as a tool takes it: the shape every surface that applies a mapping parses its input with. */
|
|
27
|
+
const propMappingRuleSchema = z.object({
|
|
28
|
+
when: z.record(z.string(), z.union([
|
|
29
|
+
z.string(),
|
|
30
|
+
z.number(),
|
|
31
|
+
z.boolean()
|
|
32
|
+
])).describe("The stored values an instance must hold for the rule to apply, every one of them: { \"variant\": \"brand-secondary\" }."),
|
|
33
|
+
set: z.record(z.string(), z.unknown()).optional().describe("The props to write: { \"color\": \"brand\", \"prominence\": \"outline\" }."),
|
|
34
|
+
unset: z.array(z.string()).optional().describe("The props to remove. A prop the rule matches on stays unless it is named here, so a removed prop has to be.")
|
|
35
|
+
});
|
|
36
|
+
/** The rules of a mapping, as a tool takes them. */
|
|
37
|
+
const propMappingRulesSchema = z.array(propMappingRuleSchema).min(1).describe("Every rule whose `when` matches an instance as stored applies to it, so separate props are mapped by separate rules, and a combination by one rule naming both.");
|
|
38
|
+
/**
|
|
39
|
+
* The mappings a batch of edits carries, in order: each `component/map-props` op as the mapping it
|
|
40
|
+
* applies. The op changes nothing in the config, so the host that ran the batch applies these to the
|
|
41
|
+
* documents it holds once the batch lands: Create to a branch's design files, a checkout to its call
|
|
42
|
+
* sites. An op whose rules don't parse is left out; running the batch refused it already.
|
|
43
|
+
*/
|
|
44
|
+
function propMappingsIn(edits) {
|
|
45
|
+
return edits.flatMap((edit) => {
|
|
46
|
+
const { op } = editOp(edit);
|
|
47
|
+
if (op.kind !== "component" || op.operation !== "map-props") return [];
|
|
48
|
+
const rules = propMappingRulesSchema.safeParse(op.data?.rules);
|
|
49
|
+
if (!rules.success || typeof op.path !== "string") return [];
|
|
50
|
+
return [{
|
|
51
|
+
component: op.path.replace(/^component:/, ""),
|
|
52
|
+
rules: rules.data
|
|
53
|
+
}];
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Whether a batch is mappings alone: every edit a `component/map-props` op. Such a batch changes
|
|
58
|
+
* nothing in the config, so a read-only branch (`main`, a published version) still runs it: its
|
|
59
|
+
* design files take edits, and those are all it moves.
|
|
60
|
+
*/
|
|
61
|
+
function onlyMapsProps(edits) {
|
|
62
|
+
return edits.length > 0 && propMappingsIn(edits).length === edits.length;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
7
65
|
* Why a prop can't hold `value`, or `undefined` when it can. A variant's or a slot's values come off
|
|
8
66
|
* its surface, which follows a forward to the prop it routes to. Any other prop with a domain (a
|
|
9
67
|
* composite, a style property) answers through its declaration, as validation does. A bracketed
|
|
@@ -165,5 +223,33 @@ function applyPropMapping(config, spec, mapping) {
|
|
|
165
223
|
conflicts
|
|
166
224
|
};
|
|
167
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* A mapping tried on some instances' props, writing nothing: the `component/map-props` op. Refused,
|
|
228
|
+
* with each reason, when the component can't take what it writes. Each bag is mapped as a design
|
|
229
|
+
* file's instance is, modifier blocks and layer overrides included, so what it returns is what
|
|
230
|
+
* applying the mapping to a document or a call site would store.
|
|
231
|
+
*/
|
|
232
|
+
function mapComponentProps(config, mapping, instances) {
|
|
233
|
+
const errors = propMappingErrors(config, mapping);
|
|
234
|
+
if (errors.length > 0) return {
|
|
235
|
+
ok: false,
|
|
236
|
+
errors
|
|
237
|
+
};
|
|
238
|
+
const { spec, conflicts } = applyPropMapping(config, { elements: Object.fromEntries(instances.map((props, i) => [String(i), {
|
|
239
|
+
type: `component:${mapping.component}`,
|
|
240
|
+
props
|
|
241
|
+
}])) }, mapping);
|
|
242
|
+
return {
|
|
243
|
+
ok: true,
|
|
244
|
+
instances: instances.map((props, i) => {
|
|
245
|
+
const after = spec.elements?.[String(i)]?.props ?? props;
|
|
246
|
+
return {
|
|
247
|
+
props: after,
|
|
248
|
+
changed: after !== props,
|
|
249
|
+
conflicts: conflicts.filter((conflict) => conflict.element === String(i)).flatMap((conflict) => conflict.props)
|
|
250
|
+
};
|
|
251
|
+
})
|
|
252
|
+
};
|
|
253
|
+
}
|
|
168
254
|
//#endregion
|
|
169
|
-
export { applyPropMapping, propMappingErrors };
|
|
255
|
+
export { applyPropMapping, mapComponentProps, onlyMapsProps, propMappingErrors, propMappingRuleSchema, propMappingRulesSchema, propMappingsIn };
|