@forumone/throughline-design-system 0.0.0 → 1.0.0-next.2

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.
Files changed (122) hide show
  1. package/CHANGELOG.md +182 -0
  2. package/LICENSE +21 -0
  3. package/README.md +23 -0
  4. package/bin/check-block-props.mjs +51 -0
  5. package/bin/stub-styles.mjs +28 -0
  6. package/dist/admin/BlockGuidance.d.ts +13 -0
  7. package/dist/admin/BlockGuidance.d.ts.map +1 -0
  8. package/dist/admin/BlockGuidance.js +13 -0
  9. package/dist/admin/BlockGuidance.js.map +1 -0
  10. package/dist/admin/BlockSummary.d.ts +10 -0
  11. package/dist/admin/BlockSummary.d.ts.map +1 -0
  12. package/dist/admin/BlockSummary.js +73 -0
  13. package/dist/admin/BlockSummary.js.map +1 -0
  14. package/dist/admin/RowSummary.d.ts +15 -0
  15. package/dist/admin/RowSummary.d.ts.map +1 -0
  16. package/dist/admin/RowSummary.js +16 -0
  17. package/dist/admin/RowSummary.js.map +1 -0
  18. package/dist/admin/summary.d.ts +18 -0
  19. package/dist/admin/summary.d.ts.map +1 -0
  20. package/dist/admin/summary.js +48 -0
  21. package/dist/admin/summary.js.map +1 -0
  22. package/dist/altText.d.ts +16 -0
  23. package/dist/altText.d.ts.map +1 -0
  24. package/dist/altText.js +54 -0
  25. package/dist/altText.js.map +1 -0
  26. package/dist/client.d.ts +15 -0
  27. package/dist/client.d.ts.map +1 -0
  28. package/dist/client.js +12 -0
  29. package/dist/client.js.map +1 -0
  30. package/dist/contract/index.d.ts +5 -0
  31. package/dist/contract/index.d.ts.map +1 -0
  32. package/dist/contract/index.js +4 -0
  33. package/dist/contract/index.js.map +1 -0
  34. package/dist/contract/lint.d.ts +38 -0
  35. package/dist/contract/lint.d.ts.map +1 -0
  36. package/dist/contract/lint.js +110 -0
  37. package/dist/contract/lint.js.map +1 -0
  38. package/dist/contract/loader.d.ts +49 -0
  39. package/dist/contract/loader.d.ts.map +1 -0
  40. package/dist/contract/loader.js +95 -0
  41. package/dist/contract/loader.js.map +1 -0
  42. package/dist/contract/manifest.d.ts +454 -0
  43. package/dist/contract/manifest.d.ts.map +1 -0
  44. package/dist/contract/manifest.js +29 -0
  45. package/dist/contract/manifest.js.map +1 -0
  46. package/dist/contract/schema.d.ts +334 -0
  47. package/dist/contract/schema.d.ts.map +1 -0
  48. package/dist/contract/schema.js +224 -0
  49. package/dist/contract/schema.js.map +1 -0
  50. package/dist/generate/blocks.d.ts +79 -0
  51. package/dist/generate/blocks.d.ts.map +1 -0
  52. package/dist/generate/blocks.js +118 -0
  53. package/dist/generate/blocks.js.map +1 -0
  54. package/dist/generate/fields.d.ts +60 -0
  55. package/dist/generate/fields.d.ts.map +1 -0
  56. package/dist/generate/fields.js +478 -0
  57. package/dist/generate/fields.js.map +1 -0
  58. package/dist/generate/guidance.d.ts +5 -0
  59. package/dist/generate/guidance.d.ts.map +1 -0
  60. package/dist/generate/guidance.js +29 -0
  61. package/dist/generate/guidance.js.map +1 -0
  62. package/dist/generate/index.d.ts +13 -0
  63. package/dist/generate/index.d.ts.map +1 -0
  64. package/dist/generate/index.js +9 -0
  65. package/dist/generate/index.js.map +1 -0
  66. package/dist/generate/labels.d.ts +42 -0
  67. package/dist/generate/labels.d.ts.map +1 -0
  68. package/dist/generate/labels.js +226 -0
  69. package/dist/generate/labels.js.map +1 -0
  70. package/dist/generate/layout.d.ts +42 -0
  71. package/dist/generate/layout.d.ts.map +1 -0
  72. package/dist/generate/layout.js +252 -0
  73. package/dist/generate/layout.js.map +1 -0
  74. package/dist/generate/selectOptionSnapshot.d.ts +26 -0
  75. package/dist/generate/selectOptionSnapshot.d.ts.map +1 -0
  76. package/dist/generate/selectOptionSnapshot.js +47 -0
  77. package/dist/generate/selectOptionSnapshot.js.map +1 -0
  78. package/dist/generate/selectOptions.d.ts +64 -0
  79. package/dist/generate/selectOptions.d.ts.map +1 -0
  80. package/dist/generate/selectOptions.js +145 -0
  81. package/dist/generate/selectOptions.js.map +1 -0
  82. package/dist/overrides.d.ts +71 -0
  83. package/dist/overrides.d.ts.map +1 -0
  84. package/dist/overrides.js +16 -0
  85. package/dist/overrides.js.map +1 -0
  86. package/dist/render/RenderBlocks.d.ts +67 -0
  87. package/dist/render/RenderBlocks.d.ts.map +1 -0
  88. package/dist/render/RenderBlocks.js +31 -0
  89. package/dist/render/RenderBlocks.js.map +1 -0
  90. package/dist/render/coerce.d.ts +78 -0
  91. package/dist/render/coerce.d.ts.map +1 -0
  92. package/dist/render/coerce.js +243 -0
  93. package/dist/render/coerce.js.map +1 -0
  94. package/dist/render/index.d.ts +5 -0
  95. package/dist/render/index.d.ts.map +1 -0
  96. package/dist/render/index.js +3 -0
  97. package/dist/render/index.js.map +1 -0
  98. package/dist/testing/checkBlockProps.d.ts +66 -0
  99. package/dist/testing/checkBlockProps.d.ts.map +1 -0
  100. package/dist/testing/checkBlockProps.js +241 -0
  101. package/dist/testing/checkBlockProps.js.map +1 -0
  102. package/dist/testing/checkBlockPropsCli.d.ts +12 -0
  103. package/dist/testing/checkBlockPropsCli.d.ts.map +1 -0
  104. package/dist/testing/checkBlockPropsCli.js +86 -0
  105. package/dist/testing/checkBlockPropsCli.js.map +1 -0
  106. package/dist/testing/contractDefaults.d.ts +21 -0
  107. package/dist/testing/contractDefaults.d.ts.map +1 -0
  108. package/dist/testing/contractDefaults.js +65 -0
  109. package/dist/testing/contractDefaults.js.map +1 -0
  110. package/dist/testing/describeBlockInvariants.d.ts +39 -0
  111. package/dist/testing/describeBlockInvariants.d.ts.map +1 -0
  112. package/dist/testing/describeBlockInvariants.js +71 -0
  113. package/dist/testing/describeBlockInvariants.js.map +1 -0
  114. package/dist/testing/index.d.ts +18 -0
  115. package/dist/testing/index.d.ts.map +1 -0
  116. package/dist/testing/index.js +14 -0
  117. package/dist/testing/index.js.map +1 -0
  118. package/dist/testing/untouchedBlocks.d.ts +48 -0
  119. package/dist/testing/untouchedBlocks.d.ts.map +1 -0
  120. package/dist/testing/untouchedBlocks.js +155 -0
  121. package/dist/testing/untouchedBlocks.js.map +1 -0
  122. package/package.json +108 -1
@@ -0,0 +1,252 @@
1
+ import { pairTitle } from './labels.js';
2
+ /*
3
+ How a block's fields are arranged on screen, as distinct from what they are.
4
+
5
+ Everything here is presentational. An unnamed `group` and a `collapsible` hold
6
+ their children in the same data object as their siblings, so moving a field
7
+ into one changes where it is drawn and nothing about where it is stored: no
8
+ migration, no change to `payload-types.ts`, and `coerce` — which walks the
9
+ contract, not this config — never sees the difference. That is the property
10
+ that makes it safe to rearrange every block at once.
11
+
12
+ Two rules, both read off the contract rather than configured per block.
13
+
14
+ **A label and its link are one thing.** A contract names a call to action as a
15
+ pair of siblings, `ctaLabel` and `ctaHref`, because the component takes them as
16
+ two props. Generated literally, the editor drew them as unrelated fields with
17
+ unrelated headings: a text box captioned "Cta Label" and, below it, a group
18
+ headed "Cta Href". So a `<prefix>Label` text field and a `<prefix>Href` or
19
+ `<prefix>Url` link become one group headed by what they are together — "Call to
20
+ action", "View all link" — at the position of whichever came first. A
21
+ `<prefix>Icon` beside them joins too, since it is drawn inside the same button.
22
+
23
+ **What most authors should leave alone goes behind a disclosure.** Top-level
24
+ selects, checkboxes and numbers on a block are how it looks — heading level,
25
+ variant, columns, aspect ratio — and every one has a default the component
26
+ already chose well. A field the contract marks `advanced` is the same kind of
27
+ thing in words: a screen-reader label or a status message the component
28
+ already supplies. Both move to one collapsed "More options" section at the end
29
+ of the block, so an author reading a block sees its content first. A field the
30
+ contract marks `required` is never tucked away, whatever its type: a required
31
+ field hidden behind a closed section is an error nobody can find.
32
+
33
+ Only the top level gets the disclosure. Inside an array, a collapsed section per
34
+ row would be one more click on every card, which is the cost this exists to
35
+ remove.
36
+ */
37
+ /** The data-bearing field types that set how a block looks rather than what it says. */
38
+ const SETTING_TYPES = new Set(['select', 'boolean', 'number']);
39
+ const LINK_SUFFIX = /^(.+)(Href|Url)$/;
40
+ /** The label/link pairs among one level's fields, by contract index. */
41
+ function findPairs(contract, generated) {
42
+ const index = new Map(contract.map((field, i) => [field.name, i]));
43
+ const pairs = [];
44
+ contract.forEach((field, i) => {
45
+ // An `advanced` link is one the component fills in itself —
46
+ // `ShareDiscussion.copyUrl` is the current page unless told otherwise — so
47
+ // it goes with the other defaults rather than beside the label it shares a
48
+ // prefix with.
49
+ if (field.type !== 'link' || field.advanced || !generated[i])
50
+ return;
51
+ const prefix = LINK_SUFFIX.exec(field.name)?.[1];
52
+ if (!prefix)
53
+ return;
54
+ const label = index.get(`${prefix}Label`);
55
+ if (label === undefined || contract[label]?.type !== 'text' || !generated[label])
56
+ return;
57
+ const icon = index.get(`${prefix}Icon`);
58
+ pairs.push({
59
+ prefix,
60
+ link: i,
61
+ label,
62
+ ...(icon !== undefined && generated[icon] ? { icon } : {}),
63
+ });
64
+ });
65
+ return pairs;
66
+ }
67
+ /**
68
+ * A link group drawn inside its pair: the pair's heading already names it.
69
+ *
70
+ * Its description stays on it. Payload draws a group's description only in
71
+ * the header a label opens, so with `label: false` nothing shows twice — and
72
+ * `payload-types.ts`, which carries each description as JSDoc on the field it
73
+ * belongs to, keeps the contract's guidance on the property it describes.
74
+ */
75
+ function unheaded(link) {
76
+ return { ...link, label: false };
77
+ }
78
+ function pairGroup(pair, contract, generated) {
79
+ const link = generated[pair.link];
80
+ const label = generated[pair.label];
81
+ const icon = pair.icon === undefined ? null : generated[pair.icon];
82
+ // The link's description is about the destination, which is what the pair's
83
+ // heading introduces, so it moves up to sit under that heading.
84
+ const description = contract[pair.link]?.constraints;
85
+ return {
86
+ type: 'group',
87
+ label: pairTitle(pair.prefix),
88
+ ...(description ? { admin: { description } } : {}),
89
+ fields: [
90
+ { ...label, label: 'Label' },
91
+ ...(icon ? [{ ...icon, label: 'Icon' }] : []),
92
+ unheaded(link),
93
+ ],
94
+ };
95
+ }
96
+ function isSetting(field) {
97
+ return !field.required && (SETTING_TYPES.has(field.type) || field.advanced === true);
98
+ }
99
+ /**
100
+ * One level's generated fields, in the order and grouping an author reads them.
101
+ *
102
+ * `generated[i]` is the Payload field for `contract[i]`, or null where the
103
+ * contract field is not authorable. Nulls are dropped here, so the caller does
104
+ * not have to.
105
+ */
106
+ export function arrange(contract, generated, options) {
107
+ const pairs = findPairs(contract, generated);
108
+ const pairAt = new Map();
109
+ const consumed = new Set();
110
+ for (const pair of pairs) {
111
+ const members = [pair.link, pair.label, ...(pair.icon === undefined ? [] : [pair.icon])];
112
+ pairAt.set(Math.min(...members), pair);
113
+ members.forEach(member => consumed.add(member));
114
+ }
115
+ const main = [];
116
+ const more = [];
117
+ contract.forEach((field, i) => {
118
+ const pair = pairAt.get(i);
119
+ if (pair) {
120
+ main.push(pairGroup(pair, contract, generated));
121
+ return;
122
+ }
123
+ const built = generated[i];
124
+ if (!built || consumed.has(i))
125
+ return;
126
+ (options.disclose && isSetting(field) ? more : main).push(built);
127
+ });
128
+ if (more.length === 0)
129
+ return main;
130
+ return [
131
+ ...main,
132
+ {
133
+ type: 'collapsible',
134
+ label: 'More options',
135
+ admin: {
136
+ initCollapsed: true,
137
+ description: 'Layout and wording the component already sets sensibly. Change them only when this block needs to differ.',
138
+ },
139
+ fields: more,
140
+ },
141
+ ];
142
+ }
143
+ /*
144
+ Two more rules, for the rows of an array rather than the top of a block.
145
+
146
+ **A row of a few short strings is one line.** A stat is a figure and a label,
147
+ a fact is a label and a value, and generated literally each row was two
148
+ full-width inputs stacked under each other — a 12-character box as wide as the
149
+ editor, then its partner below it. When every field of a row is a single-line
150
+ text input and there are only two or three of them, they share one line, each
151
+ as wide as its `maxLength` is long against the others', and never narrower
152
+ than it can be read in. A row holding anything else is left stacked: an image
153
+ or a link beside a text box is a layout to design, not one to derive.
154
+
155
+ **A row is named by what it says.** See `../admin/summary.ts`. The header
156
+ reads the row's first one or two plain-text fields — not a setting, not an
157
+ `advanced` field, and not the label of a call to action, which says "Read more"
158
+ in every row it appears in.
159
+
160
+ Both are drawing only. A `row` stores its children flat, exactly as the
161
+ unnamed groups above do, and a row label is a component in the import map.
162
+ */
163
+ /** How many short fields one line holds before it is easier read stacked. */
164
+ const ONE_LINE_MAX_FIELDS = 3;
165
+ /**
166
+ * An array row's fields, on one line when they are all short single-line text.
167
+ *
168
+ * Widths are each field's share of the row's combined `maxLength`, floored so
169
+ * a 12-character figure beside a 60-character label is still a box somebody
170
+ * can type into.
171
+ */
172
+ export function oneLine(fields) {
173
+ if (fields.length < 2 || fields.length > ONE_LINE_MAX_FIELDS)
174
+ return fields;
175
+ const lengths = [];
176
+ for (const field of fields) {
177
+ if (field.type !== 'text' || typeof field.maxLength !== 'number')
178
+ return fields;
179
+ lengths.push(field.maxLength);
180
+ }
181
+ // A field whose share falls under the floor is held at it, and the rest of
182
+ // the line is shared among the others in proportion — repeated, because
183
+ // raising one can push a third under.
184
+ const floor = 1 / (fields.length + 2);
185
+ const held = new Set();
186
+ let shares = [];
187
+ for (let settled = false; !settled;) {
188
+ const free = lengths.reduce((sum, length, i) => (held.has(i) ? sum : sum + length), 0);
189
+ const room = 1 - held.size * floor;
190
+ shares = lengths.map((length, i) => (held.has(i) ? floor : (length / free) * room));
191
+ const under = shares.findIndex((share, i) => !held.has(i) && share < floor);
192
+ if (under === -1)
193
+ settled = true;
194
+ else
195
+ held.add(under);
196
+ }
197
+ const widths = shares.map(share => Math.round(share * 100));
198
+ // Rounding can leave the line a point over or under; the last field absorbs it.
199
+ widths[widths.length - 1] += 100 - widths.reduce((sum, width) => sum + width, 0);
200
+ return [
201
+ {
202
+ type: 'row',
203
+ fields: fields.map((field, i) => ({
204
+ ...field,
205
+ admin: { ...('admin' in field ? field.admin : {}), width: `${widths[i]}%` },
206
+ })),
207
+ },
208
+ ];
209
+ }
210
+ /** The most fields a row header joins. More reads as a sentence, not a name. */
211
+ const SUMMARY_FIELDS = 2;
212
+ /**
213
+ * Which of an array row's fields name it in its header, by contract name.
214
+ *
215
+ * `generated[i]` is the Payload field for `contract[i]`, as `arrange` takes
216
+ * it — so an omitted field, or a text field an override turned into a select,
217
+ * is judged by what it became rather than by what the contract says.
218
+ */
219
+ export function summaryFields(contract, generated, limit = SUMMARY_FIELDS) {
220
+ const paired = new Set(findPairs(contract, generated).flatMap(pair => [
221
+ pair.label,
222
+ pair.link,
223
+ ...(pair.icon === undefined ? [] : [pair.icon]),
224
+ ]));
225
+ return contract
226
+ .filter((field, i) => {
227
+ const built = generated[i];
228
+ return (!paired.has(i) &&
229
+ !field.advanced &&
230
+ (built?.type === 'text' || built?.type === 'textarea'));
231
+ })
232
+ .slice(0, limit)
233
+ .map(field => field.name);
234
+ }
235
+ /** What a block's heading is called when it has one. */
236
+ const NAME_FIELDS = new Set(['heading', 'title']);
237
+ /**
238
+ * Which one field names a block in its collapsed header, by contract name.
239
+ *
240
+ * One rather than `summaryFields`' two: a block's header already carries its
241
+ * number and the pill naming its type, and a heading is the name a reader
242
+ * would give it. So the block's `heading` or `title` when it has one — an
243
+ * eyebrow comes first in a hero's contract and says less — and otherwise its
244
+ * first short text field, which for a statement or a testimonial is the text
245
+ * itself. `[]` for a block with no text of its own.
246
+ */
247
+ export function blockSummaryFields(contract, generated) {
248
+ const eligible = summaryFields(contract, generated, contract.length);
249
+ const named = eligible.find(name => NAME_FIELDS.has(name));
250
+ return named ? [named] : eligible.slice(0, 1);
251
+ }
252
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/generate/layout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { ManifestLike } from './blocks.js';
2
+ /**
3
+ * Every resolvable set of allowed values, keyed `Component.field.path`, plus
4
+ * the named unions under `#TypeName`.
5
+ */
6
+ export type SelectOptionSnapshot = Record<string, readonly string[]>;
7
+ /**
8
+ * Reads the design system's types once, at build time, and writes down what it
9
+ * found.
10
+ *
11
+ * Resolution needs a TypeScript `Program` over the whole declaration tree —
12
+ * seconds of work and a dependency on `typescript` being installed. Doing that
13
+ * when the Payload config loads would put it on every serverless cold start,
14
+ * for an answer that cannot change between deploys. So it happens in a script,
15
+ * the result is committed, and the runtime reads a small JSON object.
16
+ *
17
+ * Committing it also makes the values reviewable: rename a member of
18
+ * `CardVariant` and the diff says so, in the same pull request as the rename.
19
+ */
20
+ export declare function collectSelectOptions(options: {
21
+ manifest: ManifestLike;
22
+ typesEntry: string;
23
+ /** Named unions to include regardless of any field referencing them. */
24
+ namedTypes?: string[];
25
+ }): SelectOptionSnapshot;
26
+ //# sourceMappingURL=selectOptionSnapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectOptionSnapshot.d.ts","sourceRoot":"","sources":["../../src/generate/selectOptionSnapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI/C;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAA;AAEpE;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,QAAQ,EAAE,YAAY,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB,GAAG,oBAAoB,CAiBvB"}
@@ -0,0 +1,47 @@
1
+ import { SelectOptionResolver } from './selectOptions.js';
2
+ /**
3
+ * Reads the design system's types once, at build time, and writes down what it
4
+ * found.
5
+ *
6
+ * Resolution needs a TypeScript `Program` over the whole declaration tree —
7
+ * seconds of work and a dependency on `typescript` being installed. Doing that
8
+ * when the Payload config loads would put it on every serverless cold start,
9
+ * for an answer that cannot change between deploys. So it happens in a script,
10
+ * the result is committed, and the runtime reads a small JSON object.
11
+ *
12
+ * Committing it also makes the values reviewable: rename a member of
13
+ * `CardVariant` and the diff says so, in the same pull request as the rename.
14
+ */
15
+ export function collectSelectOptions(options) {
16
+ const resolver = new SelectOptionResolver(options.typesEntry);
17
+ const snapshot = {};
18
+ for (const typeName of options.namedTypes ?? []) {
19
+ const values = resolver.resolveNamed(typeName);
20
+ if (values)
21
+ snapshot[`#${typeName}`] = values;
22
+ }
23
+ for (const component of Object.values(options.manifest.components)) {
24
+ walk(component.content.fields, '', path => {
25
+ const values = resolver.resolve(component.name, path);
26
+ if (values)
27
+ snapshot[`${component.name}.${path}`] = values;
28
+ });
29
+ }
30
+ return Object.fromEntries(Object.entries(snapshot).sort(([a], [b]) => a.localeCompare(b)));
31
+ }
32
+ /**
33
+ * Visits every field path, not only the `select` ones.
34
+ *
35
+ * A `text` field whose prop is a literal union is a select the contract could
36
+ * not express — `IntroSection.actions[].icon` is the case — so the snapshot has
37
+ * to carry those too or the generator cannot find them.
38
+ */
39
+ function walk(fields, prefix, visit) {
40
+ for (const field of fields) {
41
+ const path = prefix ? `${prefix}.${field.name}` : field.name;
42
+ visit(path);
43
+ if (field.of)
44
+ walk(field.of, path, visit);
45
+ }
46
+ }
47
+ //# sourceMappingURL=selectOptionSnapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectOptionSnapshot.js","sourceRoot":"","sources":["../../src/generate/selectOptionSnapshot.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAQzD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAKpC;IACC,MAAM,QAAQ,GAAG,IAAI,oBAAoB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAC7D,MAAM,QAAQ,GAAyB,EAAE,CAAA;IAEzC,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAA;QAC9C,IAAI,MAAM;YAAE,QAAQ,CAAC,IAAI,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAA;IAC/C,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE;YACxC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;YACrD,IAAI,MAAM;gBAAE,QAAQ,CAAC,GAAG,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM,CAAA;QAC5D,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC5F,CAAC;AAED;;;;;;GAMG;AACH,SAAS,IAAI,CAAC,MAAsB,EAAE,MAAc,EAAE,KAA6B;IACjF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAA;QAC5D,KAAK,CAAC,IAAI,CAAC,CAAA;QACX,IAAI,KAAK,CAAC,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IAC3C,CAAC;AACH,CAAC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Resolves the allowed values of a `select` field from the design system's own
3
+ * types.
4
+ *
5
+ * The manifest does not carry them. `ContentField` has no `options`, and the
6
+ * allowed values live in prose — `constraints: "'primary', 'secondary' or
7
+ * 'on-dark'."` — which is documentation, not data. `content.variants` looks
8
+ * like a substitute and is not: for most components it holds prose labels
9
+ * ("Body only", "Body with rail") or runtime states ("paused", "playing"), so
10
+ * mapping variants to options produces garbage more often than not.
11
+ *
12
+ * The component's own prop type is the one place the values exist as data, and
13
+ * it is the definition the component actually behaves according to. Reading it
14
+ * makes drift impossible in the direction that matters: rename a member of
15
+ * `CardVariant` and the generated block changes with it, which shows up as a
16
+ * diff in review rather than as an option that silently stopped working.
17
+ *
18
+ * The compiler API rather than a regex because the values are reached three
19
+ * different ways — a named alias (`type CardVariant = 'default' | 'panel'`), an
20
+ * inline union on the prop, and through an array element for a nested field
21
+ * like `Credentials.credentials.icon`. The checker resolves all three; a regex
22
+ * resolves the first.
23
+ */
24
+ export declare class SelectOptionResolver {
25
+ private readonly checker;
26
+ private readonly source;
27
+ /**
28
+ * @param typesEntry Path to the design system's declaration entry — the file
29
+ * that re-exports every component's props, e.g.
30
+ * `design-system/dist/src/lib/index.d.ts`.
31
+ */
32
+ constructor(typesEntry: string);
33
+ /**
34
+ * `('Card', 'variant')` → `['default', 'panel']`.
35
+ * `('Credentials', 'credentials.icon')` → the icon keys.
36
+ *
37
+ * Null when the path does not resolve or the leaf is not a union of string
38
+ * literals — the caller decides whether that is an error or a cue to look in
39
+ * the overrides.
40
+ */
41
+ resolve(component: string, path: string): readonly string[] | null;
42
+ /**
43
+ * A named exported type's literal union — `resolveNamed('IconName')` gives
44
+ * every glyph in the global set.
45
+ *
46
+ * For fields whose prop type cannot carry the values: `ReportGrid.ctaIcon` is
47
+ * a `ReactNode` because the component takes a rendered glyph, while the
48
+ * contract, correctly, says an author picks a *name*.
49
+ */
50
+ resolveNamed(typeName: string): readonly string[] | null;
51
+ private propsType;
52
+ private exportedType;
53
+ private propertyType;
54
+ /**
55
+ * `Item[]` → `Item`, so a nested path can keep walking into an array's rows.
56
+ *
57
+ * Matched on the array symbol rather than on a numeric index signature. A
58
+ * union of string literals *has* a numeric index type — `string`, from
59
+ * character access — so indexing it collapses `'default' | 'panel'` to
60
+ * `string` and every select silently resolves to nothing.
61
+ */
62
+ private unwrapArray;
63
+ }
64
+ //# sourceMappingURL=selectOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectOptions.d.ts","sourceRoot":"","sources":["../../src/generate/selectOptions.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA2B;IAElD;;;;OAIG;gBACS,UAAU,EAAE,MAAM;IAY9B;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI;IAalE;;;;;;;OAOG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI;IAKxD,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,YAAY;IAUpB;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;CAKpB"}
@@ -0,0 +1,145 @@
1
+ import ts from 'typescript';
2
+ /**
3
+ * Resolves the allowed values of a `select` field from the design system's own
4
+ * types.
5
+ *
6
+ * The manifest does not carry them. `ContentField` has no `options`, and the
7
+ * allowed values live in prose — `constraints: "'primary', 'secondary' or
8
+ * 'on-dark'."` — which is documentation, not data. `content.variants` looks
9
+ * like a substitute and is not: for most components it holds prose labels
10
+ * ("Body only", "Body with rail") or runtime states ("paused", "playing"), so
11
+ * mapping variants to options produces garbage more often than not.
12
+ *
13
+ * The component's own prop type is the one place the values exist as data, and
14
+ * it is the definition the component actually behaves according to. Reading it
15
+ * makes drift impossible in the direction that matters: rename a member of
16
+ * `CardVariant` and the generated block changes with it, which shows up as a
17
+ * diff in review rather than as an option that silently stopped working.
18
+ *
19
+ * The compiler API rather than a regex because the values are reached three
20
+ * different ways — a named alias (`type CardVariant = 'default' | 'panel'`), an
21
+ * inline union on the prop, and through an array element for a nested field
22
+ * like `Credentials.credentials.icon`. The checker resolves all three; a regex
23
+ * resolves the first.
24
+ */
25
+ export class SelectOptionResolver {
26
+ checker;
27
+ source;
28
+ /**
29
+ * @param typesEntry Path to the design system's declaration entry — the file
30
+ * that re-exports every component's props, e.g.
31
+ * `design-system/dist/src/lib/index.d.ts`.
32
+ */
33
+ constructor(typesEntry) {
34
+ const program = ts.createProgram([typesEntry], {
35
+ target: ts.ScriptTarget.ES2022,
36
+ module: ts.ModuleKind.ESNext,
37
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
38
+ skipLibCheck: true,
39
+ noEmit: true,
40
+ });
41
+ this.checker = program.getTypeChecker();
42
+ this.source = program.getSourceFile(typesEntry);
43
+ }
44
+ /**
45
+ * `('Card', 'variant')` → `['default', 'panel']`.
46
+ * `('Credentials', 'credentials.icon')` → the icon keys.
47
+ *
48
+ * Null when the path does not resolve or the leaf is not a union of string
49
+ * literals — the caller decides whether that is an error or a cue to look in
50
+ * the overrides.
51
+ */
52
+ resolve(component, path) {
53
+ const propsType = this.propsType(component);
54
+ if (!propsType)
55
+ return null;
56
+ let current = propsType;
57
+ for (const segment of path.split('.')) {
58
+ current = this.propertyType(current, segment);
59
+ if (!current)
60
+ return null;
61
+ current = this.unwrapArray(current);
62
+ }
63
+ return literalUnion(current);
64
+ }
65
+ /**
66
+ * A named exported type's literal union — `resolveNamed('IconName')` gives
67
+ * every glyph in the global set.
68
+ *
69
+ * For fields whose prop type cannot carry the values: `ReportGrid.ctaIcon` is
70
+ * a `ReactNode` because the component takes a rendered glyph, while the
71
+ * contract, correctly, says an author picks a *name*.
72
+ */
73
+ resolveNamed(typeName) {
74
+ const type = this.exportedType(typeName);
75
+ return type ? literalUnion(type) : null;
76
+ }
77
+ propsType(component) {
78
+ return this.exportedType(`${component}Props`);
79
+ }
80
+ exportedType(name) {
81
+ if (!this.source)
82
+ return null;
83
+ const moduleSymbol = this.checker.getSymbolAtLocation(this.source);
84
+ if (!moduleSymbol)
85
+ return null;
86
+ const exported = this.checker
87
+ .getExportsOfModule(moduleSymbol)
88
+ .find(symbol => symbol.getName() === name);
89
+ if (!exported)
90
+ return null;
91
+ // Re-exports arrive as alias symbols; the local declaration is behind one
92
+ // hop. A directly-declared export has no alias to follow.
93
+ const symbol = exported.flags & ts.SymbolFlags.Alias ? this.checker.getAliasedSymbol(exported) : exported;
94
+ const declaration = symbol.declarations?.[0];
95
+ return declaration ? this.checker.getDeclaredTypeOfSymbol(symbol) : null;
96
+ }
97
+ propertyType(type, name) {
98
+ const symbol = type.getProperty(name);
99
+ if (!symbol)
100
+ return null;
101
+ const declaration = symbol.declarations?.[0];
102
+ if (!declaration)
103
+ return null;
104
+ // The declared type, not the type at the use site, so an optional prop
105
+ // resolves to its union rather than to `T | undefined` collapsed.
106
+ return this.checker.getTypeOfSymbolAtLocation(symbol, declaration);
107
+ }
108
+ /**
109
+ * `Item[]` → `Item`, so a nested path can keep walking into an array's rows.
110
+ *
111
+ * Matched on the array symbol rather than on a numeric index signature. A
112
+ * union of string literals *has* a numeric index type — `string`, from
113
+ * character access — so indexing it collapses `'default' | 'panel'` to
114
+ * `string` and every select silently resolves to nothing.
115
+ */
116
+ unwrapArray(type) {
117
+ const name = type.getSymbol()?.getName();
118
+ if (name !== 'Array' && name !== 'ReadonlyArray')
119
+ return type;
120
+ return this.checker.getTypeArguments(type)[0] ?? type;
121
+ }
122
+ }
123
+ /**
124
+ * The string literals in a union, or null if it is anything else.
125
+ *
126
+ * `undefined` and `null` members are dropped — a prop being optional says
127
+ * nothing about which values are allowed. A union with a non-literal member
128
+ * (`string`, a component type) returns null rather than a partial list, because
129
+ * a partial list would silently forbid values the component accepts.
130
+ */
131
+ function literalUnion(type) {
132
+ const members = type.isUnion() ? type.types : [type];
133
+ const values = [];
134
+ for (const member of members) {
135
+ if (member.flags & (ts.TypeFlags.Undefined | ts.TypeFlags.Null))
136
+ continue;
137
+ if (member.isStringLiteral()) {
138
+ values.push(member.value);
139
+ continue;
140
+ }
141
+ return null;
142
+ }
143
+ return values.length > 0 ? values : null;
144
+ }
145
+ //# sourceMappingURL=selectOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectOptions.js","sourceRoot":"","sources":["../../src/generate/selectOptions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The hand-authored half of the bridge.
3
+ *
4
+ * A design-system contract describes a *content model* — what an author writes.
5
+ * A React component has a *prop model* — what it renders from. They are the
6
+ * same thing 99 times out of 100, which is what makes generation worth doing,
7
+ * and the exceptions are few enough to enumerate. This is where they are
8
+ * enumerated, once, for both the generator and the renderer: a field renamed
9
+ * here is renamed in the Payload schema *and* in what gets passed to the
10
+ * component, so the two can never disagree.
11
+ */
12
+ /** Where a contract's field type is not the whole story. */
13
+ export interface FieldOverride {
14
+ /**
15
+ * Leave the field out of the CMS entirely.
16
+ *
17
+ * For fields that model runtime state rather than authored content — a
18
+ * form's `status`, say, which is the host's business and changes several
19
+ * times a minute. An author cannot usefully set it and a stored value would
20
+ * be wrong the moment it was read.
21
+ */
22
+ omit?: true;
23
+ /**
24
+ * The prop name to pass this field as, when it differs from the field name.
25
+ * `ArticleBody` is the only case today: its contract calls the body `body`
26
+ * and the component takes it as `children`.
27
+ */
28
+ propName?: string;
29
+ /**
30
+ * Force a treatment the contract's `type` cannot express.
31
+ *
32
+ * `videoUpload` is for a video the site hosts itself rather than embeds. The
33
+ * contract's `video` type means a provider URL — it is `VideoEmbed`'s
34
+ * YouTube/Vimeo/Wistia iframe src — and the field type enum in
35
+ * `@forumone/throughline-design-system/contract` has no eleventh value for "a file
36
+ * in the media library". So the contract says `video`, and this says which
37
+ * of the two kinds of video it meant: an upload field, resolved to the
38
+ * stored file's URL with no `srcSet` beside it, because a video has no
39
+ * candidate widths to choose between.
40
+ */
41
+ as?: 'icon' | 'url' | 'videoUpload';
42
+ /**
43
+ * Explicit `select` options. The manifest carries none — allowed values live
44
+ * in prose `constraints` — so they come from the component's own literal
45
+ * union type where one exists, and from here where it does not.
46
+ */
47
+ options?: readonly string[];
48
+ }
49
+ export interface ComponentOverride {
50
+ /**
51
+ * Keep the component out of the block palette.
52
+ *
53
+ * For components that are real parts of a page but not authorable as blocks:
54
+ * site chrome a template renders (`Header`, `Footer`, `Skiplink`), and
55
+ * anything whose props are supplied by the document rather than by a block
56
+ * author.
57
+ */
58
+ notABlock?: true;
59
+ /**
60
+ * The block's name in the picker, when splitting the component name reads
61
+ * wrong: `AtAGlance` humanizes to "At AGlance". The slug is unchanged — it
62
+ * is the manifest name, byte for byte, whatever the label says.
63
+ */
64
+ label?: string;
65
+ /** Keyed by field path — `heading`, `items.title`, `image.src`. */
66
+ fields?: Record<string, FieldOverride>;
67
+ }
68
+ export type Overrides = Record<string, ComponentOverride>;
69
+ /** Look an override up by component and dotted field path. */
70
+ export declare function fieldOverride(overrides: Overrides, component: string, path: string): FieldOverride | undefined;
71
+ //# sourceMappingURL=overrides.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../src/overrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,IAAI,CAAA;IACX;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;OAWG;IACH,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,aAAa,CAAA;IACnC;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,IAAI,CAAA;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CACvC;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;AAEzD,8DAA8D;AAC9D,wBAAgB,aAAa,CAC3B,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX,aAAa,GAAG,SAAS,CAE3B"}