@prestack/thread-ui 0.1.10 → 0.1.12

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 (209) hide show
  1. package/CHANGELOG.md +375 -0
  2. package/dist/ui/attach.js +6 -7
  3. package/dist/ui/compositions.d.ts +0 -8
  4. package/dist/ui/compositions.js +3 -166
  5. package/dist/ui/generators/routes.js +1 -0
  6. package/package.json +2 -2
  7. package/dist/ui/animations/anm_appear.ts +0 -13
  8. package/dist/ui/animations/anm_blur_in.ts +0 -19
  9. package/dist/ui/animations/anm_blur_out.ts +0 -18
  10. package/dist/ui/animations/anm_dismiss.ts +0 -18
  11. package/dist/ui/animations/anm_drop.ts +0 -19
  12. package/dist/ui/animations/anm_fall.ts +0 -18
  13. package/dist/ui/animations/anm_flash.ts +0 -18
  14. package/dist/ui/animations/anm_glow.ts +0 -16
  15. package/dist/ui/animations/anm_lift.ts +0 -16
  16. package/dist/ui/animations/anm_nudge.ts +0 -16
  17. package/dist/ui/animations/anm_pop_in.ts +0 -23
  18. package/dist/ui/animations/anm_pop_out.ts +0 -22
  19. package/dist/ui/animations/anm_press.ts +0 -12
  20. package/dist/ui/animations/anm_pulse.ts +0 -13
  21. package/dist/ui/animations/anm_rise.ts +0 -19
  22. package/dist/ui/animations/anm_shake.ts +0 -13
  23. package/dist/ui/animations/anm_slide_in_left.ts +0 -19
  24. package/dist/ui/animations/anm_slide_in_right.ts +0 -19
  25. package/dist/ui/animations/anm_slide_out_left.ts +0 -18
  26. package/dist/ui/animations/anm_slide_out_right.ts +0 -18
  27. package/dist/ui/animations/anm_spin.ts +0 -12
  28. package/dist/ui/animations/anm_swell.ts +0 -16
  29. package/dist/ui/animations/anm_wobble.ts +0 -24
  30. package/dist/ui/animations/anm_zoom_in.ts +0 -19
  31. package/dist/ui/animations/anm_zoom_out.ts +0 -18
  32. package/dist/ui/animations.ts +0 -308
  33. package/dist/ui/attach.ts +0 -553
  34. package/dist/ui/charts.tsx +0 -69
  35. package/dist/ui/checked.ts +0 -32
  36. package/dist/ui/collector.ts +0 -140
  37. package/dist/ui/compositions.ts +0 -613
  38. package/dist/ui/consent.ts +0 -55
  39. package/dist/ui/content.tsx +0 -27
  40. package/dist/ui/contrast.ts +0 -27
  41. package/dist/ui/controls.tsx +0 -25
  42. package/dist/ui/disclosure.tsx +0 -20
  43. package/dist/ui/dismiss-layer.ts +0 -87
  44. package/dist/ui/door.ts +0 -140
  45. package/dist/ui/drag.ts +0 -316
  46. package/dist/ui/elements/accordion.tsx +0 -47
  47. package/dist/ui/elements/accordion.works.ts +0 -66
  48. package/dist/ui/elements/aspect-ratio.tsx +0 -31
  49. package/dist/ui/elements/aspect-ratio.works.ts +0 -5
  50. package/dist/ui/elements/avatar.tsx +0 -34
  51. package/dist/ui/elements/avatar.works.ts +0 -16
  52. package/dist/ui/elements/badge.tsx +0 -33
  53. package/dist/ui/elements/badge.works.ts +0 -9
  54. package/dist/ui/elements/bars.tsx +0 -50
  55. package/dist/ui/elements/bars.works.ts +0 -29
  56. package/dist/ui/elements/box.tsx +0 -36
  57. package/dist/ui/elements/breadcrumb.tsx +0 -38
  58. package/dist/ui/elements/breadcrumb.works.ts +0 -24
  59. package/dist/ui/elements/button.tsx +0 -46
  60. package/dist/ui/elements/button.works.tsx +0 -52
  61. package/dist/ui/elements/column.tsx +0 -29
  62. package/dist/ui/elements/consent.tsx +0 -71
  63. package/dist/ui/elements/consent.works.tsx +0 -132
  64. package/dist/ui/elements/donut.tsx +0 -45
  65. package/dist/ui/elements/donut.works.ts +0 -42
  66. package/dist/ui/elements/each.tsx +0 -34
  67. package/dist/ui/elements/each.works.tsx +0 -82
  68. package/dist/ui/elements/empty.tsx +0 -33
  69. package/dist/ui/elements/field.tsx +0 -45
  70. package/dist/ui/elements/field.works.ts +0 -65
  71. package/dist/ui/elements/funnel.tsx +0 -50
  72. package/dist/ui/elements/funnel.works.ts +0 -37
  73. package/dist/ui/elements/gauge.tsx +0 -40
  74. package/dist/ui/elements/gauge.works.ts +0 -42
  75. package/dist/ui/elements/glyph.tsx +0 -30
  76. package/dist/ui/elements/glyph.works.tsx +0 -42
  77. package/dist/ui/elements/grid.tsx +0 -29
  78. package/dist/ui/elements/heatmap.tsx +0 -48
  79. package/dist/ui/elements/heatmap.works.ts +0 -24
  80. package/dist/ui/elements/hit-box.tsx +0 -47
  81. package/dist/ui/elements/hit-box.works.tsx +0 -576
  82. package/dist/ui/elements/image.tsx +0 -29
  83. package/dist/ui/elements/line-chart.tsx +0 -45
  84. package/dist/ui/elements/line-chart.works.ts +0 -41
  85. package/dist/ui/elements/link.tsx +0 -38
  86. package/dist/ui/elements/link.works.ts +0 -25
  87. package/dist/ui/elements/media.tsx +0 -57
  88. package/dist/ui/elements/menu.tsx +0 -51
  89. package/dist/ui/elements/menu.works.tsx +0 -154
  90. package/dist/ui/elements/money.tsx +0 -36
  91. package/dist/ui/elements/money.works.ts +0 -9
  92. package/dist/ui/elements/pagination.tsx +0 -39
  93. package/dist/ui/elements/pagination.works.ts +0 -20
  94. package/dist/ui/elements/progress.tsx +0 -34
  95. package/dist/ui/elements/progress.works.ts +0 -8
  96. package/dist/ui/elements/radio-group.tsx +0 -50
  97. package/dist/ui/elements/radio-group.works.ts +0 -33
  98. package/dist/ui/elements/row.tsx +0 -29
  99. package/dist/ui/elements/scatter.tsx +0 -42
  100. package/dist/ui/elements/scatter.works.ts +0 -38
  101. package/dist/ui/elements/select.tsx +0 -43
  102. package/dist/ui/elements/select.works.ts +0 -5
  103. package/dist/ui/elements/separator.tsx +0 -31
  104. package/dist/ui/elements/skeleton.tsx +0 -31
  105. package/dist/ui/elements/slider.tsx +0 -34
  106. package/dist/ui/elements/slider.works.ts +0 -5
  107. package/dist/ui/elements/sparkline.tsx +0 -37
  108. package/dist/ui/elements/sparkline.works.ts +0 -32
  109. package/dist/ui/elements/spinner.tsx +0 -33
  110. package/dist/ui/elements/stack.tsx +0 -29
  111. package/dist/ui/elements/stat.tsx +0 -36
  112. package/dist/ui/elements/stat.works.ts +0 -14
  113. package/dist/ui/elements/surface.tsx +0 -36
  114. package/dist/ui/elements/surface.works.ts +0 -9
  115. package/dist/ui/elements/table.tsx +0 -62
  116. package/dist/ui/elements/table.works.ts +0 -30
  117. package/dist/ui/elements/tabs.tsx +0 -47
  118. package/dist/ui/elements/tabs.works.ts +0 -84
  119. package/dist/ui/elements/text.tsx +0 -33
  120. package/dist/ui/elements/textarea.tsx +0 -38
  121. package/dist/ui/elements/textarea.works.ts +0 -5
  122. package/dist/ui/elements/time.tsx +0 -40
  123. package/dist/ui/elements/time.works.ts +0 -29
  124. package/dist/ui/elements/toggle.tsx +0 -34
  125. package/dist/ui/elements/toggle.works.ts +0 -5
  126. package/dist/ui/elements/tooltip.tsx +0 -40
  127. package/dist/ui/elements/tooltip.works.ts +0 -77
  128. package/dist/ui/elements/value.tsx +0 -38
  129. package/dist/ui/elements/value.works.ts +0 -9
  130. package/dist/ui/elements.ts +0 -127
  131. package/dist/ui/focus-scope.ts +0 -121
  132. package/dist/ui/fonts.ts +0 -355
  133. package/dist/ui/generators/dev.d.ts +0 -1
  134. package/dist/ui/generators/dev.js +0 -62
  135. package/dist/ui/generators/dev.ts +0 -75
  136. package/dist/ui/generators/elements.ts +0 -796
  137. package/dist/ui/generators/i18n.ts +0 -283
  138. package/dist/ui/generators/icons.ts +0 -207
  139. package/dist/ui/generators/react.ts +0 -267
  140. package/dist/ui/generators/routes.ts +0 -847
  141. package/dist/ui/generators/show.ts +0 -261
  142. package/dist/ui/generators/styles.ts +0 -677
  143. package/dist/ui/generators/variants.ts +0 -196
  144. package/dist/ui/i18n/catalogs/en.ts +0 -191
  145. package/dist/ui/i18n/format.ts +0 -221
  146. package/dist/ui/i18n/locales.ts +0 -33
  147. package/dist/ui/i18n/parse.ts +0 -315
  148. package/dist/ui/i18n/translator.ts +0 -31
  149. package/dist/ui/i18n/types.ts +0 -96
  150. package/dist/ui/icons.ts +0 -331
  151. package/dist/ui/islands/map.ts +0 -58
  152. package/dist/ui/localizations.ts +0 -75
  153. package/dist/ui/material.ts +0 -236
  154. package/dist/ui/materials/mat_bordered.ts +0 -13
  155. package/dist/ui/materials/mat_ceramic.ts +0 -13
  156. package/dist/ui/materials/mat_clay.ts +0 -13
  157. package/dist/ui/materials/mat_elevated.ts +0 -24
  158. package/dist/ui/materials/mat_flat.ts +0 -34
  159. package/dist/ui/materials/mat_glass.ts +0 -20
  160. package/dist/ui/materials/mat_lit.ts +0 -13
  161. package/dist/ui/materials/mat_moulded.ts +0 -11
  162. package/dist/ui/materials.ts +0 -197
  163. package/dist/ui/modes.ts +0 -44
  164. package/dist/ui/navigation.tsx +0 -29
  165. package/dist/ui/positioning.ts +0 -90
  166. package/dist/ui/presented.tsx +0 -119
  167. package/dist/ui/provider.tsx +0 -184
  168. package/dist/ui/render-loop.ts +0 -133
  169. package/dist/ui/roving-focus.ts +0 -161
  170. package/dist/ui/screens.ts +0 -141
  171. package/dist/ui/scroll-lock.ts +0 -28
  172. package/dist/ui/series.ts +0 -97
  173. package/dist/ui/signals.ts +0 -386
  174. package/dist/ui/styleguide.ts +0 -231
  175. package/dist/ui/styleguides.ts +0 -374
  176. package/dist/ui/themes.ts +0 -22
  177. package/dist/ui/token.ts +0 -78
  178. package/dist/ui/tokens/shadow.ts +0 -25
  179. package/dist/ui/tokens.ts +0 -234
  180. package/dist/ui/validators.ts +0 -232
  181. package/dist/ui/variant.ts +0 -349
  182. package/dist/ui/variants/var_avatar-round.ts +0 -14
  183. package/dist/ui/variants/var_bubble-me.ts +0 -14
  184. package/dist/ui/variants/var_bubble-them.ts +0 -13
  185. package/dist/ui/variants/var_button-border.ts +0 -76
  186. package/dist/ui/variants/var_button-cta.ts +0 -76
  187. package/dist/ui/variants/var_button-destructive-ghost.ts +0 -72
  188. package/dist/ui/variants/var_button-destructive.ts +0 -76
  189. package/dist/ui/variants/var_button-fill.ts +0 -76
  190. package/dist/ui/variants/var_button-ghost.ts +0 -72
  191. package/dist/ui/variants/var_button-glass.ts +0 -55
  192. package/dist/ui/variants/var_button-normal.ts +0 -76
  193. package/dist/ui/variants/var_button-quiet.ts +0 -74
  194. package/dist/ui/variants/var_card.ts +0 -17
  195. package/dist/ui/variants/var_chip.ts +0 -13
  196. package/dist/ui/variants/var_choice.ts +0 -15
  197. package/dist/ui/variants/var_framed.ts +0 -10
  198. package/dist/ui/variants/var_glyph-plain.ts +0 -11
  199. package/dist/ui/variants/var_input.ts +0 -43
  200. package/dist/ui/variants/var_link-inline.ts +0 -29
  201. package/dist/ui/variants/var_list-row.ts +0 -34
  202. package/dist/ui/variants/var_rule.ts +0 -10
  203. package/dist/ui/variants/var_skeleton-block.ts +0 -12
  204. package/dist/ui/variants/var_spinner-turning.ts +0 -12
  205. package/dist/ui/variants/var_stat-tile.ts +0 -13
  206. package/dist/ui/variants/var_tab-bar.ts +0 -15
  207. package/dist/ui/variants/var_tap-target.ts +0 -25
  208. package/dist/ui/variants.ts +0 -64
  209. package/dist/ui/wrappers.tsx +0 -429
@@ -1,796 +0,0 @@
1
- import { existsSync, readFileSync, writeFileSync } from '@prestack/thread/integrations/disk'
2
- import { join, resolve } from 'node:path'
3
- import { argv, stdout } from 'node:process'
4
- import { fileURLToPath, pathToFileURL } from 'node:url'
5
-
6
- import { exit, invoked } from '@prestack/thread/core/invoked'
7
- import { findTree, inTree } from '@prestack/thread/core/tree'
8
-
9
- import { camel, classOf, classOfElement, looking } from '../variant.ts'
10
-
11
- const idOf = (name: string): string => name.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
12
-
13
- export function pascal(id: string): string {
14
- return id
15
- .split('-')
16
- .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
17
- .join('')
18
- }
19
-
20
- const TAG = /<([A-Z][A-Za-z0-9]*)/g
21
-
22
- export const OWNED_AT = 'ui/'
23
-
24
- export function owned(
25
- body: string,
26
- rows: readonly { readonly id: string; readonly at?: string }[],
27
- ): readonly string[] {
28
- const named = new Map<string, string>()
29
- for (const row of rows) {
30
- const at = row.at
31
- if (typeof at !== 'string' || !at.startsWith(OWNED_AT)) continue
32
- named.set(pascal(row.id), at)
33
- }
34
- const found = new Set<string>()
35
- for (const [, tag] of body.matchAll(TAG)) if (tag !== undefined && named.has(tag)) found.add(tag)
36
- return [...found].sort().map((one) => `import { ${one} } from '../${named.get(one) ?? ''}'`)
37
- }
38
-
39
- export function translated(
40
- name: string,
41
- tag: string | Readonly<Record<string, string>>,
42
- arranges: boolean,
43
- raises: readonly string[],
44
- from: { readonly wrappers: string; readonly content: string; readonly rows: string; readonly validators: string; readonly fonts: string; readonly map: string; readonly parts: string; readonly consent: string; readonly drag: string; readonly checked: string },
45
- carries: Readonly<Record<string, string>> = {},
46
- always: readonly string[] = [],
47
- places: Readonly<Record<string, Readonly<Record<string, string>>>> = {},
48
- works?: string,
49
- measures: readonly string[] = [],
50
- reads?: string,
51
- gives: readonly string[] = [],
52
- keeps = false,
53
- animates = false,
54
- says?: string,
55
- called?: string,
56
- wires?: string,
57
- dresses?: string,
58
- ): string {
59
- const made = pascal(name)
60
-
61
- const dressing = wears.get(name)
62
- if (dressing === undefined) {
63
- throw new Error(
64
- `\`${name}\` is written before the variant rows that wear it were read, so it would ship wearing none of them`,
65
- )
66
- }
67
-
68
- const invoking = [
69
- ...(carries['dispatch'] === undefined ? [' readonly dispatch?: keyof Dispatching'] : []),
70
- ...(carries['input'] === undefined ? [' readonly input?: Readonly<Record<string, unknown>>'] : []),
71
- " readonly invokeOn?: 'press' | 'shown'",
72
- ]
73
-
74
- const stated = Object.entries(carries)
75
-
76
- const names = stated.find(([, kind]) => NAMES(kind))
77
- const source = stated.find(([, kind]) => SOURCES(kind))
78
-
79
- const silent = reads !== undefined && READS[reads]?.hidden === true
80
-
81
- const bare = typeof tag === 'string' && tag === ''
82
-
83
- const worded = silent || bare || called !== undefined || Object.keys(places).length > 0 || (works !== undefined && !arranges) || stated.some(([, kind]) => NAMES(kind) || KINDS[kind]?.from === 'content')
84
-
85
- const chosen = typeof tag === 'string' ? undefined : tag
86
- const first = typeof tag === 'string' ? tag : (Object.entries(tag).find(([key]) => key !== 'by')?.[1] ?? 'div')
87
-
88
- const titled = called === undefined ? '' : ` aria-label={surface.text('${called}')}`
89
-
90
- const naming = (one: string): string =>
91
- names === undefined || SPEAKS.has(one)
92
- ? ''
93
- : KINDS[names[1]]?.needed === false
94
- ? ` {...(props.${names[0]} === undefined ? {} : { 'aria-label': surface.text(props.${names[0]}) })}`
95
- : one === 'img'
96
- ? ` alt={surface.text(props.${names[0]})}`
97
- : ` aria-label={surface.text(props.${names[0]})}`
98
- const sourced = (one: string): string =>
99
- source === undefined ? '' : ` ${SOURCE[one] ?? 'src'}={props.${source[0]}}`
100
-
101
- const props = [
102
- ...(arranges
103
- ? [' readonly children: ReactNode']
104
- : worded
105
- ? []
106
- : [' readonly labelKey: MessageKey']),
107
-
108
- ...stated.map(([id, kind]) => {
109
- const held = KINDS[kind]
110
-
111
- const picks = chosen !== undefined && chosen['by'] === id
112
- const must = held?.needed === true || measures.includes(id) || picks
113
- const is = picks
114
- ? Object.keys(chosen).filter((key) => key !== 'by').map((key) => `'${key}'`).join(' | ')
115
- : (held?.is ?? 'never')
116
- return ` readonly ${id}${must ? '' : '?'}: ${is}`
117
- }),
118
- ...raises.map((one) => ` readonly ${one}?: boolean`),
119
- ]
120
-
121
- const named = [
122
- ...new Set(
123
- Object.values(places)
124
- .map((made) => (made as Record<string, string> | undefined)?.['tag'])
125
- .filter((one): one is string => one !== undefined && /^[A-Z]/.test(one))
126
- .map((one) => one.split('.')[0] ?? ''),
127
- ),
128
- ]
129
-
130
- const worn = named.filter((one) => declared.has(idOf(one)))
131
- const built = named.filter((one) => !declared.has(idOf(one)))
132
-
133
- const nested = new Set<string>()
134
- for (const made of Object.values(places)) {
135
- const held = (made as Record<string, string> | undefined)?.['holds']
136
- if (typeof held === 'string') for (const one of held.split(/\s+/).filter(Boolean)) nested.add(one)
137
- }
138
-
139
- const region = (place: string, depth: number, rows: readonly string[] = [], looped = false): string => {
140
- const made = (places[place] ?? {}) as Record<string, string>
141
- const row = rows[rows.length - 1]
142
- const gave = rows.length === 0 ? '' : `, ${rows.join(', ')}`
143
- const pad = ' ' + ' '.repeat(depth)
144
- const as = made['tag'] ?? 'span'
145
- const meant = made['reads'] === undefined ? undefined : READS[made['reads']]
146
- const from = made['shows']
147
-
148
- const kind = from === undefined ? undefined : carries[from]
149
- const shape = kind === undefined ? undefined : KINDS[kind]
150
-
151
- const points = shape?.from === 'source' && from !== undefined
152
-
153
- const pointed = points ? ` src={props.${String(from)}}${as === 'img' ? ' alt=""' : ''}` : ''
154
-
155
- const shown =
156
- points
157
- ? ''
158
- : from === undefined
159
- ? ''
160
- : row !== undefined
161
- ? shape === undefined
162
- ? `{shown(props${gave}, surface${keeps ? ', kept' : ''}).${from}}`
163
- : `{surface.text(String(${row}.${from}))}`
164
- : shape === undefined
165
- ? `{shown(props, surface${keeps ? ', kept' : ''}${wires === undefined ? '' : ', wired'}).${from}}`
166
- : shape.from === 'content'
167
- ? `{surface.text(props.${from})}`
168
- : `{props.${from}}`
169
-
170
- const facing =
171
- meant?.orients === undefined || carries[meant.orients] === undefined
172
- ? ''
173
- : ` aria-orientation={props.${meant.orients} === 'vertical' ? 'vertical' : 'horizontal'}`
174
-
175
- const taken = (made['takes'] ?? '')
176
- .split(/\s+/)
177
- .filter(Boolean)
178
- .map((one) => GIVES[one] ?? '')
179
- .join('')
180
-
181
- const sized =
182
- made['marks'] === undefined || row === undefined
183
- ? ''
184
- : ` style={marking(${made['marks']}(props${keeps ? ', kept' : ''}${gave}, at))}`
185
-
186
- const handed =
187
- made['gives'] === undefined
188
- ? ''
189
- : ` {...${made['gives']}(props${keeps ? ', kept' : ''}${gave}${row === undefined ? '' : ', at'})}`
190
-
191
- const owns =
192
- row === undefined || IDENTIFIES[as] === undefined || made['by'] === undefined
193
- ? ''
194
- : ` ${String(IDENTIFIES[as])}={String(${row}.${String(made['by'])})}`
195
-
196
- const bound =
197
- owns +
198
- handed +
199
- sized +
200
- pointed +
201
- facing +
202
- taken +
203
- (meant === undefined
204
- ? ''
205
- : (meant.control ?? '') +
206
-
207
- (from === undefined || row !== undefined || meant.state === undefined || carries[from] === undefined
208
- ? ''
209
- : ` ${meant.state}={props.${from}${meant.state === 'checked' ? ' === true' : " ?? ''"}}`) +
210
- (meant.answers ?? ''))
211
-
212
- const held = (made['holds'] ?? '').split(/\s+/).filter(Boolean)
213
- const within = held.map((one) => region(one, depth + 1, rows)).join('\n')
214
-
215
- const body = within === '' ? (meant?.state === undefined ? shown : '') : `\n${within}\n${pad}`
216
-
217
- const dressed = /^[A-Z]/.test(as)
218
- ? ''
219
- : place === dresses
220
- ? ` className={\`e-${name}-${place} \${surface.className ?? ''}\`}`
221
- : looking(made)
222
- ? ` className="e-${name}-${place}"`
223
- : ''
224
-
225
- const written = SHUT.has(as)
226
- ? `<${as}${dressed}${bound} />`
227
- : `<${as}${dressed}${bound}>${body}</${as}>`
228
-
229
- const only = made['when']
230
-
231
- const one =
232
- only !== undefined
233
- ? `${pad}{!${only}(props${keeps ? ', kept' : ''}${gave}) ? null : ${written}}`
234
- : shape !== undefined && !shape.needed && from !== undefined && row === undefined
235
- ? `${pad}{props.${from} === undefined ? null : ${written}}`
236
- : `${pad}${written}`
237
-
238
- const over = made['each']
239
- if (looped || over === undefined) return one
240
-
241
- if (KINDS[carries[over] ?? '']?.is === 'number') {
242
- return (
243
- `${pad}{Array.from({ length: Math.max(1, props.${over} ?? 1) }, (_, at) => (\n` +
244
- `${region(place, depth + 2, rows, true).replace(`className="e-${name}-${place}"`, `key={at} className="e-${name}-${place}"`)}\n` +
245
- `${pad}))}`
246
- )
247
- }
248
-
249
- const by = made['by']
250
- if (by === undefined) {
251
- if (made['keyed'] !== 'position') {
252
- throw new Error(
253
- `\`${name}\` repeats the place \`${place}\` over \`${String(over)}\` and names no field to identify a ` +
254
- 'row by. React matches an unkeyed list by position and keeps the previous row\'s state when it ' +
255
- 'reorders. Name the field with `by`, or state `keyed: \'position\'` where position IS identity.',
256
- )
257
- }
258
- return (
259
- `${pad}{props.${over}.map((${`each${String(rows.length)}`}, at) => (\n` +
260
- `${region(place, depth + 2, [...rows, `each${String(rows.length)}`], true).replace(`<${as}`, `<${as} key={at}`)}\n` +
261
- `${pad}))}`
262
- )
263
- }
264
- return (
265
- `${pad}<Each rows={${carries[over] === undefined ? `${over}(props${keeps ? ', kept' : ''})` : `props.${over}`}} by={${carries[by] === undefined ? `'${by}'` : `props.${by}`}}>\n` +
266
- `${pad} {(${`each${String(rows.length)}`}, at) => (\n${region(place, depth + 2, [...rows, `each${String(rows.length)}`], true)}\n${pad} )}\n` +
267
- `${pad}</Each>`
268
- )
269
- }
270
-
271
- const repeats = Object.values(places).some((made) => {
272
- const over = (made as Record<string, string> | undefined)?.['each']
273
- return over !== undefined && KINDS[carries[over] ?? '']?.is !== 'number' && (made as Record<string, string>)['keyed'] !== 'position'
274
- })
275
- const worked = Object.entries(places).some(([, made]) => {
276
- const from = (made as Record<string, string> | undefined)?.['shows']
277
- return from !== undefined && carries[from] === undefined
278
- })
279
-
280
- const answers = (reads !== undefined && READS[reads]?.answers !== undefined) || Object.values(places).some((made) => {
281
- const said = (made as Record<string, string> | undefined)?.['reads']
282
- return said !== undefined && READS[said]?.answers !== undefined
283
- })
284
-
285
- const inside = Object.keys(places)
286
- .filter((place) => !nested.has(place))
287
- .map((place) => region(place, 0))
288
- .join('\n')
289
-
290
- const roles = stated.find(([, kind]) => KINDS[kind]?.from === 'fonts')
291
- const roled =
292
- roles !== undefined
293
- ? `: roleClass(props.${roles[0]}, className)`
294
- : arranges
295
- ? ''
296
- : ': roleClass(undefined, className)'
297
-
298
- const measured = measures.length === 0 || wires !== undefined ? '' : ' style={surface.measures}'
299
-
300
- const meant = reads === undefined ? undefined : READS[reads]
301
-
302
- const wired =
303
- meant === undefined
304
- ? ''
305
- : (meant.control ?? '') +
306
- (meant.binds === undefined || carries[meant.binds] === undefined
307
- ? ''
308
- : ` ${String(meant.state)}={props.${meant.binds} ?? ${KINDS[carries[meant.binds] ?? '']?.is === 'number' ? '0' : "''"}}`) +
309
- (meant.answers ?? '')
310
-
311
- const gathered = (gives ?? [])
312
- .map((one) => GIVES[one] ?? '')
313
- .join('')
314
-
315
- const wired2 = wires === undefined ? '' : ' {...(wired.on ?? {})}'
316
-
317
- const meaning =
318
- meant === undefined
319
- ? ''
320
- : (meant.role === undefined || (meant.needs !== undefined && carries[meant.needs] === undefined)
321
- ? ''
322
- : meant.needs === undefined || KINDS[carries[meant.needs] ?? '']?.needed === true
323
- ? ` role="${meant.role}"`
324
- : ` {...(props.${meant.needs} === undefined ? {} : { role: '${meant.role}' as const })}`) +
325
- (meant.hidden === true ? ' aria-hidden="true"' : '') +
326
- (meant.orients === undefined || carries[meant.orients] === undefined
327
- ? ''
328
- : ` aria-orientation={props.${meant.orients} === 'vertical' ? 'vertical' : 'horizontal'}`) +
329
- (meant.reports === undefined || !measures.includes(meant.reports)
330
- ? ''
331
- : ` aria-valuenow={Number(measured(props).${meant.reports})} aria-valuemin={0} aria-valuemax={100}`)
332
-
333
- const opening = (one: string): string =>
334
- `<${one}${dresses === undefined ? ' className={surface.className}' : ''} {...surface.marks} {...surface.invokes} {...surface.a11y}${keeps ? ' {...(kept.on ?? {})}' : ''}${meaning}${titled}${announcing}${wired}${wired2}${gathered}${measured}${sourced(one)}${naming(one)}${EXTRA[one] ?? ''}${source === undefined ? '' : (FETCHES[one] ?? '')}`
335
-
336
- const empty = typeof tag === 'string' && SHUT.has(tag)
337
-
338
- const holds = stated.find(([, kind]) => kind === 'holding')
339
-
340
- const telling =
341
- says === undefined
342
- ? undefined
343
- : KINDS[carries[says] ?? '']?.from === 'content'
344
- ? `{surface.text(props.${says}${holds === undefined ? '' : `, props.${holds[0]}`})}`
345
- : `{props.${says}}`
346
-
347
- const announced = stated.find(([, kind]) => kind === 'announcing')
348
- const announcing = announced === undefined ? '' : ` role={props.${announced[0]}}`
349
-
350
- const content =
351
- telling !== undefined
352
- ? telling
353
- : works !== undefined && !arranges && !empty && inside === ''
354
- ? `{shows(props, surface${keeps ? ', kept' : ''})}`
355
- : inside === ''
356
- ? `{${arranges ? 'props.children' : names === undefined ? 'surface.text(props.labelKey)' : 'null'}}`
357
- : arranges
358
- ? `{props.children}\n${inside}`
359
- : undefined
360
-
361
- const spoken = (one: string): string =>
362
- SPEAKS.has(one) && names !== undefined ? `{surface.text(props.${names[0]})}` : (content ?? inside)
363
-
364
- const written = (one: string): string =>
365
- SHUT.has(one)
366
- ? `${opening(one)} />`
367
- : `${opening(one)}>\n${SPEAKS.has(one) || content !== undefined ? ` ${spoken(one)}` : inside}\n </${one}>`
368
-
369
- const shut =
370
- bare && inside !== ''
371
- ? `<>\n${inside}\n </>`
372
- : bare
373
- ? `shows(props, surface${keeps ? ', kept' : ''})`
374
- : chosen === undefined
375
- ? written(first)
376
- : `(() => {\n switch (props.${String(chosen['by'])}) {\n${Object.entries(chosen)
377
- .filter(([key]) => key !== 'by')
378
- .map(([key, one]) => ` case '${key}':\n return (\n ${written(one).split('\n').join('\n ')}\n )`)
379
- .join('\n')}\n default:\n return null\n }\n })()`
380
-
381
- const speaks = wires !== undefined || /\bsurface\b/.test(shut)
382
- const fromProps = measures.filter((one) => carries[one] !== undefined)
383
- const computes = measures.filter((one) => carries[one] === undefined)
384
- const passed = fromProps.map((one) => `${JSON.stringify(one)}: String(props.${one} ?? '')`).join(', ')
385
-
386
- const taken =
387
- measures.length === 0 || wires !== undefined
388
- ? ''
389
- : computes.length === 0
390
- ? `\n measures: { ${passed} },`
391
- : fromProps.length === 0
392
- ? `\n measures: measured(props${keeps ? ', kept' : ''}),`
393
- : `\n measures: { ...measured(props${keeps ? ', kept' : ''}), ${passed} },`
394
-
395
- const wanted = [...(arranges || empty || says !== undefined || inside !== '' ? [] : ['shows']), ...(worked ? ['shown'] : []), ...(computes.length === 0 || wires !== undefined ? [] : ['measured']), ...[...new Set(Object.values(places).flatMap((made) => [(made as Record<string, string> | undefined)?.['marks'], (made as Record<string, string> | undefined)?.['gives'], (made as Record<string, string> | undefined)?.['when'], ((made as Record<string, string> | undefined)?.['each'] !== undefined && carries[(made as Record<string, string>)['each'] ?? ''] === undefined) ? (made as Record<string, string>)['each'] : undefined]).filter((one): one is string => one !== undefined))], ...(answers ? ['does'] : []), ...(keeps ? ['uses'] : []), ...(wires === undefined ? [] : [wires])]
396
-
397
- const held =
398
- always.length === 0 && raises.length === 0 && !keeps
399
- ? ''
400
- : `\n holds: [\n${[
401
- ...always.map((one) => ` '${one}' as const,`),
402
- ...raises.map((one) => ` ...(props.${one} === true ? ['${one}' as const] : []),`),
403
- ...(keeps ? [' ...(kept.holds ?? []),'] : []),
404
- ].join('\n')}\n ],${raises.length === 0 ? '' : `\n raises: [${raises.map((one) => `'${one}' as const`).join(', ')}],`}`
405
-
406
- return `'use client'
407
-
408
- ${[
409
- ...(arranges || stated.some(([, kind]) => KINDS[kind]?.from === 'react' || KINDS[kind]?.from === 'both')
410
- ? ["import type { ReactNode } from 'react'"]
411
- : []),
412
- ...((!arranges && !worded) || stated.some(([, kind]) => KINDS[kind]?.from === 'content')
413
- ? [`import type { MessageKey } from '${from.content}'`]
414
- : []),
415
- ...(stated.some(([, kind]) => KINDS[kind]?.from === 'rows' || KINDS[kind]?.from === 'both') ? [`import type { Row } from '${from.rows}'`] : []),
416
- ...(roles !== undefined
417
- ? [`import { roleClass, type FontRoleId } from '${from.fonts}'`]
418
- : arranges
419
- ? []
420
- : [`import { roleClass } from '${from.fonts}'`]),
421
- ...(stated.some(([, kind]) => KINDS[kind]?.from === 'map') ? [`import type { MapIsland } from '${from.map}'`] : []),
422
- ...(stated.some(([, kind]) => KINDS[kind]?.from === 'parts') ? [`import type { TabItem } from '${from.parts}'`] : []),
423
- ...(stated.some(([, kind]) => KINDS[kind]?.from === 'consent') ? [`import type { ConsentDecision } from '${from.consent}'`] : []),
424
- ...(stated.some(([, kind]) => KINDS[kind]?.from === 'drag') ? [`import type { Subject } from '${from.drag}'`] : []),
425
- ...worn.map((one) => `import { ${one} } from './${idOf(one)}.tsx'`),
426
- ...(built.length === 0 || works === undefined ? [] : [`import { ${built.join(', ')} } from '${works}'`]),
427
- ...(Object.values(places).some((made) => (made as Record<string, string> | undefined)?.['marks'] !== undefined)
428
- ? [`import { measured as marking } from '${from.rows}'`]
429
- : []),
430
- ].join('\n')}
431
-
432
- import type { Dispatching } from '${from.checked}'
433
- import { type Element, element, useSurface } from '${from.wrappers}'
434
- ${Object.values(places).some((made) => ((made as Record<string, string> | undefined)?.['takes'] ?? '').split(/\s+/).includes('validator')) ? `import { limited } from '${from.validators}'\n` : ''}${repeats ? `import { Each } from '../elements/each.tsx'\n` : ''}${works === undefined || wanted.length === 0 ? '' : `import { ${wanted.join(', ')} } from '${works}'\n`}
435
-
436
- export interface ${made}Props {
437
- ${[...props, ...invoking].join('\n')}
438
- }
439
-
440
- export const ${made}: Element<'${made}', ${made}Props> = element(
441
- '${made}',
442
- (props: ${made}Props, className) => {
443
- ${keeps ? ' const kept = uses(props)\n' : ''} ${speaks ? 'const surface = ' : ''}useSurface({ className${roled},\n invokes: { ...(props.dispatch === undefined ? {} : { dispatch: props.dispatch }), ...(props.input === undefined ? {} : { input: props.input }), ...(props.invokeOn === undefined ? {} : { on: props.invokeOn }) },${held}${taken}${animates ? '\n animates: true,' : ''}${keeps ? "\n ...(kept.animates === undefined ? {} : { animates: kept.animates })," : ''} })${wires === undefined ? '' : `\n const wired = ${wires}(props${keeps ? ', kept' : ''}, surface)`}
444
- return (
445
- ${shut}
446
- )
447
- },
448
- '${classOfElement(name)}',
449
- ${JSON.stringify(dressing)},
450
- )
451
- `
452
- }
453
-
454
- const KINDS: Readonly<Record<string, { readonly is: string; readonly needed: boolean; readonly from?: 'react' | 'content' | 'rows' | 'source' | 'fonts' | 'map' | 'parts' | 'consent' | 'both' | 'drag' }>> = {
455
- children: { is: 'ReactNode', needed: true, from: 'react' },
456
- wording: { is: 'MessageKey', needed: true, from: 'content' },
457
- naming: { is: 'MessageKey', needed: true, from: 'content' },
458
- flag: { is: 'boolean', needed: false },
459
- count: { is: 'number', needed: false },
460
- words: { is: 'string', needed: false },
461
-
462
- source: { is: 'string', needed: true },
463
-
464
- amount: { is: 'unknown', needed: false },
465
-
466
- holding: { is: 'Readonly<Record<string, unknown>>', needed: false },
467
-
468
- tuning: { is: 'Readonly<Record<string, number>>', needed: false },
469
-
470
- mapping: { is: 'MapIsland', needed: true, from: 'map' },
471
-
472
- parting: { is: 'readonly TabItem[]', needed: true, from: 'parts' },
473
-
474
- judging: { is: 'ConsentDecision', needed: false, from: 'consent' },
475
- judged: { is: '(said: ConsentDecision) => void', needed: true, from: 'consent' },
476
-
477
- ruling: { is: 'string', needed: true },
478
-
479
- subjecting: { is: 'Subject | readonly Subject[]', needed: false, from: 'drag' },
480
-
481
- announcing: { is: "'alert' | 'status'", needed: false },
482
-
483
- noting: { is: 'MessageKey', needed: false, from: 'content' },
484
-
485
- titling: { is: 'MessageKey', needed: false, from: 'content' },
486
-
487
- picture: { is: 'string', needed: false, from: 'source' },
488
-
489
- styling: { is: 'FontRoleId', needed: false, from: 'fonts' },
490
-
491
- telling: { is: '(said: boolean) => void', needed: false },
492
-
493
- doing: { is: '() => void', needed: false },
494
-
495
- picking: { is: '(said: string) => void', needed: false },
496
-
497
- moving: { is: '(said: number) => void', needed: false },
498
-
499
- choosing: { is: '(said: readonly string[]) => void', needed: false },
500
-
501
- listing: { is: 'readonly Row[]', needed: true, from: 'rows' },
502
-
503
- ordering: { is: 'readonly string[]', needed: true },
504
-
505
- anything: { is: 'readonly object[]', needed: true },
506
-
507
- drawing: { is: '(row: Row, at: number) => ReactNode', needed: true, from: 'both' },
508
- }
509
-
510
- const SOURCES = (kind: string): boolean => kind === 'source'
511
-
512
- const NAMES = (kind: string): boolean => kind === 'naming' || kind === 'titling'
513
-
514
- const SHUT = new Set(['img', 'hr', 'input', 'br', 'area', 'col', 'embed', 'source', 'track', 'wbr'])
515
-
516
- const SOURCE: Readonly<Record<string, string>> = { a: 'href' }
517
-
518
- const EXTRA: Readonly<Record<string, string>> = {
519
- video: ' controls preload="metadata"',
520
- audio: ' controls preload="metadata"',
521
- }
522
-
523
- const FETCHES: Readonly<Record<string, string>> = { a: ' download' }
524
-
525
- const SPEAKS = new Set(['a'])
526
-
527
- const IDENTIFIES: Readonly<Record<string, string>> = { option: 'value' }
528
-
529
- const GIVES: Readonly<Record<string, string>> = {
530
- name: ' name={props.name}',
531
- rows: ' rows={props.rows ?? 4}',
532
- validator: ' {...limited(props.validator)}',
533
- min: ' min={props.min}',
534
- max: ' max={props.max}',
535
- step: ' step={props.step ?? 1}',
536
- }
537
-
538
- interface Reading {
539
- readonly role?: string
540
- readonly reports?: string
541
-
542
- readonly control?: string
543
- readonly state?: string
544
- readonly answers?: string
545
-
546
- readonly hidden?: boolean
547
-
548
- readonly needs?: string
549
-
550
- readonly orients?: string
551
-
552
- readonly binds?: string
553
- }
554
-
555
- const READS: Readonly<Record<string, Reading>> = {
556
-
557
- choice: {
558
- control: ' type="checkbox" disabled={surface.marked(\'disabled\')}',
559
- state: 'checked',
560
-
561
- answers: ' onChange={(event) => does(props).changed(event.target.checked)}',
562
- },
563
-
564
- writing: {
565
- control: " disabled={surface.marked('disabled')}",
566
- state: 'value',
567
- answers: ' onChange={(event) => does(props).wrote(event.target.value)}',
568
- },
569
-
570
- sliding: {
571
- control: " type=\"range\" disabled={surface.marked('disabled')}",
572
- state: 'value',
573
- binds: 'value',
574
- answers: ' onChange={(event) => does(props).moved(Number(event.target.value))}',
575
- },
576
-
577
- choosing: {
578
- control: " type=\"radio\" disabled={surface.marked('disabled')}",
579
- },
580
-
581
- choices: { role: 'radiogroup', needs: 'labelKey' },
582
-
583
- chooser: {
584
- control: " disabled={surface.marked('disabled')}",
585
- state: 'value',
586
- answers: ' onChange={(event) => does(props).chose(event.target.value)}',
587
- },
588
-
589
- progress: { role: 'progressbar', reports: 'fraction' },
590
- picture: { role: 'img' },
591
- divider: { role: 'separator', orients: 'orientation' },
592
-
593
- grouping: { role: 'group', needs: 'labelKey' },
594
-
595
- placeholder: { hidden: true },
596
- notice: { role: 'status' },
597
- alarm: { role: 'alert' },
598
- }
599
-
600
- export const CARRIES: readonly string[] = Object.keys(KINDS)
601
-
602
- export const READINGS: readonly string[] = Object.keys(READS)
603
-
604
- const BESIDE = { wrappers: '../wrappers.tsx', content: '../content.tsx', rows: '../charts.tsx', validators: '../validators.ts', fonts: '../fonts.ts', map: '../islands/map.ts', parts: '../disclosure.tsx', consent: '../consent.ts', drag: '../drag.ts', checked: '../checked.ts' } as const
605
-
606
- interface Row {
607
- readonly id: string
608
- readonly at?: string
609
- readonly by?: string
610
- readonly tag?: string | Readonly<Record<string, string>>
611
- readonly arranges?: boolean
612
- readonly carries?: Readonly<Record<string, string>>
613
- readonly always?: readonly string[]
614
- readonly places?: Readonly<Record<string, Readonly<Record<string, string>>>>
615
- readonly works?: string
616
- readonly measures?: readonly string[]
617
- readonly reads?: string
618
- readonly gives?: readonly string[]
619
- readonly keeps?: boolean
620
- readonly animates?: boolean
621
- readonly shows?: string
622
- readonly called?: string
623
- readonly wires?: string
624
- readonly dresses?: string
625
- readonly raises?: readonly string[]
626
- readonly derives?: readonly { readonly at?: string; readonly by?: string }[]
627
- }
628
-
629
- export const declared = new Set<string>()
630
-
631
- export const wears = new Map<string, Readonly<Record<string, string>>>()
632
-
633
- const SHEET = `elements.${'ts'}`
634
-
635
- const BY = 'thread elements'
636
-
637
- const INSTALLED = 'node_' + 'modules/'
638
-
639
- function derived(row: Row, own: boolean): boolean {
640
- if (typeof row.at !== 'string' || row.at.startsWith(INSTALLED)) return false
641
- if ((row.derives ?? []).some((one) => one.by === BY && one.at === row.at)) return true
642
- return own ? row.by === BY : true
643
- }
644
-
645
- const VARIANTS_SHEET = `variants.${'ts'}`
646
-
647
- async function variants(own: boolean): Promise<readonly { readonly id: string; readonly wears: readonly string[] }[]> {
648
- if (own) {
649
- const held = (await import(pathToFileURL(resolve(fileURLToPath(import.meta.url), '../..', VARIANTS_SHEET)).href)) as {
650
- VARIANTS?: { readonly declares?: readonly { readonly id: string; readonly wears: readonly string[] }[] }
651
- }
652
- return held.VARIANTS?.declares ?? []
653
- }
654
- const at = inTree(findTree(), VARIANTS_SHEET)
655
- if (!existsSync(at)) return []
656
- const held = (await import(pathToFileURL(at).href)) as {
657
- VARIANT?: { readonly declares?: readonly { readonly id: string; readonly wears: readonly string[] }[] }
658
- }
659
- return held.VARIANT?.declares ?? []
660
- }
661
-
662
- async function run(): Promise<number> {
663
- const own = argv.includes('--own')
664
-
665
- const held = own
666
- ? ((await import(pathToFileURL(resolve(fileURLToPath(import.meta.url), '../../elements.ts')).href)) as {
667
- ELEMENTS?: { readonly derives?: readonly Row[]; readonly declares?: readonly Row[] }
668
- })
669
- : undefined
670
-
671
- const tree = own ? undefined : findTree()
672
-
673
- if (tree !== undefined && !existsSync(inTree(tree, SHEET))) {
674
- stdout.write(
675
- `thread: this tree has no ${tree.name}/${SHEET}, so it declares no element and there is nothing to write or check.\n`,
676
- )
677
- return 0
678
- }
679
-
680
- const sheet =
681
- tree === undefined
682
- ? { ELEMENT: held?.ELEMENTS }
683
- : ((await import(pathToFileURL(inTree(tree, SHEET)).href)) as {
684
- ELEMENT?: { readonly derives?: readonly Row[]; readonly declares?: readonly Row[] }
685
- })
686
-
687
- if (sheet.ELEMENT === undefined) {
688
- stdout.write(
689
- own
690
- ? 'elements: thread/ui/elements.ts exports no `ELEMENTS`.\n'
691
- : `thread: ${String(tree?.name)}/${SHEET} exports no \`ELEMENT\`, so nothing declares an element here.\n`,
692
- )
693
- return 1
694
- }
695
-
696
- const root = findTree().at
697
- const from = BESIDE
698
-
699
- const listed = sheet.ELEMENT.derives ?? sheet.ELEMENT.declares ?? []
700
-
701
- for (const one of listed) declared.add(one.id)
702
-
703
- const dressed = await variants(own)
704
- for (const one of listed) {
705
- const held: Record<string, string> = {}
706
- for (const row of dressed) {
707
- if (!row.wears.includes(pascal(one.id))) continue
708
- held[camel(row.id)] = classOf(row.id)
709
- }
710
- wears.set(one.id, held)
711
- }
712
-
713
- const rows = listed.filter((row) => derived(row, own))
714
- const checking = argv.includes('--check')
715
-
716
- for (const row of rows) {
717
- if (row.at === undefined) {
718
- stdout.write(`elements: \`${row.id}\` derives a file and names none, so nothing can be written for it.\n`)
719
- return 1
720
- }
721
- if (row.tag === undefined && row.works !== undefined) {
722
- const made = translated(row.id, '', row.arranges === true, row.raises ?? [], from, row.carries, row.always, row.places, row.works, row.measures, row.reads, row.gives, row.keeps === true, row.animates === true, row.shows, row.called, row.wires, row.dresses)
723
- const at = join(root, row.at)
724
- if (!checking) {
725
- writeFileSync(at, made, 'utf8')
726
- stdout.write(`elements: wrote ${row.at}\n`)
727
- continue
728
- }
729
- if (!existsSync(at) || readFileSync(at, 'utf8') !== made) {
730
- stdout.write(
731
- `elements: ${row.at} is not what the row produces. A derived file that differs from its ` +
732
- 'generator is a hand edit wearing a generated name. Run `thread elements` and commit what it writes.\n',
733
- )
734
- return 1
735
- }
736
- continue
737
- }
738
- if (row.tag === undefined) {
739
- stdout.write(
740
- `elements: \`${row.id}\` is written by \`${BY}\` and states no tag, and a tag is what this ` +
741
- 'writes the element out of. State one on the row, or take the `derives` off and keep the file.\n',
742
- )
743
- return 1
744
- }
745
-
746
- if (row.reads !== undefined && READS[row.reads] === undefined) {
747
- stdout.write(
748
- `elements: \`${row.id}\` says it reads as \`${String(row.reads)}\`, and that is not something an ` +
749
- `element reads as. It reads as one of: ${Object.keys(READS).join(' · ')}.\n`,
750
- )
751
- return 1
752
- }
753
-
754
- const unknown = Object.entries(row.carries ?? {}).find(([, kind]) => KINDS[kind] === undefined)
755
- if (unknown !== undefined) {
756
- stdout.write(
757
- `elements: \`${row.id}\` says it carries \`${unknown[0]}\` as a \`${unknown[1]}\`, and that is ` +
758
- `not a kind an element carries. It carries one of: ${Object.keys(KINDS).join(' · ')}.\n`,
759
- )
760
- return 1
761
- }
762
-
763
- const text = translated(row.id, row.tag, row.arranges === true, row.raises ?? [], from, row.carries, row.always, row.places, row.works, row.measures, row.reads, row.gives, row.keeps === true, row.animates === true, row.shows, row.called, row.wires, row.dresses)
764
- const where = join(root, row.at)
765
-
766
- if (!checking) {
767
- writeFileSync(where, text, 'utf8')
768
- stdout.write(`elements: wrote ${row.at}\n`)
769
- continue
770
- }
771
-
772
- if (!existsSync(where)) {
773
- stdout.write(
774
- `elements: \`${row.id}\` is declared at \`${row.at}\`, and the file is not there. ` +
775
- 'A row names a file and `--check` never writes one — run `thread elements` and commit what it writes.\n',
776
- )
777
- return 1
778
- }
779
- if (readFileSync(where, 'utf8') !== text) {
780
- stdout.write(
781
- `elements: ${row.at} is not what the row produces. A derived file that differs from its ` +
782
- 'generator is a hand edit wearing a generated name. Run `thread elements` and commit what it writes.\n',
783
- )
784
- return 1
785
- }
786
- }
787
-
788
- stdout.write(
789
- checking
790
- ? `thread: ${String(rows.length)} element(s) written from their rows, all agreeing\n`
791
- : `thread: wrote ${String(rows.length)} element(s) from their rows\n`,
792
- )
793
- return 0
794
- }
795
-
796
- if (invoked(import.meta.url)) exit(await run())