@astryxdesign/cli 0.7.2-canary.fe2c655 → 0.7.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 (509) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +175 -154
  3. package/api/build/_adapter.d.mts +8 -0
  4. package/api/build/_adapter.mjs +12 -1
  5. package/api/build/build.doc.mjs +4 -0
  6. package/api/build/help/help.mjs +1 -1
  7. package/api/build/kit/kit.mjs +3 -11
  8. package/api/component/_adapter.d.mts +72 -1
  9. package/api/component/_adapter.mjs +212 -18
  10. package/api/component/component.doc.mjs +6 -1
  11. package/api/component/component.mjs +82 -15
  12. package/api/component/component.test.mjs +80 -0
  13. package/api/component/doc-links.test.mjs +386 -0
  14. package/api/component/integration-export-name.test.mjs +644 -0
  15. package/api/component/list/list.mjs +42 -4
  16. package/api/discover/_adapter.d.mts +132 -12
  17. package/api/discover/_adapter.mjs +488 -27
  18. package/api/discover/_adapter.test.mjs +599 -17
  19. package/api/discover/_catalog-view.d.mts +16 -5
  20. package/api/discover/_catalog-view.mjs +32 -8
  21. package/api/discover/detail/doc/doc.mjs +9 -1
  22. package/api/discover/discover.d.mts +11 -0
  23. package/api/discover/discover.doc.mjs +9 -7
  24. package/api/discover/discover.mjs +121 -19
  25. package/api/discover/discover.type.d.mts +20 -7
  26. package/api/discover/discover.type.mjs +17 -7
  27. package/api/discover/list/list.d.mts +4 -3
  28. package/api/discover/list/list.mjs +17 -10
  29. package/api/docs/_adapter.d.mts +74 -0
  30. package/api/docs/_adapter.mjs +553 -16
  31. package/api/docs/docs.doc.mjs +2 -1
  32. package/api/docs/docs.mjs +2 -1
  33. package/api/docs/docs.test.mjs +5 -2
  34. package/api/docs/integration-tree.test.mjs +22 -0
  35. package/api/docs/integrationDocs.test.mjs +124 -0
  36. package/api/docs/list/list.mjs +12 -2
  37. package/api/docs/reference-blocks.test.mjs +1 -3
  38. package/api/doctor/_adapter.d.mts +108 -0
  39. package/api/doctor/_adapter.mjs +223 -4
  40. package/api/doctor/doctor.d.mts +199 -0
  41. package/api/doctor/doctor.doc.mjs +3 -2
  42. package/api/doctor/doctor.mjs +530 -34
  43. package/api/doctor/doctor.test.mjs +180 -14
  44. package/api/doctor/release-checks.test.mjs +156 -0
  45. package/api/doctor/theme-checks.mjs +17 -4
  46. package/api/doctor/theme-checks.test.mjs +223 -7
  47. package/api/error.d.mts +10 -0
  48. package/api/error.mjs +25 -0
  49. package/api/gap-report/gap-report.d.mts +3 -0
  50. package/api/gap-report/gap-report.doc.mjs +11 -3
  51. package/api/gap-report/gap-report.mjs +219 -52
  52. package/api/gap-report/gap-report.test.mjs +233 -0
  53. package/api/gap-report/gap-report.type.d.mts +41 -0
  54. package/api/gap-report/gap-report.type.mjs +18 -0
  55. package/api/hook/_adapter.mjs +3 -11
  56. package/api/hook/hook.doc.mjs +4 -0
  57. package/api/hook/list/list.d.mts +1 -1
  58. package/api/index.d.mts +2 -0
  59. package/api/index.mjs +2 -0
  60. package/api/init/_adapter.d.mts +22 -0
  61. package/api/init/_adapter.mjs +59 -0
  62. package/api/init/init.d.mts +7 -5
  63. package/api/init/init.doc.mjs +28 -7
  64. package/api/init/init.mjs +28 -4
  65. package/api/init/init.type.d.mts +48 -3
  66. package/api/init/init.type.mjs +37 -3
  67. package/api/init/run/run.d.mts +1 -1
  68. package/api/init/run/run.mjs +17 -6
  69. package/api/init/user/user.d.mts +14 -0
  70. package/api/init/user/user.mjs +87 -0
  71. package/api/init/user/user.test.mjs +232 -0
  72. package/api/integration/_adapter.d.mts +45 -0
  73. package/api/integration/_adapter.mjs +112 -0
  74. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  75. package/api/integration/add-contribution.mjs +8 -4
  76. package/api/integration/add-contribution.test.mjs +4 -2
  77. package/api/integration/authoring-checks.mjs +110 -6
  78. package/api/integration/authoring-checks.test.mjs +808 -0
  79. package/api/integration/authoring-checks.type.d.mts +143 -0
  80. package/api/integration/authoring-checks.type.mjs +74 -2
  81. package/api/integration/check/check.d.mts +96 -0
  82. package/api/integration/check/check.mjs +357 -0
  83. package/api/integration/check/check.test.mjs +489 -0
  84. package/api/integration/integration-check.type.d.mts +118 -0
  85. package/api/integration/integration-check.type.mjs +84 -0
  86. package/api/integration/integrationCheck.doc.d.mts +10 -0
  87. package/api/integration/integrationCheck.doc.mjs +73 -0
  88. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  89. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  90. package/api/integration/pack-check.mjs +364 -120
  91. package/api/integration/pack-check.test.mjs +730 -10
  92. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  93. package/api/integration/validate-integration.d.mts +8 -0
  94. package/api/integration/validate-integration.mjs +63 -9
  95. package/api/integration/validate-integration.test.mjs +88 -8
  96. package/api/integration/validate-integration.type.d.mts +16 -0
  97. package/api/integration/validate-integration.type.mjs +14 -1
  98. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  99. package/api/integration/validateIntegration.doc.mjs +19 -3
  100. package/api/search/_adapter.d.mts +11 -0
  101. package/api/search/_adapter.mjs +25 -0
  102. package/api/search/search.d.mts +82 -13
  103. package/api/search/search.doc.mjs +8 -3
  104. package/api/search/search.domain-priority.test.mjs +108 -18
  105. package/api/search/search.integration-check.test.mjs +24 -0
  106. package/api/search/search.mjs +645 -64
  107. package/api/search/search.test.mjs +179 -1
  108. package/api/search/search.type.d.mts +14 -1
  109. package/api/search/search.type.mjs +6 -1
  110. package/api/swizzle/_adapter.mjs +2 -2
  111. package/api/swizzle/copy/copy.mjs +29 -15
  112. package/api/template/template-integration.test.mjs +1 -1
  113. package/api/template/template.test.mjs +120 -3
  114. package/api/theme/_adapter.d.mts +16 -0
  115. package/api/theme/_adapter.mjs +32 -5
  116. package/api/theme/build/build.d.mts +2 -1
  117. package/api/theme/build/build.family.test.mjs +64 -0
  118. package/api/theme/build/build.mjs +250 -38
  119. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  120. package/api/theme/build/font-warning.mjs +5 -3
  121. package/api/theme/build/font-warning.test.mjs +2 -1
  122. package/api/theme/remove/remove.test.mjs +2 -2
  123. package/api/theme/targets/targets.mjs +3 -10
  124. package/api/theme/themeBuild.doc.mjs +4 -4
  125. package/api/theme/themeTargets.doc.mjs +4 -0
  126. package/api/theme/use/use.test.mjs +1 -1
  127. package/api/upgrade/_adapter.d.mts +40 -2
  128. package/api/upgrade/_adapter.mjs +118 -5
  129. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  130. package/api/upgrade/run/run.mjs +35 -2
  131. package/api/upgrade/status/status.d.mts +21 -5
  132. package/api/upgrade/status/status.mjs +45 -8
  133. package/api/upgrade/upgrade.doc.mjs +6 -3
  134. package/api/upgrade/upgrade.mjs +5 -1
  135. package/api/upgrade/upgrade.type.d.mts +23 -0
  136. package/api/upgrade/upgrade.type.mjs +16 -1
  137. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  138. package/assets/codemods/registry.mjs +1 -0
  139. package/assets/codemods/runner.mjs +7 -1
  140. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  141. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  142. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  143. package/assets/discover/official-packages.json +367 -0
  144. package/assets/docs/README.md +10 -0
  145. package/assets/docs/author-a-theme.doc.mjs +3 -3
  146. package/assets/docs/browser-support.doc.mjs +1 -1
  147. package/assets/docs/getting-started.doc.mjs +26 -6
  148. package/assets/docs/styling-overview.doc.mjs +1 -1
  149. package/assets/docs/theme.doc.dense.mjs +1 -1
  150. package/assets/docs/theme.doc.mjs +2 -2
  151. package/assets/docs/theme.doc.zh.mjs +1 -1
  152. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  153. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  154. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  155. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  156. package/assets/docs/tree/block-template.doc.mjs +8 -20
  157. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  158. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  159. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  160. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  161. package/assets/docs/tree/checks.doc.mjs +32 -24
  162. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  163. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  164. package/assets/docs/tree/cli.doc.mjs +6 -0
  165. package/assets/docs/tree/codemods.doc.mjs +17 -7
  166. package/assets/docs/tree/commands.doc.mjs +7 -1
  167. package/assets/docs/tree/component-family.doc.mjs +8 -6
  168. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  169. package/assets/docs/tree/components.doc.mjs +7 -1
  170. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  171. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  172. package/assets/docs/tree/configuration.doc.mjs +7 -3
  173. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  174. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  175. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  176. package/assets/docs/tree/docs.doc.mjs +9 -3
  177. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  178. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  179. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  180. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  181. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  182. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  183. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  184. package/assets/docs/tree/global.doc.mjs +131 -0
  185. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  186. package/assets/docs/tree/install-once.doc.mjs +105 -0
  187. package/assets/docs/tree/integrations.doc.mjs +13 -1
  188. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  189. package/assets/docs/tree/layout.doc.mjs +6 -6
  190. package/assets/docs/tree/links.doc.mjs +7 -6
  191. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  192. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  193. package/assets/docs/tree/migration.doc.mjs +0 -1
  194. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  195. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  196. package/assets/docs/tree/page-template.doc.mjs +4 -8
  197. package/assets/docs/tree/problems.doc.mjs +205 -0
  198. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  199. package/assets/docs/tree/publishing.doc.mjs +6 -6
  200. package/assets/docs/tree/quick-start.doc.mjs +53 -27
  201. package/assets/docs/tree/reference.doc.mjs +192 -0
  202. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  203. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  204. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  205. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  206. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  207. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  208. package/assets/docs/tree/single-component.doc.mjs +15 -6
  209. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  210. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  211. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  212. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  213. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  214. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  215. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  216. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  217. package/assets/docs/tree/templates.doc.mjs +7 -1
  218. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  219. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  220. package/assets/docs/tree/themes.doc.mjs +4 -0
  221. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  222. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  223. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  224. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  225. package/assets/docs/tree/updates.doc.mjs +92 -0
  226. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  227. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  228. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  229. package/assets/docs/tree/versioning.doc.mjs +10 -9
  230. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  231. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  232. package/assets/docs/use-a-theme.doc.mjs +2 -2
  233. package/assets/docs/working-with-ai.doc.mjs +66 -13
  234. package/assets/theme.template.ts +1 -1
  235. package/authoring/codemod/codemod.doc.mjs +7 -3
  236. package/authoring/config/config.doc.mjs +9 -6
  237. package/authoring/config/type.ts +4 -2
  238. package/authoring/debug/parse.d.mts +5 -5
  239. package/authoring/discover/discover.doc.mjs +7 -1
  240. package/authoring/doctypes/_schema.d.mts +8 -3
  241. package/authoring/doctypes/_schema.mjs +1 -0
  242. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  243. package/authoring/doctypes/command/command.doc.mjs +20 -12
  244. package/authoring/doctypes/command/type.ts +14 -0
  245. package/authoring/doctypes/component/component.doc.mjs +30 -27
  246. package/authoring/doctypes/component/type.ts +21 -8
  247. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  248. package/authoring/doctypes/function/function.doc.mjs +9 -8
  249. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  250. package/authoring/doctypes/hook/type.ts +4 -0
  251. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  252. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  253. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  254. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  255. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  256. package/authoring/doctypes/template/parse.d.mts +6 -2
  257. package/authoring/doctypes/template/parse.mjs +3 -1
  258. package/authoring/doctypes/template/template.doc.mjs +3 -3
  259. package/authoring/doctypes/template/type.ts +22 -0
  260. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  261. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  262. package/authoring/identity/identity.doc.mjs +3 -3
  263. package/authoring/index.d.ts +2 -0
  264. package/authoring/integration/integration.doc.mjs +30 -6
  265. package/authoring/integration/parse.test.mjs +21 -0
  266. package/authoring/integration/schema.d.mts +16 -3
  267. package/authoring/integration/schema.mjs +34 -2
  268. package/authoring/integration/type.ts +12 -0
  269. package/clients/cli/bin/astryx.mjs +20 -3
  270. package/clients/cli/cli-command.test.mjs +166 -0
  271. package/clients/cli/commands/blog.doc.mjs +1 -0
  272. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  273. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  274. package/clients/cli/commands/build-theme.mjs +26 -13
  275. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  276. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  277. package/clients/cli/commands/build.doc.mjs +1 -0
  278. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  279. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  280. package/clients/cli/commands/component.doc.mjs +1 -0
  281. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  282. package/clients/cli/commands/discover.doc.mjs +9 -1
  283. package/clients/cli/commands/discover.mjs +144 -93
  284. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  285. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  286. package/clients/cli/commands/docs.doc.mjs +1 -0
  287. package/clients/cli/commands/docs.mjs +49 -19
  288. package/clients/cli/commands/docs.test.mjs +2 -2
  289. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  290. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  291. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  292. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  293. package/clients/cli/commands/doctor-integration.test.mjs +888 -3
  294. package/clients/cli/commands/doctor.doc.mjs +12 -2
  295. package/clients/cli/commands/doctor.mjs +292 -7
  296. package/clients/cli/commands/doctor.test.mjs +10 -4
  297. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  298. package/clients/cli/commands/gap-report.mjs +24 -2
  299. package/clients/cli/commands/gap-report.test.mjs +79 -0
  300. package/clients/cli/commands/hook.doc.mjs +1 -0
  301. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  302. package/clients/cli/commands/init.doc.mjs +29 -3
  303. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  304. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  305. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  306. package/clients/cli/commands/integration-check.test.mjs +441 -0
  307. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  308. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  309. package/clients/cli/commands/integration.doc.mjs +6 -2
  310. package/clients/cli/commands/integration.mjs +119 -7
  311. package/clients/cli/commands/json-contract.test.mjs +11 -2
  312. package/clients/cli/commands/manifest.doc.mjs +1 -0
  313. package/clients/cli/commands/outside-project.test.mjs +774 -0
  314. package/clients/cli/commands/search.doc.mjs +1 -0
  315. package/clients/cli/commands/search.mjs +16 -11
  316. package/clients/cli/commands/search.test.mjs +429 -296
  317. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  318. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  319. package/clients/cli/commands/swizzle.mjs +5 -3
  320. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  321. package/clients/cli/commands/template.doc.mjs +1 -0
  322. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  323. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  324. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  325. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  326. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  327. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  328. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  329. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  330. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  331. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  332. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  333. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  334. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  335. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  336. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  337. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  338. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  339. package/clients/cli/index.mjs +112 -16
  340. package/clients/cli/lib/cli-error.mjs +4 -1
  341. package/clients/cli/lib/component-format.test.mjs +1 -1
  342. package/clients/cli/lib/define-command.mjs +2 -2
  343. package/clients/cli/lib/json-shim.mjs +3 -1
  344. package/clients/cli/lib/manifest.d.ts +9 -0
  345. package/clients/cli/lib/manifest.mjs +17 -1
  346. package/clients/cli/lib/manifest.test.mjs +25 -0
  347. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  348. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  349. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  350. package/foundation/agent-docs/agent-docs.mjs +11 -9
  351. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  352. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  353. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  354. package/foundation/config/project.d.mts +35 -3
  355. package/foundation/config/project.mjs +226 -7
  356. package/foundation/config/project.test.mjs +66 -0
  357. package/foundation/config/theme-state.mjs +5 -4
  358. package/foundation/discovery/authoring-self-docs.mjs +2 -1
  359. package/foundation/discovery/cli-self-docs.mjs +20 -3
  360. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  361. package/foundation/discovery/component-discovery.d.mts +190 -10
  362. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  363. package/foundation/discovery/component-discovery.mjs +864 -56
  364. package/foundation/discovery/component-discovery.test.mjs +96 -0
  365. package/foundation/discovery/component-replacement.mjs +20 -1
  366. package/foundation/discovery/component-replacement.test.mjs +40 -0
  367. package/foundation/discovery/copied-passages.d.mts +216 -0
  368. package/foundation/discovery/copied-passages.mjs +720 -0
  369. package/foundation/discovery/copied-passages.test.mjs +124 -0
  370. package/foundation/discovery/doc-consistency.d.mts +34 -0
  371. package/foundation/discovery/doc-consistency.mjs +513 -0
  372. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  373. package/foundation/discovery/doc-fluff.d.mts +112 -0
  374. package/foundation/discovery/doc-fluff.mjs +328 -0
  375. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  376. package/foundation/discovery/doc-types.d.mts +157 -0
  377. package/foundation/discovery/doc-types.mjs +1231 -0
  378. package/foundation/discovery/doc-types.test.mjs +744 -0
  379. package/foundation/discovery/docs-discovery.d.mts +88 -0
  380. package/foundation/discovery/docs-discovery.mjs +83 -2
  381. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  382. package/foundation/discovery/integration-doc-files.mjs +231 -0
  383. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  384. package/foundation/discovery/module-exports.d.mts +27 -0
  385. package/foundation/discovery/module-exports.mjs +356 -31
  386. package/foundation/discovery/module-exports.test.mjs +172 -0
  387. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  388. package/foundation/discovery/package-tsconfig.mjs +228 -0
  389. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  390. package/foundation/discovery/project-typescript.d.mts +32 -0
  391. package/foundation/discovery/project-typescript.mjs +89 -0
  392. package/foundation/discovery/prop-drift.d.mts +297 -0
  393. package/foundation/discovery/prop-drift.mjs +2428 -0
  394. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  395. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  396. package/foundation/discovery/runner-allowlist.mjs +53 -0
  397. package/foundation/discovery/stray-pointers.d.mts +52 -0
  398. package/foundation/discovery/stray-pointers.mjs +293 -0
  399. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  400. package/foundation/discovery/template-adapter.mjs +74 -5
  401. package/foundation/discovery/template-needs.mjs +5 -2
  402. package/foundation/discovery/theme-imports.mjs +8 -4
  403. package/foundation/discovery/theme-imports.test.mjs +1 -1
  404. package/foundation/discovery/type-match.d.mts +53 -0
  405. package/foundation/discovery/type-match.mjs +660 -0
  406. package/foundation/discovery/type-match.test.mjs +169 -0
  407. package/foundation/discovery/type-text.d.mts +68 -0
  408. package/foundation/discovery/type-text.mjs +658 -0
  409. package/foundation/discovery/type-text.test.mjs +196 -0
  410. package/foundation/discovery/unresolved-references.d.mts +62 -0
  411. package/foundation/discovery/unresolved-references.mjs +790 -0
  412. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  413. package/foundation/doc-compiler/doc-loads.test.mjs +51 -1
  414. package/foundation/doc-compiler/inputs.mjs +27 -1
  415. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  416. package/foundation/doc-compiler/links.d.mts +41 -5
  417. package/foundation/doc-compiler/links.mjs +167 -28
  418. package/foundation/doc-compiler/links.test.mjs +97 -4
  419. package/foundation/doc-compiler/read.d.mts +1 -1
  420. package/foundation/doc-compiler/tree.mjs +30 -1
  421. package/foundation/doc-compiler/tree.test.mjs +8 -1
  422. package/foundation/env/cli-command.d.mts +83 -0
  423. package/foundation/env/cli-command.mjs +152 -0
  424. package/foundation/env/cli-command.test.mjs +129 -0
  425. package/foundation/env/node-version.test.mjs +15 -3
  426. package/foundation/env/package-manager.d.mts +93 -9
  427. package/foundation/env/package-manager.global.test.mjs +66 -0
  428. package/foundation/env/package-manager.mjs +184 -13
  429. package/foundation/env/package-manager.test.mjs +341 -15
  430. package/foundation/env/project-scope.d.mts +9 -0
  431. package/foundation/env/project-scope.mjs +33 -0
  432. package/foundation/env/search-path.d.mts +42 -0
  433. package/foundation/env/search-path.mjs +205 -0
  434. package/foundation/env/search-path.test.mjs +122 -0
  435. package/foundation/env/user-dirs.d.mts +39 -0
  436. package/foundation/env/user-dirs.mjs +93 -0
  437. package/foundation/env/user-dirs.test.mjs +72 -0
  438. package/foundation/env/user-settings.d.mts +46 -0
  439. package/foundation/env/user-settings.mjs +106 -0
  440. package/foundation/env/user-settings.test.mjs +64 -0
  441. package/foundation/fs/file-protection.mjs +15 -1
  442. package/foundation/fs/module-loader.d.mts +2 -1
  443. package/foundation/fs/module-loader.mjs +60 -4
  444. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  445. package/foundation/fs/module-loader.test.mjs +194 -0
  446. package/foundation/fs/path-compare.d.mts +33 -0
  447. package/foundation/fs/path-compare.mjs +73 -0
  448. package/foundation/fs/paths.d.mts +57 -1
  449. package/foundation/fs/paths.mjs +131 -3
  450. package/foundation/fs/paths.test.mjs +10 -1
  451. package/foundation/integrations/computer-integrations.d.mts +78 -0
  452. package/foundation/integrations/computer-integrations.mjs +316 -0
  453. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  454. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  455. package/foundation/integrations/contribution-inventory.mjs +56 -3
  456. package/foundation/integrations/integration-warnings.mjs +3 -2
  457. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  458. package/foundation/integrations/integrations.d.mts +25 -1
  459. package/foundation/integrations/integrations.mjs +46 -3
  460. package/foundation/integrations/issue.ts +6 -0
  461. package/foundation/integrations/validate-contributions.d.mts +2 -0
  462. package/foundation/integrations/validate-contributions.mjs +41 -1
  463. package/foundation/release/agent-text-worker.d.mts +4 -0
  464. package/foundation/release/agent-text-worker.mjs +52 -0
  465. package/foundation/release/cache.d.mts +310 -0
  466. package/foundation/release/cache.mjs +1216 -0
  467. package/foundation/release/cache.test.mjs +920 -0
  468. package/foundation/release/choose.d.mts +207 -0
  469. package/foundation/release/choose.mjs +677 -0
  470. package/foundation/release/choose.test.mjs +427 -0
  471. package/foundation/release/core-worker.d.mts +4 -0
  472. package/foundation/release/core-worker.mjs +38 -0
  473. package/foundation/release/global-state.d.mts +259 -0
  474. package/foundation/release/global-state.mjs +472 -0
  475. package/foundation/release/global-state.test.mjs +381 -0
  476. package/foundation/release/handoff.d.mts +60 -0
  477. package/foundation/release/handoff.e2e.test.mjs +278 -0
  478. package/foundation/release/handoff.mjs +357 -0
  479. package/foundation/release/lock-lease.d.mts +23 -0
  480. package/foundation/release/lock-lease.mjs +80 -0
  481. package/foundation/release/notices.d.mts +253 -0
  482. package/foundation/release/notices.mjs +596 -0
  483. package/foundation/release/notices.test.mjs +565 -0
  484. package/foundation/release/npm-guard.d.mts +4 -0
  485. package/foundation/release/npm-guard.mjs +126 -0
  486. package/foundation/release/project.d.mts +141 -0
  487. package/foundation/release/project.mjs +466 -0
  488. package/foundation/release/project.test.mjs +288 -0
  489. package/foundation/release/refresh-worker.d.mts +4 -0
  490. package/foundation/release/refresh-worker.mjs +27 -0
  491. package/foundation/release/run-context.d.mts +47 -0
  492. package/foundation/release/run-context.mjs +149 -0
  493. package/foundation/release/update-notice-env.d.mts +28 -0
  494. package/foundation/release/update-notice-env.mjs +39 -0
  495. package/foundation/response/base.d.ts +26 -3
  496. package/foundation/response/error-codes.d.mts +3 -1
  497. package/foundation/response/error-codes.doc.mjs +12 -0
  498. package/foundation/response/error-codes.mjs +17 -0
  499. package/foundation/response/error-codes.test.mjs +2 -0
  500. package/foundation/response/json-contract.test.mjs +7 -2
  501. package/foundation/response/json.mjs +4 -1
  502. package/foundation/response/response-types.doc.mjs +22 -11
  503. package/foundation/response/response-types.doc.test.mjs +46 -0
  504. package/foundation/response/response.doc.mjs +3 -3
  505. package/foundation/xle/browser-imports.test.mjs +74 -0
  506. package/foundation/xle/registry.mjs +4 -0
  507. package/foundation/xle/registry.test.mjs +28 -0
  508. package/package.json +11 -11
  509. package/scripts/postinstall.mjs +24 -5
@@ -19,9 +19,11 @@ import {
19
19
  requireCoreDir,
20
20
  loadIntegrationsSafely,
21
21
  resolveOwners,
22
+ scopeIntegrationMember,
22
23
  classifyScope,
23
24
  assertUnambiguousOwners,
24
25
  resolveLegacyExternalDoc,
26
+ legacyExternalKeeps,
25
27
  resolveCoreSourcePath,
26
28
  resolveUnscopedDoc,
27
29
  loadComponentDoc,
@@ -29,6 +31,8 @@ import {
29
31
  ComponentAmbiguityError,
30
32
  installedComponentPackageVersion,
31
33
  resolveComponentReplacement,
34
+ unscopedComponentOwners,
35
+ linkComponentResponse,
32
36
  } from './_adapter.mjs';
33
37
  import {componentList} from './list/list.mjs';
34
38
  import {componentDetail} from './detail/detail.mjs';
@@ -254,6 +258,23 @@ async function componentBatch(selectors, options, coreDir) {
254
258
  * )>}
255
259
  */
256
260
  export async function component(name, options = {}) {
261
+ // Every read resolves the links in the doc text it returns (spec:AST-047
262
+ // FR9); a batch's results are each a read of their own.
263
+ return /** @type {any} */ (
264
+ await linkComponentResponse(
265
+ await componentRead(name, options),
266
+ options.cwd ?? process.cwd(),
267
+ )
268
+ );
269
+ }
270
+
271
+ /**
272
+ * {@link component}, with each `{@link}` in its doc text as written.
273
+ * @param {string|string[]} [name]
274
+ * @param {Parameters<typeof component>[1]} [options]
275
+ * @returns {ReturnType<typeof component>}
276
+ */
277
+ async function componentRead(name, options = {}) {
257
278
  const {
258
279
  cwd = process.cwd(),
259
280
  list = false,
@@ -354,7 +375,7 @@ export async function component(name, options = {}) {
354
375
  requireInstalledSelectorVersion(target, coreDir, loadedIntegrations);
355
376
  }
356
377
  if (target.name !== name || target.package !== packageScope) {
357
- return component(target.name, {...options, package: target.package});
378
+ return componentRead(target.name, {...options, package: target.package});
358
379
  }
359
380
  name = target.name;
360
381
  }
@@ -393,7 +414,10 @@ export async function component(name, options = {}) {
393
414
  dirName,
394
415
  );
395
416
  if (replacement?.package === packageScope) {
396
- return component(replacement.name, {...options, package: packageScope});
417
+ return componentRead(replacement.name, {
418
+ ...options,
419
+ package: packageScope,
420
+ });
397
421
  }
398
422
  }
399
423
  const scoped = classifyScope(packageScope, {
@@ -421,20 +445,40 @@ export async function component(name, options = {}) {
421
445
  // a showcase, so skip discovery entirely.
422
446
  if (showcase) {
423
447
  return scoped.kind === 'core'
424
- ? componentDetailShowcase(dirName, {cwd, name, ownerPackage: owner.package})
425
- : componentDetailShowcase(dirName, {cwd, name, resolve: false, ownerPackage: owner.package});
448
+ ? componentDetailShowcase(dirName, {
449
+ cwd,
450
+ name,
451
+ ownerPackage: owner.package,
452
+ })
453
+ : componentDetailShowcase(dirName, {
454
+ cwd,
455
+ name,
456
+ resolve: false,
457
+ ownerPackage: owner.package,
458
+ });
426
459
  }
427
460
  if (blocks) {
428
461
  return componentDetailBlocks(dirName, cwd, owner.package);
429
462
  }
430
- const docs = await loadComponentDoc(owner.docPath, docOpts);
463
+ const loaded = await loadComponentDoc(owner.docPath, docOpts);
464
+ // A family member an integration lists under its own name reads as that
465
+ // member, the way a Core sub-component does below.
466
+ const docs =
467
+ scoped.kind === 'integration'
468
+ ? scopeIntegrationMember(loaded, owner, dirName)
469
+ : loaded;
431
470
  if (props) return componentDetailProps(docs, owner.package);
432
471
  return componentDetail(docs, owner, dirName, coreDir);
433
472
  }
434
473
 
435
474
  // Legacy `pkg.astryx.docs` external package.
436
475
  if (showcase) {
437
- return componentDetailShowcase(dirName, {cwd, name, packageScope, ownerPackage: packageScope});
476
+ return componentDetailShowcase(dirName, {
477
+ cwd,
478
+ name,
479
+ packageScope,
480
+ ownerPackage: packageScope,
481
+ });
438
482
  }
439
483
  const extDocPath = resolveLegacyExternalDoc(scoped.ext, dirName);
440
484
  if (extDocPath) {
@@ -475,18 +519,20 @@ export async function component(name, options = {}) {
475
519
  dirName,
476
520
  );
477
521
  if (replacement) {
478
- return component(replacement.name, {
522
+ return componentRead(replacement.name, {
479
523
  ...options,
480
524
  package: replacement.package,
481
525
  });
482
526
  }
483
527
 
528
+ // The owner rule runs first, over valid and invalid owners alike.
529
+ const ruled = unscopedComponentOwners(owners);
484
530
  // Invalid integration metadata does not create ambiguity against a valid
485
- // owner. Keep raw owners only when no doc owner is valid, so an integration-
531
+ // owner. Keep ruled owners only when no doc owner is valid, so an integration-
486
532
  // only component still exposes its source and returns ERR_INVALID_DOC for
487
533
  // detail instead of degrading to an unrelated unknown-component error.
488
534
  const validOwners = [];
489
- for (const owner of owners) {
535
+ for (const owner of ruled) {
490
536
  if (owner.package === CORE_PACKAGE) {
491
537
  validOwners.push(owner);
492
538
  continue;
@@ -498,7 +544,12 @@ export async function component(name, options = {}) {
498
544
  // Project/Doctor report invalid metadata; it is not an effective owner.
499
545
  }
500
546
  }
501
- const effectiveOwners = validOwners.length > 0 ? validOwners : owners;
547
+ const loadable = validOwners.length > 0 ? validOwners : ruled;
548
+ // A legacy docs package's exact doc keeps its name over an export name a doc
549
+ // records, as Core and file-named owners do.
550
+ const effectiveOwners = legacyExternalKeeps(cwd, dirName, loadable)
551
+ ? []
552
+ : loadable;
502
553
 
503
554
  // ── Ambiguity: owned by MORE THAN ONE valid package, no --package ──
504
555
  assertUnambiguousOwners(effectiveOwners, dirName);
@@ -510,13 +561,22 @@ export async function component(name, options = {}) {
510
561
  ) {
511
562
  const owner = effectiveOwners[0];
512
563
  if (source) {
513
- return componentDetailSource(dirName, owner.sourcePath, {name, ownerPackage: owner.package});
564
+ return componentDetailSource(dirName, owner.sourcePath, {
565
+ name,
566
+ ownerPackage: owner.package,
567
+ });
514
568
  }
515
569
  if (showcase) {
516
570
  // Integration components don't carry showcases — fail without scanning.
517
- return componentDetailShowcase(dirName, {cwd, name, resolve: false, ownerPackage: owner.package});
571
+ return componentDetailShowcase(dirName, {
572
+ cwd,
573
+ name,
574
+ resolve: false,
575
+ ownerPackage: owner.package,
576
+ });
518
577
  }
519
- const docs = await loadComponentDoc(owner.docPath, docOpts);
578
+ const loaded = await loadComponentDoc(owner.docPath, docOpts);
579
+ const docs = scopeIntegrationMember(loaded, owner, dirName);
520
580
  if (props) return componentDetailProps(docs, owner.package);
521
581
  return componentDetail(docs, owner, dirName, coreDir);
522
582
  }
@@ -531,7 +591,11 @@ export async function component(name, options = {}) {
531
591
  );
532
592
  }
533
593
  if (showcase) {
534
- return componentDetailShowcase(dirName, {cwd, name, ownerPackage: CORE_PACKAGE});
594
+ return componentDetailShowcase(dirName, {
595
+ cwd,
596
+ name,
597
+ ownerPackage: CORE_PACKAGE,
598
+ });
535
599
  }
536
600
 
537
601
  const resolved = await resolveUnscopedDoc(dirName, {coreDir, cwd, name});
@@ -548,7 +612,10 @@ export async function component(name, options = {}) {
548
612
  const sub = scopeSubComponent(docs, dirName, coreDir);
549
613
  if (sub) {
550
614
  if (props)
551
- return componentDetailProps({props: sub.matchingComponent.props}, resolved.resolvedOwnerPackage);
615
+ return componentDetailProps(
616
+ {props: sub.matchingComponent.props},
617
+ resolved.resolvedOwnerPackage,
618
+ );
552
619
  return componentDetail(
553
620
  sub.scoped,
554
621
  {
@@ -13,6 +13,7 @@ import {describe, it, expect} from 'vitest';
13
13
  import * as path from 'node:path';
14
14
  import {fileURLToPath} from 'node:url';
15
15
  import {component} from './component.mjs';
16
+ import {search} from '../search/search.mjs';
16
17
  import {AstryxError} from '../error.mjs';
17
18
  import {runCli} from '../../test-utils/run-cli.mjs';
18
19
 
@@ -92,6 +93,85 @@ describe('component dispatcher — detail levels on list', () => {
92
93
  );
93
94
  });
94
95
 
96
+ describe('component list — components whose docs live in a family folder', () => {
97
+ it('lists HStack, VStack and Heading at every detail level, each with its own import', async () => {
98
+ const {docs: stackDoc} = await import(
99
+ path.join(REPO, 'packages', 'core', 'src', 'Stack', 'Stack.doc.mjs')
100
+ );
101
+ const vstackMember = stackDoc.components.find(c => c.name === 'VStack');
102
+
103
+ const names = await component(undefined, {cwd, list: true});
104
+ const listed = Object.values(names.data.components).flat();
105
+ for (const name of ['HStack', 'VStack', 'Heading']) {
106
+ expect(listed).toContainEqual({name, package: '@astryxdesign/core'});
107
+ }
108
+
109
+ const compact = await component(undefined, {cwd, list: true, detail: 'compact'});
110
+ const brief = Object.values(compact.data.components).flat();
111
+ expect(brief.find(e => e.name === 'VStack')).toEqual({
112
+ name: 'VStack',
113
+ package: '@astryxdesign/core',
114
+ description: vstackMember.description,
115
+ import: '@astryxdesign/core/VStack',
116
+ });
117
+
118
+ const full = await component(undefined, {cwd, list: true, detail: 'full'});
119
+ const entry = Object.values(full.data.components)
120
+ .flat()
121
+ .find(e => e.name === 'VStack');
122
+ // Shaped like every other full entry (a component doc plus its package):
123
+ // the member's own name, display name and summary, and no scoping field
124
+ // the full-entry type does not describe.
125
+ expect(entry).toEqual({
126
+ name: 'VStack',
127
+ package: '@astryxdesign/core',
128
+ displayName: vstackMember.displayName,
129
+ group: stackDoc.group,
130
+ category: stackDoc.category,
131
+ usage: {description: vstackMember.description},
132
+ props: vstackMember.props,
133
+ theming: stackDoc.theming,
134
+ import: '@astryxdesign/core/VStack',
135
+ });
136
+ }, SLOW);
137
+
138
+ it('prints each one\'s own import path in the list, the detail, and search', async () => {
139
+ // The path `component <Name>` prints is the one docs and examples teach:
140
+ // the component's own entry point, never its family's (/Stack, /Text) or
141
+ // a re-export (/Layout).
142
+ const compact = Object.values(
143
+ (await component(undefined, {cwd, list: true, detail: 'compact'})).data.components,
144
+ ).flat();
145
+ const full = Object.values(
146
+ (await component(undefined, {cwd, list: true, detail: 'full'})).data.components,
147
+ ).flat();
148
+ const textList = (await runCli(['component', '--list'], cwd)).stdout;
149
+ const textCompact = (
150
+ await runCli(['component', '--list', '--detail', 'compact'], cwd)
151
+ ).stdout;
152
+ /** The `import:` line of a text record, found by its `name:` line. */
153
+ const textImport = (text, name) =>
154
+ new RegExp(`^name:\\s+${name}\\n(?:(?!name:).*\\n)*?import:\\s+(\\S+)`, 'm').exec(
155
+ text,
156
+ )?.[1];
157
+ for (const name of ['VStack', 'HStack', 'Heading']) {
158
+ const expected = `@astryxdesign/core/${name}`;
159
+ expect(compact.find(e => e.name === name)?.import).toBe(expected);
160
+ expect((await component(name, {cwd})).data.import).toBe(expected);
161
+ const hits = (await search(name, {cwd, type: 'component'})).data.results;
162
+ expect(hits.find(r => r.name === name)?.import).toBe(expected);
163
+ expect(textImport(textList, name)).toBe(expected);
164
+ expect(textImport(textCompact, name)).toBe(expected);
165
+ }
166
+ // The full level spreads a component's own doc, which states no import
167
+ // (Button's entry has none either); a member's entry is scoped to the
168
+ // member and states its own.
169
+ for (const name of ['VStack', 'HStack']) {
170
+ expect(full.find(e => e.name === name)?.import).toBe(`@astryxdesign/core/${name}`);
171
+ }
172
+ }, SLOW);
173
+ });
174
+
95
175
  describe('component dispatcher — projection precedence (locked)', () => {
96
176
  it(
97
177
  '--props routes to component.detail.props',
@@ -0,0 +1,386 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Links in a component doc's text and a topic's description
5
+ * (spec:AST-047 FR9): a read resolves each `{@link <target>}` to the command
6
+ * that opens its doc, in text and JSON, the way a topic section's links read.
7
+ * Code fields and code ticks show the syntax as written.
8
+ *
9
+ * Fixtures live under a repo-local temp dir, not /tmp, because Vite refuses to
10
+ * dynamically import a module from outside the project root.
11
+ */
12
+
13
+ import * as fs from 'node:fs';
14
+ import * as path from 'node:path';
15
+ import {afterEach, beforeEach, describe, expect, it} from 'vitest';
16
+ import {component} from './component.mjs';
17
+ import {CORE_PACKAGE, linkComponentResponse} from './_adapter.mjs';
18
+ import {docs} from '../docs/docs.mjs';
19
+ import {search} from '../search/search.mjs';
20
+ import {runCli} from '../../test-utils/run-cli.mjs';
21
+
22
+ const SLOW = 60_000;
23
+
24
+ let tmpDir;
25
+
26
+ const ACME_BUTTON = {
27
+ type: 'component',
28
+ name: 'AcmeButton',
29
+ displayName: 'Acme Button',
30
+ usage: {
31
+ description:
32
+ 'A button. Set it up as {@link generic:acme-setup} says; theme it with {@link @astryxdesign/cli:generic:theme}.',
33
+ bestPractices: [
34
+ {
35
+ guidance: true,
36
+ description: 'Pair it with {@link component:AcmeButton}.',
37
+ },
38
+ ],
39
+ },
40
+ props: [
41
+ {
42
+ name: 'tone',
43
+ type: 'string',
44
+ description:
45
+ 'Read {@link generic:acme-setup}; write `{@link generic:acme-setup}` to link.',
46
+ default: "'{@link generic:acme-setup}'",
47
+ },
48
+ ],
49
+ examples: [
50
+ {
51
+ label: 'As {@link generic:acme-setup} shows',
52
+ code: '// {@link generic:acme-setup}',
53
+ },
54
+ ],
55
+ };
56
+
57
+ const ACME_SETUP = {
58
+ type: 'generic',
59
+ name: 'acme-setup',
60
+ title: 'Acme setup',
61
+ description:
62
+ 'Set up Acme, then theme it as {@link @astryxdesign/cli:generic:theme} says.',
63
+ sections: [
64
+ {title: 'Overview', content: [{type: 'prose', text: 'Install it.'}]},
65
+ ],
66
+ };
67
+
68
+ function scaffold() {
69
+ fs.writeFileSync(
70
+ path.join(tmpDir, 'package.json'),
71
+ JSON.stringify({name: 'consumer'}),
72
+ );
73
+ fs.writeFileSync(
74
+ path.join(tmpDir, 'astryx.config.mjs'),
75
+ "export default {integrations: ['@acme/widgets']};\n",
76
+ );
77
+ const pkgDir = path.join(tmpDir, 'node_modules', '@acme', 'widgets');
78
+ fs.mkdirSync(path.join(pkgDir, 'components'), {recursive: true});
79
+ fs.mkdirSync(path.join(pkgDir, 'docs'), {recursive: true});
80
+ fs.writeFileSync(
81
+ path.join(pkgDir, 'package.json'),
82
+ JSON.stringify({name: '@acme/widgets', version: '1.0.0'}),
83
+ );
84
+ fs.writeFileSync(
85
+ path.join(pkgDir, 'astryx.integration.mjs'),
86
+ "export default {components: './components', docs: './docs'};\n",
87
+ );
88
+ fs.writeFileSync(
89
+ path.join(pkgDir, 'components', 'AcmeButton.doc.mjs'),
90
+ `export default ${JSON.stringify(ACME_BUTTON, null, 2)};\n`,
91
+ );
92
+ fs.writeFileSync(
93
+ path.join(pkgDir, 'components', 'AcmeButton.tsx'),
94
+ 'export function AcmeButton() { return null; }\n',
95
+ );
96
+ fs.writeFileSync(
97
+ path.join(pkgDir, 'docs', 'acme-setup.doc.mjs'),
98
+ `export default ${JSON.stringify(ACME_SETUP, null, 2)};\n`,
99
+ );
100
+ }
101
+
102
+ beforeEach(() => {
103
+ tmpDir = fs.mkdtempSync(path.join(process.cwd(), '.astryx-component-links-'));
104
+ });
105
+
106
+ afterEach(() => {
107
+ fs.rmSync(tmpDir, {recursive: true, force: true});
108
+ });
109
+
110
+ describe("a link in a component doc's text", () => {
111
+ it(
112
+ "reads as the command that opens its doc, against the integration's own docs",
113
+ async () => {
114
+ scaffold();
115
+ const read = /** @type {any} */ (
116
+ await component('AcmeButton', {cwd: tmpDir})
117
+ );
118
+ expect(read.type).toBe('component.detail');
119
+ expect(read.data.usage.description).toBe(
120
+ 'A button. Set it up as `astryx docs acme-setup` says; theme it with `astryx docs theme`.',
121
+ );
122
+ expect(read.data.usage.bestPractices[0].description).toBe(
123
+ 'Pair it with `astryx component AcmeButton --package @acme/widgets`.',
124
+ );
125
+ // Code ticks and code fields show the syntax as written.
126
+ expect(read.data.props[0].description).toBe(
127
+ 'Read `astryx docs acme-setup`; write `{@link generic:acme-setup}` to link.',
128
+ );
129
+ expect(read.data.props[0].default).toBe("'{@link generic:acme-setup}'");
130
+ expect(read.data.examples[0]).toEqual({
131
+ label: 'As `astryx docs acme-setup` shows',
132
+ code: '// {@link generic:acme-setup}',
133
+ });
134
+ },
135
+ SLOW,
136
+ );
137
+
138
+ it(
139
+ 'reads the same in `astryx discover <package>/<Component>`',
140
+ async () => {
141
+ scaffold();
142
+ const json = await runCli(
143
+ ['--json', 'discover', '@acme/widgets/AcmeButton'],
144
+ tmpDir,
145
+ );
146
+ expect(json.status).toBe(0);
147
+ expect(JSON.parse(json.stdout).data.usage.description).toBe(
148
+ 'A button. Set it up as `astryx docs acme-setup` says; theme it with `astryx docs theme`.',
149
+ );
150
+ },
151
+ SLOW,
152
+ );
153
+
154
+ it(
155
+ "reads the same in a search hit's description",
156
+ async () => {
157
+ scaffold();
158
+ const found = /** @type {any} */ (
159
+ await search('AcmeButton', {cwd: tmpDir, type: 'component'})
160
+ );
161
+ const hit = found.data.results.find(
162
+ (/** @type {any} */ result) => result.name === 'AcmeButton',
163
+ );
164
+ expect(hit.description).toBe(
165
+ 'A button. Set it up as `astryx docs acme-setup` says; theme it with `astryx docs theme`.',
166
+ );
167
+ },
168
+ SLOW,
169
+ );
170
+
171
+ it(
172
+ 'reads the same in the props projection, in JSON, and in text',
173
+ async () => {
174
+ scaffold();
175
+ const props = /** @type {any} */ (
176
+ await component('AcmeButton', {cwd: tmpDir, props: true})
177
+ );
178
+ expect(props.data[0].description).toBe(
179
+ 'Read `astryx docs acme-setup`; write `{@link generic:acme-setup}` to link.',
180
+ );
181
+
182
+ const json = await runCli(['--json', 'component', 'AcmeButton'], tmpDir);
183
+ expect(json.status).toBe(0);
184
+ expect(JSON.parse(json.stdout).data.usage.description).toContain(
185
+ '`astryx docs acme-setup`',
186
+ );
187
+
188
+ const text = await runCli(['component', 'AcmeButton'], tmpDir);
189
+ expect(text.status).toBe(0);
190
+ expect(text.stdout).toContain('`astryx docs acme-setup`');
191
+ expect(text.stdout).not.toContain('{@link generic:acme-setup} says');
192
+ },
193
+ SLOW,
194
+ );
195
+
196
+ it("resolves a Core component's links against the CLI's own docs", async () => {
197
+ const read = await linkComponentResponse(
198
+ {
199
+ type: 'component.detail',
200
+ package: CORE_PACKAGE,
201
+ data: {
202
+ usage: {description: 'Theme it with {@link generic:theme}.'},
203
+ props: [
204
+ {
205
+ name: 'x',
206
+ type: 'string',
207
+ description: 'See {@link generic:nope}.',
208
+ },
209
+ ],
210
+ },
211
+ },
212
+ tmpDir,
213
+ );
214
+ expect(read.data.usage.description).toBe(
215
+ 'Theme it with `astryx docs theme`.',
216
+ );
217
+ // A link that names no doc prints as written.
218
+ expect(read.data.props[0].description).toBe('See {@link generic:nope}.');
219
+ });
220
+ });
221
+
222
+ describe("a link in a topic's description", () => {
223
+ it(
224
+ 'reads as the command that opens its doc, in the detail and the list',
225
+ async () => {
226
+ scaffold();
227
+ const detail = /** @type {any} */ (
228
+ await docs('acme-setup', undefined, {cwd: tmpDir, full: true})
229
+ );
230
+ expect(detail.data.description).toBe(
231
+ 'Set up Acme, then theme it as `astryx docs theme` says.',
232
+ );
233
+ const listed = /** @type {any} */ (
234
+ await docs(undefined, undefined, {cwd: tmpDir})
235
+ );
236
+ expect(
237
+ listed.data.find((/** @type {any} */ t) => t.topic === 'acme-setup')
238
+ .description,
239
+ ).toBe('Set up Acme, then theme it as `astryx docs theme` says.');
240
+ },
241
+ SLOW,
242
+ );
243
+ });
244
+
245
+ describe("a link in a guide's description, as the docs tree shows it", () => {
246
+ it(
247
+ 'reads as the command that opens its doc in the guide and its namespace',
248
+ async () => {
249
+ const expected =
250
+ 'Document a theme by extending the core theme topic, so an app reads it in `astryx docs theme`.';
251
+ const guide = /** @type {any} */ (
252
+ await docs(
253
+ 'cli/integrations/building-blocks/themes/document-the-theme',
254
+ undefined,
255
+ {cwd: tmpDir, full: true},
256
+ )
257
+ );
258
+ expect(guide.data.description).toBe(expected);
259
+ const namespace = /** @type {any} */ (
260
+ await docs('cli/integrations/building-blocks/themes', undefined, {
261
+ cwd: tmpDir,
262
+ })
263
+ );
264
+ const child = namespace.data.slots
265
+ .flatMap((/** @type {any} */ slot) => slot.children)
266
+ .find((/** @type {any} */ each) => each.name === 'document-the-theme');
267
+ expect(child.summary).toBe(expected);
268
+ },
269
+ SLOW,
270
+ );
271
+ });
272
+
273
+ describe("a reference block's summary of another package's component", () => {
274
+ it(
275
+ "resolves the summary's links against the component's own package",
276
+ async () => {
277
+ const write = (
278
+ /** @type {string} */ file,
279
+ /** @type {string} */ text,
280
+ ) => {
281
+ fs.mkdirSync(path.dirname(path.join(tmpDir, file)), {recursive: true});
282
+ fs.writeFileSync(path.join(tmpDir, file), text);
283
+ };
284
+ const doc = (/** @type {object} */ value) =>
285
+ `export default ${JSON.stringify(value)};\n`;
286
+ write('package.json', JSON.stringify({name: 'consumer'}));
287
+ write(
288
+ 'astryx.config.mjs',
289
+ "export default {integrations: ['@acme/widgets', '@acme/other']};\n",
290
+ );
291
+ const widgets = 'node_modules/@acme/widgets';
292
+ write(
293
+ `${widgets}/package.json`,
294
+ JSON.stringify({name: '@acme/widgets', version: '1.0.0'}),
295
+ );
296
+ write(
297
+ `${widgets}/astryx.integration.mjs`,
298
+ "export default {docs: './docs'};\n",
299
+ );
300
+ write(
301
+ `${widgets}/docs/acme-ref.doc.mjs`,
302
+ doc({
303
+ type: 'generic',
304
+ name: 'acme-ref',
305
+ title: 'Acme ref',
306
+ description: 'Where the card is documented.',
307
+ sections: [
308
+ {
309
+ title: 'Overview',
310
+ content: [
311
+ {
312
+ type: 'reference',
313
+ target: '@acme/other:component:OtherCardBody',
314
+ },
315
+ ],
316
+ },
317
+ ],
318
+ }),
319
+ );
320
+ const other = 'node_modules/@acme/other';
321
+ write(
322
+ `${other}/package.json`,
323
+ JSON.stringify({name: '@acme/other', version: '1.0.0'}),
324
+ );
325
+ write(
326
+ `${other}/astryx.integration.mjs`,
327
+ "export default {components: './components', docs: './docs'};\n",
328
+ );
329
+ write(
330
+ `${other}/docs/other-setup.doc.mjs`,
331
+ doc({
332
+ type: 'generic',
333
+ name: 'other-setup',
334
+ title: 'Other setup',
335
+ description: 'Set up Other.',
336
+ sections: [
337
+ {
338
+ title: 'Overview',
339
+ content: [{type: 'prose', text: 'Install it.'}],
340
+ },
341
+ ],
342
+ }),
343
+ );
344
+ write(
345
+ `${other}/components/OtherCard.doc.mjs`,
346
+ doc({
347
+ type: 'component',
348
+ name: 'OtherCard',
349
+ displayName: 'Other Card',
350
+ usage: {description: 'A card.'},
351
+ props: [],
352
+ }),
353
+ );
354
+ write(
355
+ `${other}/components/OtherCard.tsx`,
356
+ 'export function OtherCard() { return null; }\n',
357
+ );
358
+ write(
359
+ `${other}/components/OtherCardBody.doc.mjs`,
360
+ doc({
361
+ type: 'component',
362
+ name: 'OtherCardBody',
363
+ displayName: 'Other Card Body',
364
+ subComponentOf: 'OtherCard',
365
+ description: 'Its body, set up as {@link generic:other-setup} says.',
366
+ props: [],
367
+ }),
368
+ );
369
+ write(
370
+ `${other}/components/OtherCardBody.tsx`,
371
+ 'export function OtherCardBody() { return null; }\n',
372
+ );
373
+
374
+ const read = /** @type {any} */ (
375
+ await docs('acme-ref', undefined, {cwd: tmpDir, full: true})
376
+ );
377
+
378
+ // `other-setup` is @acme/other's topic: from @acme/widgets, the
379
+ // linking package, the same link names no doc.
380
+ expect(read.data.sections[0].content[0].text).toContain(
381
+ 'Its body, set up as `astryx docs other-setup` says.',
382
+ );
383
+ },
384
+ SLOW,
385
+ );
386
+ });