@astryxdesign/cli 0.6.3-canary.7ada42e → 0.6.3-canary.7ec7be0

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 (221) hide show
  1. package/README.md +21 -19
  2. package/api/build/_adapter.d.mts +50 -0
  3. package/api/build/_adapter.mjs +60 -0
  4. package/api/build/build.doc.mjs +13 -7
  5. package/api/build/build.test.mjs +184 -6
  6. package/api/build/build.type.d.mts +57 -2
  7. package/api/build/build.type.mjs +30 -7
  8. package/api/build/help/help.d.mts +4 -0
  9. package/api/build/help/help.mjs +24 -11
  10. package/api/build/kit/kit.d.mts +4 -1
  11. package/api/build/kit/kit.mjs +165 -49
  12. package/api/build/kit/rank.d.mts +44 -0
  13. package/api/build/kit/rank.mjs +432 -0
  14. package/api/build/kit/rank.test.mjs +196 -0
  15. package/api/component/_adapter.d.mts +6 -12
  16. package/api/component/_adapter.mjs +20 -10
  17. package/api/component/component.mjs +60 -12
  18. package/api/component/list/list.mjs +3 -2
  19. package/api/docs/_adapter.d.mts +251 -28
  20. package/api/docs/_adapter.mjs +918 -35
  21. package/api/docs/detail/detail.mjs +11 -3
  22. package/api/docs/detail/section/section.mjs +24 -13
  23. package/api/docs/detail/section/section.test.mjs +15 -6
  24. package/api/docs/docs.d.mts +5 -3
  25. package/api/docs/docs.doc.mjs +38 -17
  26. package/api/docs/docs.mjs +44 -8
  27. package/api/docs/docs.test.mjs +158 -4
  28. package/api/docs/docs.type.d.mts +181 -2
  29. package/api/docs/docs.type.mjs +117 -3
  30. package/api/docs/index/index.mjs +11 -3
  31. package/api/docs/index/index.test.mjs +1 -1
  32. package/api/docs/integration-tree.test.mjs +555 -0
  33. package/api/docs/integrationDocs.test.mjs +14 -14
  34. package/api/docs/list/list.mjs +25 -5
  35. package/api/docs/node/node.d.mts +43 -0
  36. package/api/docs/node/node.mjs +192 -0
  37. package/api/docs/reference-blocks.test.mjs +406 -0
  38. package/api/doctor/doctor.d.mts +30 -2
  39. package/api/doctor/doctor.mjs +151 -8
  40. package/api/doctor/doctor.test.mjs +95 -7
  41. package/api/index.d.mts +1 -1
  42. package/api/index.mjs +1 -0
  43. package/api/init/init.doc.mjs +1 -1
  44. package/api/integration/add-contribution.d.mts +2 -1
  45. package/api/integration/add-contribution.mjs +107 -5
  46. package/api/integration/add-contribution.test.mjs +137 -0
  47. package/api/integration/authoring-checks.mjs +138 -28
  48. package/api/integration/authoring-checks.test.mjs +171 -3
  49. package/api/integration/authoring-checks.type.mjs +6 -1
  50. package/api/integration/integration-authoring.type.d.mts +2 -0
  51. package/api/integration/integration-authoring.type.mjs +2 -0
  52. package/api/integration/integrationAdd.doc.mjs +6 -0
  53. package/api/integration/integrationAddDoc.doc.mjs +7 -1
  54. package/api/integration/integrationDocConflicts.doc.mjs +1 -1
  55. package/api/integration/integrationPackCheck.doc.mjs +1 -1
  56. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  57. package/api/integration/integrationTemplateConflicts.doc.mjs +3 -0
  58. package/api/integration/pack-check.mjs +34 -0
  59. package/api/integration/pack-check.test.mjs +121 -0
  60. package/api/integration/pack-check.type.d.mts +26 -2
  61. package/api/integration/pack-check.type.mjs +14 -1
  62. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  63. package/api/integration/validate-integration.test.mjs +3 -3
  64. package/api/integration/validateIntegration.doc.mjs +1 -1
  65. package/api/json/index.ts +2 -0
  66. package/api/layout/_adapter.mjs +20 -5
  67. package/api/search/search.d.mts +59 -1
  68. package/api/search/search.doc.mjs +4 -3
  69. package/api/search/search.mjs +442 -71
  70. package/api/search/search.test.mjs +91 -2
  71. package/api/search/search.type.d.mts +13 -1
  72. package/api/search/search.type.mjs +4 -1
  73. package/api/template/list/list.mjs +1 -0
  74. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  75. package/api/template/template-integration.test.mjs +1072 -3
  76. package/api/template/template.doc.mjs +27 -7
  77. package/api/template/template.mjs +45 -8
  78. package/api/template/template.type.d.mts +6 -8
  79. package/api/template/template.type.mjs +3 -2
  80. package/api/theme/template/template.test.mjs +5 -0
  81. package/api/theme/themeTemplate.doc.mjs +1 -1
  82. package/assets/docs/getting-started.doc.mjs +2 -2
  83. package/assets/docs/layout.doc.dense.mjs +2 -2
  84. package/assets/docs/layout.doc.mjs +1 -1
  85. package/assets/docs/principles.doc.mjs +6 -6
  86. package/assets/docs/styling-libraries.doc.mjs +3 -3
  87. package/assets/docs/styling.doc.mjs +4 -4
  88. package/assets/docs/theme.doc.mjs +4 -4
  89. package/assets/docs/tokens.doc.mjs +1 -1
  90. package/assets/docs/tree/api.doc.mjs +30 -0
  91. package/assets/docs/tree/cli.doc.mjs +23 -0
  92. package/assets/docs/tree/commands.doc.mjs +25 -0
  93. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +37 -9
  94. package/assets/docs/tree/integrations.test.mjs +62 -0
  95. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  96. package/assets/docs/working-with-ai.doc.mjs +3 -3
  97. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  98. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  99. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  100. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  101. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  102. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  103. package/authoring/debug/parse.d.mts +3 -3
  104. package/authoring/doctypes/_schema.d.mts +119 -117
  105. package/authoring/doctypes/_schema.mjs +54 -3
  106. package/authoring/doctypes/base/graph-fields.doc.mjs +7 -5
  107. package/authoring/doctypes/base/type.ts +6 -5
  108. package/authoring/doctypes/command/command.doc.mjs +1 -1
  109. package/authoring/doctypes/command/type.ts +2 -2
  110. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  111. package/authoring/doctypes/enum/type.ts +1 -1
  112. package/authoring/doctypes/function/function.doc.mjs +3 -2
  113. package/authoring/doctypes/function/type.ts +3 -2
  114. package/authoring/doctypes/load-contract.test.mjs +26 -1
  115. package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
  116. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  117. package/authoring/doctypes/namespace/type.ts +5 -2
  118. package/authoring/doctypes/parse.mjs +3 -2
  119. package/authoring/doctypes/reference/reference.doc.mjs +34 -5
  120. package/authoring/doctypes/reference/type.ts +28 -11
  121. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  122. package/authoring/doctypes/schema/type.ts +1 -1
  123. package/authoring/doctypes/template/parse.d.mts +2 -0
  124. package/authoring/doctypes/template/parse.mjs +4 -0
  125. package/authoring/doctypes/template/parse.test.mjs +18 -0
  126. package/authoring/doctypes/template/template.doc.mjs +6 -0
  127. package/authoring/doctypes/template/type.ts +8 -0
  128. package/authoring/identity/identity.doc.mjs +2 -2
  129. package/authoring/index.d.ts +1 -0
  130. package/authoring/integration/type.ts +1 -1
  131. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  132. package/clients/cli/commands/build-theme.mjs +1 -1
  133. package/clients/cli/commands/build.doc.mjs +11 -6
  134. package/clients/cli/commands/build.mjs +105 -67
  135. package/clients/cli/commands/build.text-fields.test.mjs +41 -0
  136. package/clients/cli/commands/component/index.mjs +1 -1
  137. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  138. package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
  139. package/clients/cli/commands/discover.mjs +1 -1
  140. package/clients/cli/commands/docs.doc.mjs +20 -8
  141. package/clients/cli/commands/docs.mjs +183 -69
  142. package/clients/cli/commands/docs.test.mjs +120 -16
  143. package/clients/cli/commands/doctor-integration-docs.doc.mjs +2 -2
  144. package/clients/cli/commands/doctor-integration-templates.doc.mjs +14 -7
  145. package/clients/cli/commands/doctor-integration.test.mjs +75 -1
  146. package/clients/cli/commands/doctor.mjs +53 -16
  147. package/clients/cli/commands/integration-add.doc.mjs +10 -0
  148. package/clients/cli/commands/integration.mjs +1 -0
  149. package/clients/cli/commands/search.mjs +11 -2
  150. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  151. package/clients/cli/commands/template.doc.mjs +23 -6
  152. package/clients/cli/commands/template.mjs +4 -91
  153. package/clients/cli/index.mjs +8 -0
  154. package/clients/cli/lib/manifest.mjs +9 -2
  155. package/foundation/agent-docs/agent-docs.d.mts +3 -2
  156. package/foundation/agent-docs/agent-docs.mjs +13 -9
  157. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  158. package/foundation/config/project.d.mts +12 -11
  159. package/foundation/config/project.mjs +73 -59
  160. package/foundation/config/project.test.mjs +141 -5
  161. package/foundation/discovery/authoring-self-docs.d.mts +12 -0
  162. package/foundation/discovery/authoring-self-docs.mjs +20 -8
  163. package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
  164. package/foundation/discovery/cli-self-docs.d.mts +40 -34
  165. package/foundation/discovery/cli-self-docs.mjs +91 -115
  166. package/foundation/discovery/cli-self-docs.test.mjs +104 -166
  167. package/foundation/discovery/component-discovery.d.mts +38 -0
  168. package/foundation/discovery/component-discovery.mjs +48 -0
  169. package/foundation/discovery/docs-discovery.d.mts +109 -9
  170. package/foundation/discovery/docs-discovery.mjs +223 -24
  171. package/foundation/discovery/docs-discovery.test.mjs +103 -27
  172. package/foundation/discovery/docs-output-budget.d.mts +2 -2
  173. package/foundation/discovery/docs-output-budget.mjs +1 -1
  174. package/foundation/discovery/docs-section-key.d.mts +28 -10
  175. package/foundation/discovery/docs-section-key.mjs +134 -33
  176. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  177. package/foundation/discovery/template-adapter.d.mts +92 -6
  178. package/foundation/discovery/template-adapter.mjs +453 -17
  179. package/foundation/discovery/template-adapter.test.mjs +42 -0
  180. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  181. package/foundation/discovery/template-conflict-release.mjs +40 -0
  182. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  183. package/foundation/doc-compiler/bundle.mjs +61 -1
  184. package/foundation/doc-compiler/bundle.test.mjs +21 -10
  185. package/foundation/doc-compiler/compile.d.mts +113 -132
  186. package/foundation/doc-compiler/compile.mjs +57 -10
  187. package/foundation/doc-compiler/diagnostics.mjs +28 -0
  188. package/foundation/doc-compiler/doc-loads.test.mjs +2 -2
  189. package/foundation/doc-compiler/inputs.d.mts +2 -2
  190. package/foundation/doc-compiler/inputs.mjs +6 -1
  191. package/foundation/doc-compiler/ir.mjs +7 -0
  192. package/foundation/doc-compiler/lenses.d.mts +8 -5
  193. package/foundation/doc-compiler/lenses.mjs +50 -4
  194. package/foundation/doc-compiler/links.d.mts +162 -0
  195. package/foundation/doc-compiler/links.mjs +294 -0
  196. package/foundation/doc-compiler/links.test.mjs +192 -0
  197. package/foundation/doc-compiler/lower-doc.test.mjs +5 -2
  198. package/foundation/doc-compiler/read.d.mts +3 -2
  199. package/foundation/doc-compiler/read.mjs +6 -1
  200. package/foundation/doc-compiler/tree.d.mts +288 -0
  201. package/foundation/doc-compiler/tree.mjs +876 -0
  202. package/foundation/doc-compiler/tree.test.mjs +598 -0
  203. package/foundation/integrations/autolink.test.mjs +1 -1
  204. package/foundation/integrations/cli-requirement.d.mts +45 -0
  205. package/foundation/integrations/cli-requirement.mjs +154 -0
  206. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  207. package/foundation/integrations/contribution-inventory.d.mts +2 -1
  208. package/foundation/integrations/contribution-inventory.mjs +10 -4
  209. package/foundation/integrations/contribution-inventory.test.mjs +19 -0
  210. package/foundation/integrations/integration-warnings.d.mts +9 -2
  211. package/foundation/integrations/integration-warnings.mjs +51 -26
  212. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  213. package/foundation/integrations/validate-contributions.mjs +25 -27
  214. package/foundation/response/response-types.doc.d.mts +5 -1
  215. package/foundation/response/response-types.doc.mjs +19 -10
  216. package/foundation/text/string-utils.d.mts +8 -0
  217. package/foundation/text/string-utils.mjs +18 -0
  218. package/foundation/xle/expand.mjs +1 -1
  219. package/foundation/xle/xle.test.mjs +13 -0
  220. package/package.json +9 -9
  221. package/assets/docs/cli.doc.mjs +0 -15
@@ -4,8 +4,9 @@
4
4
  * @file build command — thin wrapper with a stable result summary.
5
5
  *
6
6
  * astryx build → the PLAYBOOK (how to build a page)
7
- * astryx build "<what>" → a COMPOSITION KIT (closest page template,
8
- * blocks, components) with a recommended START.
7
+ * astryx build "<what>" → the TEMPLATE to start from (always one: the
8
+ * closest page, or the app shell), other
9
+ * templates, then blocks and components.
9
10
  *
10
11
  * All grouping/scoring lives in api/build; this file only parses flags and
11
12
  * renders. Command strings are prefixed for the caller's package manager here
@@ -27,6 +28,7 @@ import {
27
28
  records,
28
29
  } from '../formatters/index.mjs';
29
30
  import {cliError} from '../lib/cli-error.mjs';
31
+ import {firstSentence} from '../../../foundation/text/string-utils.mjs';
30
32
  import {defineCommand} from '../lib/define-command.mjs';
31
33
  import {build as buildApi} from '../../../api/build/build.mjs';
32
34
  import {doc as buildCommand} from './build.doc.mjs';
@@ -126,6 +128,7 @@ export function registerBuild(program) {
126
128
  hasResults,
127
129
  matchCount,
128
130
  directMatch,
131
+ start,
129
132
  pages,
130
133
  blocks,
131
134
  domain,
@@ -134,8 +137,8 @@ export function registerBuild(program) {
134
137
  hint,
135
138
  } = result.data;
136
139
  // The kit spans domains, so its kind comes from the pieces themselves.
137
- // `frame` and `foundation` are always-on and deliberately excluded: they
138
- // are not what the query matched.
140
+ // `start` (which may be the fallback shell), `frame` and `foundation`
141
+ // are deliberately excluded: they are not what the query matched.
139
142
  const answered = resultSetOf([...pages, ...blocks, ...domain], {
140
143
  count: matchCount,
141
144
  empty: !hasResults,
@@ -148,7 +151,7 @@ export function registerBuild(program) {
148
151
  return answered;
149
152
  }
150
153
 
151
- if (!hasResults) {
154
+ if (!hasResults && !start) {
152
155
  emit(
153
156
  text(`No matches for "${q}".`),
154
157
  text(`Try a broader term, or browse: ${run} component --list`),
@@ -156,91 +159,126 @@ export function registerBuild(program) {
156
159
  return answered;
157
160
  }
158
161
 
159
- // Same JSON->text projection as search, but leaner: the section header
160
- // already says the kind, so drop `domain`/`import` by default (they're in
161
- // --json and under --verbose). Keeps each item to name/displayName/desc/cmd.
162
- const fields = options.verbose
163
- ? [
164
- 'name',
165
- 'domain',
166
- 'displayName',
167
- 'score',
168
- 'reason',
169
- 'import',
170
- 'description',
171
- 'command',
172
- ]
173
- : ['name', 'displayName', 'description', 'command'];
162
+ // Four sections, one job each: the TEMPLATE to scaffold, OTHER
163
+ // TEMPLATES if its layout is wrong, BLOCKS for parts it lacks, and
164
+ // COMPONENTS for the rest. Descriptions stop at their first sentence and
165
+ // blocks and components share one command line in their heading; the
166
+ // JSON and --verbose carry everything.
167
+ const verbose = Boolean(options.verbose);
174
168
  /** @type {import('../formatters/index.mjs').RecordOptions} */
175
- const recordOpts = {fields, format: {command: formatCliCommand}};
176
-
177
- // `template <name> <path>` scaffolds into your project; <path> is the file
178
- // (or folder) to write it to — a placeholder, since we can't know your
179
- // layout. `--skeleton` and `component <Name>` just print, so no path.
180
- const startCmd = directMatch
181
- ? `${run} template ${pages[0].name} <path>`
182
- : pages.length
183
- ? `${run} template ${pages[0].name} --skeleton`
184
- : `${run} component AppShell`;
185
- const startNote = directMatch
186
- ? `This \`${pages[0].name}\` page template appears to be the closest to what you want, so we recommend scaffolding it into your project — replace \`<path>\` with the file (or folder) to write it to — then adapting. Otherwise, browse PAGE TEMPLATES first, then BLOCKS and DOMAIN COMPONENTS below.`
187
- : pages.length
188
- ? `No exact match, but \`${pages[0].name}\` is the closest page template — run the above to print its layout as a reference, then compose. Otherwise, browse PAGE TEMPLATES first, then BLOCKS and DOMAIN COMPONENTS below.`
189
- : 'No page template fits — frame with AppShell, then compose from BLOCKS and DOMAIN COMPONENTS below.';
190
-
191
- // A short legend up top: what this output is, how to use it, and the exact
192
- // order of the sections below (only the ones actually present) so it reads
193
- // clearly and parses predictably.
194
- const sectionsOrder = ['RECOMMENDED START'];
195
- if (pages.length) sectionsOrder.push('PAGE TEMPLATES');
196
- if (blocks.length) sectionsOrder.push('BLOCKS');
197
- if (domain.length) sectionsOrder.push('DOMAIN COMPONENTS');
198
- sectionsOrder.push('FRAME + FOUNDATION');
199
- // The legend promises the complete order, so a section emitted after it
200
- // has to be in it.
201
- if (hint) sectionsOrder.push('FEW MATCHES');
169
+ const full = {
170
+ fields: [
171
+ 'name',
172
+ 'domain',
173
+ 'displayName',
174
+ 'score',
175
+ 'reason',
176
+ 'import',
177
+ 'description',
178
+ 'command',
179
+ ],
180
+ format: {command: formatCliCommand},
181
+ };
182
+ /** @param {string[]} fields */
183
+ const brief = fields => ({
184
+ fields,
185
+ format: {command: formatCliCommand, description: firstSentence},
186
+ });
187
+ // Blocks and components are extras: the text names the top few, and
188
+ // says how many more the JSON and --verbose carry.
189
+ const TOP = 3;
190
+ /** @param {unknown[]} items */
191
+ const shown = items => (verbose ? items : items.slice(0, TOP));
192
+ /** @param {unknown[]} items */
193
+ const more = items =>
194
+ !verbose && items.length > TOP
195
+ ? ` (top ${TOP} of ${items.length}; --verbose for all)`
196
+ : '';
202
197
 
203
198
  /** @type {import('../formatters/index.mjs').Block[]} */
204
199
  const out = [
205
200
  section(`Build kit for "${q}"`),
206
201
  text(
207
- 'A recommended set of pieces to assemble this page, in the order to use them. ' +
208
- 'Begin with RECOMMENDED START, then pull from the sections below — each ' +
209
- 'recommended item includes a `command:` to run next.\n' +
210
- `Sections in order: ${sectionsOrder.join(', ')}.`,
202
+ start
203
+ ? 'Start from the TEMPLATE, fill it with BLOCKS, and use COMPONENTS only for what is left.\n' +
204
+ 'Changing a page you already have? Keep it, and use only the blocks and components.'
205
+ : 'This kit is narrowed by --type, so it names no template.',
211
206
  ),
212
- section('RECOMMENDED START', `${startNote}\n${startCmd}`),
213
207
  ];
214
208
 
215
- if (pages.length) {
209
+ if (start) {
216
210
  out.push(
217
211
  section(
218
- 'PAGE TEMPLATES',
219
- directMatch
220
- ? 'Closest full-page templates — scaffold one, then adapt it.'
221
- : 'Closest full-page templates — use as a layout reference.',
212
+ 'TEMPLATE',
213
+ `${start.reason} ${
214
+ start.basis === 'fallback'
215
+ ? 'Scaffold it, then put blocks inside it.'
216
+ : 'Scaffold it, then replace its content. Keep its layout and spacing.'
217
+ }`,
218
+ ),
219
+ record(
220
+ start,
221
+ verbose
222
+ ? {
223
+ fields: ['name', 'displayName', 'description', 'command'],
224
+ format: {command: formatCliCommand},
225
+ }
226
+ : brief(['name', 'description', 'command']),
222
227
  ),
223
- records(pages, recordOpts),
224
228
  );
229
+ if (start.alternatives.length) {
230
+ out.push(
231
+ section(
232
+ 'OTHER TEMPLATES',
233
+ `If the layout is wrong, scaffold one of these instead. All page templates: ${formatCliCommand('template --list --type page')}`,
234
+ ),
235
+ records(
236
+ start.alternatives,
237
+ verbose ? full : brief(['name', 'description', 'command']),
238
+ ),
239
+ );
240
+ }
225
241
  }
226
- if (blocks.length) {
242
+ // Search's own page matches, so the text carries every field the JSON
243
+ // does: one line by default, the full entries under --verbose.
244
+ if (pages.length) {
227
245
  out.push(
228
- section('BLOCKS', 'Drop-in patterns that cover parts of the page.'),
229
- records(blocks, recordOpts),
246
+ verbose
247
+ ? section(
248
+ 'SEARCH MATCHES',
249
+ 'Page templates keyword search matched, best first.',
250
+ )
251
+ : text(
252
+ `Keyword search matched these page templates: ${pages.map(p => p.name).join(', ')}.`,
253
+ ),
230
254
  );
255
+ if (verbose) out.push(records(pages, full));
231
256
  }
232
- if (domain.length) {
257
+ if (blocks.length) {
233
258
  out.push(
234
- section('DOMAIN COMPONENTS', 'Components specific to this idea.'),
235
- records(domain, recordOpts),
259
+ section(
260
+ 'BLOCKS',
261
+ `Ready-made sections for parts the template lacks${more(blocks)}. Print one: ${formatCliCommand('template <name>')}`,
262
+ ),
263
+ records(
264
+ shown(blocks),
265
+ verbose ? full : brief(['name', 'description']),
266
+ ),
236
267
  );
237
268
  }
238
-
239
269
  out.push(
240
270
  section(
241
- 'FRAME + FOUNDATION',
242
- 'Always-available shell + layout/text/action primitives.',
271
+ 'COMPONENTS',
272
+ `For what the template and blocks do not cover${more(domain)}. Read one: ${formatCliCommand('component <name>')}`,
243
273
  ),
274
+ ...(domain.length
275
+ ? [
276
+ records(
277
+ shown(domain),
278
+ verbose ? full : brief(['name', 'description']),
279
+ ),
280
+ ]
281
+ : []),
244
282
  record({frame, foundation}),
245
283
  );
246
284
 
@@ -21,6 +21,8 @@ describe('build kit text fields mirror the JSON keys', () => {
21
21
  const {data} = JSON.parse(json.stdout);
22
22
  const keys = new Set([
23
23
  ...Object.keys(data),
24
+ ...Object.keys(data.start ?? {}),
25
+ ...(data.start?.alternatives ?? []).flatMap(a => Object.keys(a)),
24
26
  ...[...data.pages, ...data.blocks, ...data.domain].flatMap(e => Object.keys(e)),
25
27
  ]);
26
28
 
@@ -35,6 +37,45 @@ describe('build kit text fields mirror the JSON keys', () => {
35
37
  ),
36
38
  ];
37
39
  expect(fields).toContain('frame');
40
+ // The start is printed as a record, so its fields are checked too.
41
+ expect(fields).toContain('command');
42
+ expect(fields).toContain('displayName');
38
43
  for (const field of fields) expect(keys).toContain(field);
39
44
  }, SLOW);
45
+
46
+ it('leads with the start, then the alternatives, blocks and components', async () => {
47
+ const human = await runCli(['build', 'analytics dashboard'], REPO);
48
+ expect(human.status).toBe(0);
49
+ const headings = human.stdout
50
+ .split('\n')
51
+ .filter(line => /^(TEMPLATE|OTHER TEMPLATES|BLOCKS|COMPONENTS)$/.test(line));
52
+ expect(headings).toEqual(['TEMPLATE', 'OTHER TEMPLATES', 'BLOCKS', 'COMPONENTS']);
53
+ // A builder changing a page they already have keeps it.
54
+ expect(human.stdout).toContain(
55
+ 'Changing a page you already have? Keep it, and use only the blocks and components.',
56
+ );
57
+ }, SLOW);
58
+
59
+ it("shortens the start's description by default and prints it whole under --verbose", async () => {
60
+ const json = await runCli(['build', 'quarterly revenue dashboard', '--json'], REPO);
61
+ const {start} = JSON.parse(json.stdout).data;
62
+ const first = start.description.slice(0, start.description.indexOf('. ') + 1);
63
+ expect(first.length).toBeGreaterThan(0);
64
+ expect(first.length).toBeLessThan(start.description.length);
65
+ const line = out => out.split('\n').find(l => l.startsWith('description:'));
66
+ const brief = await runCli(['build', 'quarterly revenue dashboard'], REPO);
67
+ expect(line(brief.stdout)).toBe(`description: ${first}`);
68
+ const whole = await runCli(['build', 'quarterly revenue dashboard', '--verbose'], REPO);
69
+ expect(whole.stdout).toContain(start.description);
70
+ }, SLOW);
71
+
72
+ it("names search's page matches in the default text", async () => {
73
+ const json = await runCli(['build', 'contact form', '--json'], REPO);
74
+ const {data} = JSON.parse(json.stdout);
75
+ expect(data.pages.length).toBeGreaterThan(0);
76
+ const human = await runCli(['build', 'contact form'], REPO);
77
+ expect(human.stdout).toContain(
78
+ `Keyword search matched these page templates: ${data.pages.map(p => p.name).join(', ')}.`,
79
+ );
80
+ }, SLOW);
40
81
  });
@@ -111,7 +111,7 @@ export function registerComponent(program) {
111
111
  // doctor integration validate. Best-effort; suppressed in --json mode.
112
112
  try {
113
113
  const project = await Project.load(process.cwd());
114
- await warnOnIntegrationIssues(project.loadedIntegrations, {json});
114
+ await warnOnIntegrationIssues(project, {json});
115
115
  } catch {
116
116
  // Never let the nudge break the command.
117
117
  }
@@ -71,7 +71,7 @@ function createFixture({
71
71
  );
72
72
  fs.writeFileSync(
73
73
  path.join(compDir, 'MetaAppShell.doc.mjs'),
74
- `export const docs = {\n name: 'MetaAppShell',\n usage: { description: 'Meta-flavored app shell.' },\n props: [{ name: 'title', type: 'string', description: 'Header title' }],\n};\n`,
74
+ `export default {\n type: 'component',\n name: 'MetaAppShell',\n usage: { description: 'Meta-flavored app shell.' },\n props: [{ name: 'title', type: 'string', description: 'Header title' }],\n};\n`,
75
75
  );
76
76
  if (withSource) {
77
77
  fs.writeFileSync(
@@ -82,7 +82,7 @@ function createFixture({
82
82
  if (extraComponent) {
83
83
  fs.writeFileSync(
84
84
  path.join(compDir, `${extraComponent}.doc.mjs`),
85
- `export const docs = {\n name: '${extraComponent}',\n usage: { description: '${extraComponent} from meta.' },\n};\n`,
85
+ `export default {\n type: 'component',\n name: '${extraComponent}',\n usage: { description: '${extraComponent} from meta.' },\n props: [],\n};\n`,
86
86
  );
87
87
  fs.writeFileSync(
88
88
  path.join(compDir, `${extraComponent}.tsx`),
@@ -100,7 +100,7 @@ function createFixture({
100
100
  : '';
101
101
  fs.writeFileSync(
102
102
  path.join(entryDir, `${entryPoint.component}.doc.mjs`),
103
- `export const docs = {\n name: '${entryPoint.component}',${ownSpecifier}\n usage: { description: '${entryPoint.component} from an entry point.' },\n};\n`,
103
+ `export default {\n type: 'component',\n name: '${entryPoint.component}',${ownSpecifier}\n usage: { description: '${entryPoint.component} from an entry point.' },\n props: [],\n};\n`,
104
104
  );
105
105
  }
106
106
 
@@ -69,11 +69,12 @@ describe('astryx discover with a manifest that fails to load', () => {
69
69
 
70
70
  expect(status).toBe(0);
71
71
  expect(stderr).toContain(
72
- 'Warning: @test/broken has 1 integration issue(s). ' +
73
- 'Run: astryx doctor integration validate @test/broken',
72
+ 'Warning: @test/broken has 1 integration issue(s). Run: astryx doctor',
74
73
  );
75
74
  expect(stdout).not.toContain('No integrations configured.');
76
- expect(stdout).toContain('No external components found in configured integrations.');
75
+ expect(stdout).toContain(
76
+ 'No external components found in configured integrations.',
77
+ );
77
78
  });
78
79
 
79
80
  it('reports meta.configured=true in --json, with the nudge suppressed', async () => {
@@ -101,7 +102,7 @@ describe('astryx discover with a configured package that is not installed', () =
101
102
  );
102
103
  fs.writeFileSync(
103
104
  path.join(healthy, 'components', 'Widget.doc.mjs'),
104
- `export const docs = {name: 'Widget', usage: {description: 'A widget.'}};\n`,
105
+ `export const docs = {name: 'Widget', usage: {description: 'A widget.'}, props: []};\n`,
105
106
  );
106
107
  fs.writeFileSync(
107
108
  path.join(healthy, 'components', 'Widget.tsx'),
@@ -129,8 +130,7 @@ describe('astryx discover with a configured package that is not installed', () =
129
130
 
130
131
  expect(status).toBe(0);
131
132
  expect(stderr).toContain(
132
- 'Warning: @test/missing has 1 integration issue(s). ' +
133
- 'Run: astryx doctor integration validate @test/missing',
133
+ 'Warning: @test/missing has 1 integration issue(s). Run: astryx doctor\n',
134
134
  );
135
135
  });
136
136
  });
@@ -149,8 +149,7 @@ describe('astryx search with a manifest that fails to load', () => {
149
149
 
150
150
  expect(status).toBe(0);
151
151
  expect(stderr).toContain(
152
- 'Warning: @test/broken has 1 integration issue(s). ' +
153
- 'Run: astryx doctor integration validate @test/broken',
152
+ 'Warning: @test/broken has 1 integration issue(s). Run: astryx doctor',
154
153
  );
155
154
 
156
155
  const asJson = await runCli(['search', 'button', '--json'], {cwd: project});
@@ -83,7 +83,7 @@ export function registerDiscover(program) {
83
83
  // doctor integration validate. Best-effort; suppressed in --json mode.
84
84
  try {
85
85
  const project = await Project.load(process.cwd());
86
- await warnOnIntegrationIssues(project.loadedIntegrations, {json});
86
+ await warnOnIntegrationIssues(project, {json});
87
87
  } catch {
88
88
  // Never let the nudge break the command.
89
89
  }
@@ -15,10 +15,14 @@ export const doc = {
15
15
  namespace: 'cli/commands',
16
16
  summary: 'Print reference docs',
17
17
  description:
18
- 'Reads the reference docs: with no topic it lists every topic; a topic prints that ' +
19
- 'full doc; `--index` lists its sections instead, each with the key to read it by; a ' +
20
- 'topic plus a section prints that section (by key, exact title, or a unique part of ' +
21
- 'a title).',
18
+ 'Reads the reference docs one level at a time: with no topic it lists every topic; a ' +
19
+ 'topic lists its sections, each with the key to read it by (a topic with one section ' +
20
+ 'prints whole); a topic plus a section prints that section; `--full` prints the whole ' +
21
+ 'topic, as does `--dense`. With `--json`, a topic returns its whole doc, as docs() ' +
22
+ 'does, and `--index` its section index. A route ' +
23
+ 'opens a node of the docs tree: `cli` lists its guides and reference, ' +
24
+ '`cli/api/functions` lists every API function, and `cli/api/functions/search` prints ' +
25
+ 'one.',
22
26
  fn: 'docs',
23
27
  args: [
24
28
  {name: 'topic', param: 'topic', required: false},
@@ -28,20 +32,28 @@ export const doc = {
28
32
  {
29
33
  flag: '--index',
30
34
  param: 'options.index',
31
- description: "List the topic's sections and their keys instead of printing the whole topic",
35
+ description: "List the topic's sections and their keys, even for a topic with one section",
36
+ },
37
+ {
38
+ flag: '--full',
39
+ description:
40
+ 'Print the whole topic instead of its sections (text; `--json` returns the whole topic unless --index). Cannot be set with --index',
32
41
  },
33
42
  ],
34
43
  examples: [
35
44
  {label: 'List topics', cli: 'astryx docs'},
36
- {label: 'One topic as JSON', cli: 'astryx docs spacing --json'},
37
- {label: "A topic's sections", cli: 'astryx docs theme --index'},
45
+ {label: "A topic's sections", cli: 'astryx docs theme'},
46
+ {label: 'A whole topic as JSON', cli: 'astryx docs spacing --json'},
38
47
  {label: 'One section', cli: 'astryx docs theme quick-start'},
48
+ {label: 'The CLI docs tree', cli: 'astryx docs cli'},
49
+ {label: 'One API function', cli: 'astryx docs cli/api/functions/search'},
50
+ {label: 'A whole guide', cli: 'astryx docs cli/integrations --full'},
39
51
  ],
40
52
  exitCodes: [
41
53
  {code: 0, when: 'success'},
42
54
  {
43
55
  code: 1,
44
- when: 'unknown topic, or a section that matches no section or more than one',
56
+ when: 'unknown topic or route, or a section that matches no section or more than one',
45
57
  },
46
58
  ],
47
59
  related: ['search', 'component', 'hook', 'template'],