@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
@@ -81,12 +81,38 @@ const NON_DESCRIPTOR_DIRS = new Set([
81
81
  export async function collectDocInputs(project) {
82
82
  const list = new InputList();
83
83
 
84
+ // A component documented as a member of its family's doc (HStack and VStack
85
+ // in Stack's doc) is read through the family's own entry: one descriptor,
86
+ // one reader. Own docs go first so the family keeps its name.
87
+ /** @type {Array<{name: string, package: string, docPath: string}>} */
88
+ const ownDocs = [];
89
+ /** @type {Array<{name: string, package: string, docPath: string}>} */
90
+ const familyDocs = [];
84
91
  for (const record of await project.components()) {
85
92
  if (record.docPath == null) continue;
93
+ const stem = path
94
+ .basename(record.docPath)
95
+ .replace(/\.doc\.[cm]?[jt]s$/u, '')
96
+ .replace(/^XDS/u, '');
97
+ const entry = {
98
+ name: record.name,
99
+ package: record.package,
100
+ docPath: record.docPath,
101
+ };
102
+ (stem === record.name ? ownDocs : familyDocs).push(entry);
103
+ }
104
+ for (const record of ownDocs) {
105
+ list.add('components', record.name, record.package, record.docPath);
106
+ }
107
+ for (const record of familyDocs) {
108
+ if (list.claims(record.docPath)) continue;
86
109
  list.add('components', record.name, record.package, record.docPath);
87
110
  }
88
111
 
89
- const coreDir = findCoreDir(project.cwd);
112
+ const coreDir =
113
+ typeof project.coreDir === 'function'
114
+ ? project.coreDir()
115
+ : findCoreDir(project.cwd);
90
116
  if (coreDir != null) {
91
117
  for (const name of Object.values(discoverHooks(coreDir)).flat()) {
92
118
  const file = findHookDoc(coreDir, name);
@@ -91,7 +91,6 @@ describe('collectDocInputs over what this repo ships', () => {
91
91
  'Chat',
92
92
  'ChatComposerTokenElement',
93
93
  'ContextMenuItem',
94
- 'Heading',
95
94
  'Indicator',
96
95
  'Layer',
97
96
  'NavMenu',
@@ -13,10 +13,12 @@
13
13
  * @typedef {object} DocLink
14
14
  * @property {string} target the target as written: `[<provider>:]<kind>:<name>`
15
15
  * @property {string} id the doc's identity (a DocId)
16
- * @property {string} route what `astryx docs <route>` opens
16
+ * @property {string | null} route what `astryx docs <route>` opens; null for a
17
+ * component, page, or block doc, which no docs route opens
17
18
  * @property {string} title
18
19
  * @property {string} summary
19
- * @property {string} command the command that opens it, `astryx docs <route>`
20
+ * @property {string} command the command that opens it: `astryx docs <route>`,
21
+ * or `astryx component` or `astryx template` for a component, page, or block
20
22
  */
21
23
  /**
22
24
  * Finds the doc a target names, or says why none matches.
@@ -27,6 +29,8 @@
27
29
  * @property {string} target the target as written
28
30
  * @property {string} message
29
31
  * @property {string} [section] the key of the section the link sits in
32
+ * @property {string} [field] the field the link sits in, outside any section:
33
+ * `description`, or a path such as `props[2].description`
30
34
  * @property {true} [include] the problem is in a `reference` block, which
31
35
  * includes content rather than linking to it: a reader loses that content,
32
36
  * so the authoring check fails on it
@@ -53,6 +57,31 @@ export function parseLinkTarget(target: unknown): LinkTarget | {
53
57
  * @returns {string}
54
58
  */
55
59
  export function unlinkText(text: string): string;
60
+ /**
61
+ * Every text field of a doc object, in authored order, by the same rules
62
+ * {@link linkFields} resolves links by: the strings a reader reads, never a
63
+ * name, type, default, or code.
64
+ * @param {unknown} doc
65
+ * @returns {{field: string, text: string}[]}
66
+ */
67
+ export function textFields(doc: unknown): {
68
+ field: string;
69
+ text: string;
70
+ }[];
71
+ /**
72
+ * Resolve every inline link in the text fields of a doc object, such as a
73
+ * component doc or a topic's summary fields (spec:AST-047 FR9): its
74
+ * description, its usage and best practices, each prop's and example's
75
+ * description, at any depth. Code and code ticks show link syntax as written.
76
+ * @template T
77
+ * @param {T} doc
78
+ * @param {LinkResolver} resolve
79
+ * @returns {Promise<{doc: T, problems: LinkProblem[]}>}
80
+ */
81
+ export function linkFields<T>(doc: T, resolve: LinkResolver): Promise<{
82
+ doc: T;
83
+ problems: LinkProblem[];
84
+ }>;
56
85
  /**
57
86
  * Resolve every link in a list of content blocks, in order.
58
87
  * @param {any[]} blocks
@@ -118,13 +147,15 @@ export type DocLink = {
118
147
  */
119
148
  id: string;
120
149
  /**
121
- * what `astryx docs <route>` opens
150
+ * what `astryx docs <route>` opens; null for a
151
+ * component, page, or block doc, which no docs route opens
122
152
  */
123
- route: string;
153
+ route: string | null;
124
154
  title: string;
125
155
  summary: string;
126
156
  /**
127
- * the command that opens it, `astryx docs <route>`
157
+ * the command that opens it: `astryx docs <route>`,
158
+ * or `astryx component` or `astryx template` for a component, page, or block
128
159
  */
129
160
  command: string;
130
161
  };
@@ -144,6 +175,11 @@ export type LinkProblem = {
144
175
  * the key of the section the link sits in
145
176
  */
146
177
  section?: string | undefined;
178
+ /**
179
+ * the field the link sits in, outside any section:
180
+ * `description`, or a path such as `props[2].description`
181
+ */
182
+ field?: string | undefined;
147
183
  /**
148
184
  * the problem is in a `reference` block, which
149
185
  * includes content rather than linking to it: a reader loses that content,
@@ -47,10 +47,12 @@ const CODE_SPAN = /(`[^`]*`)/;
47
47
  * @typedef {object} DocLink
48
48
  * @property {string} target the target as written: `[<provider>:]<kind>:<name>`
49
49
  * @property {string} id the doc's identity (a DocId)
50
- * @property {string} route what `astryx docs <route>` opens
50
+ * @property {string | null} route what `astryx docs <route>` opens; null for a
51
+ * component, page, or block doc, which no docs route opens
51
52
  * @property {string} title
52
53
  * @property {string} summary
53
- * @property {string} command the command that opens it, `astryx docs <route>`
54
+ * @property {string} command the command that opens it: `astryx docs <route>`,
55
+ * or `astryx component` or `astryx template` for a component, page, or block
54
56
  */
55
57
 
56
58
  /**
@@ -63,6 +65,8 @@ const CODE_SPAN = /(`[^`]*`)/;
63
65
  * @property {string} target the target as written
64
66
  * @property {string} message
65
67
  * @property {string} [section] the key of the section the link sits in
68
+ * @property {string} [field] the field the link sits in, outside any section:
69
+ * `description`, or a path such as `props[2].description`
66
70
  * @property {true} [include] the problem is in a `reference` block, which
67
71
  * includes content rather than linking to it: a reader loses that content,
68
72
  * so the authoring check fails on it
@@ -89,7 +93,11 @@ export function parseLinkTarget(target) {
89
93
  };
90
94
  }
91
95
  const parts = target.trim().split(':');
92
- if (parts.length < 2 || parts.length > 3 || parts.some(p => p.trim() === '')) {
96
+ if (
97
+ parts.length < 2 ||
98
+ parts.length > 3 ||
99
+ parts.some(p => p.trim() === '')
100
+ ) {
93
101
  return {error: `"${target}" is not \`[<provider>:]<kind>:<name>\``};
94
102
  }
95
103
  const provider = parts.length === 3 ? parts[0].trim() : null;
@@ -125,6 +133,159 @@ export function unlinkText(text) {
125
133
  .join('');
126
134
  }
127
135
 
136
+ /**
137
+ * Text with each inline link replaced by the command that opens its doc. A
138
+ * link written inside code ticks is shown as written: it documents the syntax,
139
+ * it does not link. A link that names no doc prints as written: text that was
140
+ * never meant as a link reads the way it did, and doctor names the problem.
141
+ * @param {unknown} text
142
+ * @param {(target: string) => Promise<DocLink | null>} find
143
+ * @returns {Promise<any>}
144
+ */
145
+ async function resolveInline(text, find) {
146
+ if (typeof text !== 'string' || !text.includes('{@link')) return text;
147
+ let out = '';
148
+ for (const part of text.split(CODE_SPAN)) {
149
+ if (part.startsWith('`')) {
150
+ out += part;
151
+ continue;
152
+ }
153
+ let last = 0;
154
+ for (const match of part.matchAll(INLINE_LINK)) {
155
+ const link = await find(match[1]);
156
+ const at = /** @type {number} */ (match.index);
157
+ out += part.slice(last, at);
158
+ out += link ? `\`${link.command}\`` : match[0];
159
+ last = at + match[0].length;
160
+ }
161
+ out += part.slice(last);
162
+ }
163
+ return out;
164
+ }
165
+
166
+ /**
167
+ * The fields of a doc that hold text a reader reads, where a link resolves:
168
+ * a description or summary, an example's label, and each value of a
169
+ * `propDescriptions` overlay. Every other string is a name, a type, a
170
+ * default, or code, and is shown as written.
171
+ */
172
+ const TEXT_FIELDS = new Set(['description', 'summary', 'label']);
173
+
174
+ /** Fields that hold code or data, never text: nothing below them links. */
175
+ const CODE_FIELDS = new Set([
176
+ 'code',
177
+ 'playground',
178
+ 'slotElements',
179
+ 'registry',
180
+ 'keywords',
181
+ ]);
182
+
183
+ /**
184
+ * Every text field of a doc object, in authored order, by the same rules
185
+ * {@link linkFields} resolves links by: the strings a reader reads, never a
186
+ * name, type, default, or code.
187
+ * @param {unknown} doc
188
+ * @returns {{field: string, text: string}[]}
189
+ */
190
+ export function textFields(doc) {
191
+ /** @type {{field: string, text: string}[]} */
192
+ const out = [];
193
+ /**
194
+ * @param {unknown} value
195
+ * @param {string} at
196
+ * @param {boolean} text
197
+ */
198
+ const walk = (value, at, text) => {
199
+ if (typeof value === 'string') {
200
+ if (text) out.push({field: at, text: value});
201
+ return;
202
+ }
203
+ if (Array.isArray(value)) {
204
+ value.forEach((item, i) => walk(item, `${at}[${i}]`, false));
205
+ return;
206
+ }
207
+ if (value == null || typeof value !== 'object') return;
208
+ for (const [key, field] of Object.entries(value)) {
209
+ const path = at === '' ? key : `${at}.${key}`;
210
+ if (CODE_FIELDS.has(key)) continue;
211
+ if (key === 'propDescriptions' && field && typeof field === 'object') {
212
+ for (const [prop, description] of Object.entries(field)) {
213
+ walk(description, `${path}.${prop}`, true);
214
+ }
215
+ } else {
216
+ walk(field, path, TEXT_FIELDS.has(key));
217
+ }
218
+ }
219
+ };
220
+ walk(doc, '', false);
221
+ return out;
222
+ }
223
+
224
+ /**
225
+ * Resolve every inline link in the text fields of a doc object, such as a
226
+ * component doc or a topic's summary fields (spec:AST-047 FR9): its
227
+ * description, its usage and best practices, each prop's and example's
228
+ * description, at any depth. Code and code ticks show link syntax as written.
229
+ * @template T
230
+ * @param {T} doc
231
+ * @param {LinkResolver} resolve
232
+ * @returns {Promise<{doc: T, problems: LinkProblem[]}>}
233
+ */
234
+ export async function linkFields(doc, resolve) {
235
+ /** @type {LinkProblem[]} */
236
+ const problems = [];
237
+ /**
238
+ * @param {unknown} value
239
+ * @param {string} at the field path, e.g. `usage.bestPractices[0].description`
240
+ * @param {boolean} text whether a string here is text
241
+ * @returns {Promise<any>}
242
+ */
243
+ const walk = async (value, at, text) => {
244
+ if (typeof value === 'string') {
245
+ if (!text) return value;
246
+ return resolveInline(value, async target => {
247
+ const found = await resolve(target);
248
+ if ('problem' in found) {
249
+ problems.push({target, message: found.problem, field: at});
250
+ return null;
251
+ }
252
+ return found;
253
+ });
254
+ }
255
+ if (Array.isArray(value)) {
256
+ const out = [];
257
+ for (let i = 0; i < value.length; i++) {
258
+ out.push(await walk(value[i], `${at}[${i}]`, false));
259
+ }
260
+ return out;
261
+ }
262
+ if (value == null || typeof value !== 'object') return value;
263
+ /** @type {Record<string, unknown>} */
264
+ const out = {};
265
+ for (const [key, field] of Object.entries(value)) {
266
+ const path = at === '' ? key : `${at}.${key}`;
267
+ if (CODE_FIELDS.has(key)) {
268
+ out[key] = field;
269
+ } else if (
270
+ key === 'propDescriptions' &&
271
+ field &&
272
+ typeof field === 'object'
273
+ ) {
274
+ /** @type {Record<string, unknown>} */
275
+ const described = {};
276
+ for (const [prop, description] of Object.entries(field)) {
277
+ described[prop] = await walk(description, `${path}.${prop}`, true);
278
+ }
279
+ out[key] = described;
280
+ } else {
281
+ out[key] = await walk(field, path, TEXT_FIELDS.has(key));
282
+ }
283
+ }
284
+ return out;
285
+ };
286
+ return {doc: await walk(doc, '', false), problems};
287
+ }
288
+
128
289
  /**
129
290
  * Resolve every link in a list of content blocks, in order.
130
291
  * @param {any[]} blocks
@@ -154,30 +315,7 @@ export async function linkBlocks(blocks, resolve, at = {}, include) {
154
315
  * @param {unknown} text
155
316
  * @returns {Promise<any>}
156
317
  */
157
- const linkText = async text => {
158
- if (typeof text !== 'string' || !text.includes('{@link')) return text;
159
- let out = '';
160
- // A link written inside code ticks is shown as written: it documents the
161
- // syntax, it does not link.
162
- for (const part of text.split(CODE_SPAN)) {
163
- if (part.startsWith('`')) {
164
- out += part;
165
- continue;
166
- }
167
- let last = 0;
168
- for (const match of part.matchAll(INLINE_LINK)) {
169
- const link = await find(match[1]);
170
- const at = /** @type {number} */ (match.index);
171
- out += part.slice(last, at);
172
- // A link that names no doc prints as written: text that was never
173
- // meant as a link reads the way it did, and doctor names the problem.
174
- out += link ? `\`${link.command}\`` : match[0];
175
- last = at + match[0].length;
176
- }
177
- out += part.slice(last);
178
- }
179
- return out;
180
- };
318
+ const linkText = text => resolveInline(text, find);
181
319
  /**
182
320
  * @param {unknown[]} values
183
321
  * @returns {Promise<any[]>}
@@ -249,7 +387,8 @@ export async function linkBlocks(blocks, resolve, at = {}, include) {
249
387
  }
250
388
  if (Array.isArray(step.references)) {
251
389
  const links = [];
252
- for (const target of step.references) links.push(await find(target));
390
+ for (const target of step.references)
391
+ links.push(await find(target));
253
392
  linked.links = links;
254
393
  }
255
394
  steps.push(linked);
@@ -8,8 +8,10 @@
8
8
  import {describe, expect, it} from 'vitest';
9
9
  import {
10
10
  linkBlocks,
11
+ linkFields,
11
12
  linkSections,
12
13
  parseLinkTarget,
14
+ textFields,
13
15
  unlinkText,
14
16
  } from './links.mjs';
15
17
 
@@ -58,14 +60,20 @@ describe('linkBlocks', () => {
58
60
  const {content, problems} = await linkBlocks(
59
61
  [
60
62
  {type: 'prose', text: 'Check it with {@link command:doctor}.'},
61
- {type: 'list', style: 'unordered', items: ['See {@link @acme/kit:generic:setup}']},
63
+ {
64
+ type: 'list',
65
+ style: 'unordered',
66
+ items: ['See {@link @acme/kit:generic:setup}'],
67
+ },
62
68
  {type: 'table', headers: ['Doc'], rows: [['{@link command:doctor}']]},
63
69
  {type: 'code', lang: 'js', code: '{@link command:doctor}'},
64
70
  ],
65
71
  resolve,
66
72
  );
67
73
  expect(problems).toEqual([]);
68
- expect(content[0].text).toBe('Check it with `astryx docs cli/commands/doctor`.');
74
+ expect(content[0].text).toBe(
75
+ 'Check it with `astryx docs cli/commands/doctor`.',
76
+ );
69
77
  expect(content[1].items).toEqual(['See `astryx docs acme/setup`']);
70
78
  expect(content[2].rows).toEqual([['`astryx docs cli/commands/doctor`']]);
71
79
  // Code is an example: it is shown as written.
@@ -74,7 +82,12 @@ describe('linkBlocks', () => {
74
82
 
75
83
  it('shows a link written in code ticks as written', async () => {
76
84
  const {content, problems} = await linkBlocks(
77
- [{type: 'prose', text: 'Write `{@link <target>}` to link; {@link command:doctor} checks it.'}],
85
+ [
86
+ {
87
+ type: 'prose',
88
+ text: 'Write `{@link <target>}` to link; {@link command:doctor} checks it.',
89
+ },
90
+ ],
78
91
  resolve,
79
92
  );
80
93
  expect(problems).toEqual([]);
@@ -172,7 +185,10 @@ describe('linkBlocks', () => {
172
185
  },
173
186
  ],
174
187
  resolve,
175
- ).then(({sections, problems}) => ({content: sections[0].content, problems}));
188
+ ).then(({sections, problems}) => ({
189
+ content: sections[0].content,
190
+ problems,
191
+ }));
176
192
  expect(content[0].text).toBe('See {@link command:nope}.');
177
193
  expect(content[1].link).toBeNull();
178
194
  expect(problems.map(p => [p.section, p.target])).toEqual([
@@ -190,3 +206,80 @@ describe('unlinkText', () => {
190
206
  );
191
207
  });
192
208
  });
209
+
210
+ describe('linkFields', () => {
211
+ /** A component doc with a link in every kind of text field, and in code. */
212
+ const DOC = {
213
+ name: 'AcmeButton',
214
+ description: 'Run {@link command:doctor} first.',
215
+ usage: {
216
+ description: 'Set it up: {@link @acme/kit:generic:setup}.',
217
+ bestPractices: [
218
+ {guidance: true, description: 'Check with {@link command:doctor}.'},
219
+ ],
220
+ },
221
+ props: [
222
+ {
223
+ name: 'tone',
224
+ type: '{@link command:doctor}',
225
+ default: "'{@link command:doctor}'",
226
+ description: 'Like `{@link command:doctor}`, or {@link generic:nope}.',
227
+ },
228
+ ],
229
+ propDescriptions: {tone: 'See {@link command:doctor}.'},
230
+ examples: [
231
+ {label: 'From {@link command:doctor}', code: '{@link command:doctor}'},
232
+ ],
233
+ playground: {defaults: {label: '{@link command:doctor}'}},
234
+ };
235
+
236
+ it('resolves each text field and leaves names, types, defaults, and code as written', async () => {
237
+ const {doc, problems} = await linkFields(DOC, resolve);
238
+ expect(doc).toEqual({
239
+ ...DOC,
240
+ description: 'Run `astryx docs cli/commands/doctor` first.',
241
+ usage: {
242
+ description: 'Set it up: `astryx docs acme/setup`.',
243
+ bestPractices: [
244
+ {
245
+ guidance: true,
246
+ description: 'Check with `astryx docs cli/commands/doctor`.',
247
+ },
248
+ ],
249
+ },
250
+ props: [
251
+ {
252
+ ...DOC.props[0],
253
+ description:
254
+ 'Like `{@link command:doctor}`, or {@link generic:nope}.',
255
+ },
256
+ ],
257
+ propDescriptions: {tone: 'See `astryx docs cli/commands/doctor`.'},
258
+ examples: [
259
+ {
260
+ label: 'From `astryx docs cli/commands/doctor`',
261
+ code: '{@link command:doctor}',
262
+ },
263
+ ],
264
+ });
265
+ expect(problems).toEqual([
266
+ {
267
+ target: 'generic:nope',
268
+ message: expect.any(String),
269
+ field: 'props[0].description',
270
+ },
271
+ ]);
272
+ });
273
+
274
+ it('names the same text fields textFields reads', async () => {
275
+ const {problems} = await linkFields(DOC, async target => ({
276
+ problem: `no ${target}`,
277
+ }));
278
+ const fields = textFields(DOC)
279
+ .filter(({text}) => text.replace(/`[^`]*`/g, '').includes('{@link'))
280
+ .map(({field}) => field);
281
+ expect([...new Set(problems.map(problem => problem.field))]).toEqual(
282
+ fields,
283
+ );
284
+ });
285
+ });
@@ -102,7 +102,7 @@ export type ReadOptions = {
102
102
  * how to import the module (default
103
103
  * `user`)
104
104
  */
105
- loader?: "template" | "native" | "user" | undefined;
105
+ loader?: "template" | "user" | "native" | undefined;
106
106
  /**
107
107
  * which exports name the doc, in
108
108
  * precedence order, for a reader that has always read a narrower set than its
@@ -242,7 +242,8 @@ export function buildDocsTree({namespaces, docs, topics = []}) {
242
242
  // without case, as topic names do, so `CLI` claims `cli`.
243
243
  const cliRoutes = new Set(topics.length > 0 ? [UNORGANIZED] : []);
244
244
  for (const topic of topics) {
245
- if (topic.provider === CLI_PROVIDER) cliRoutes.add(topic.name.toLowerCase());
245
+ if (topic.provider === CLI_PROVIDER)
246
+ cliRoutes.add(topic.name.toLowerCase());
246
247
  }
247
248
  // Every other name a topic answers to (the topics it replaced, directly or
248
249
  // through a chain) is that topic's route too: `astryx docs <name>` opens the
@@ -375,6 +376,34 @@ export function buildDocsTree({namespaces, docs, topics = []}) {
375
376
  return false;
376
377
  }
377
378
  const taken = byFoldedRoute.get(folded);
379
+ if (
380
+ taken &&
381
+ taken.provider === CLI_PROVIDER &&
382
+ node.provider !== CLI_PROVIDER &&
383
+ !node.generated
384
+ ) {
385
+ // Astryx's own doc keeps its route: name both docs as an author knows
386
+ // them, never by their ids, and say what to change.
387
+ const what = (/** @type {TreeNode} */ n) =>
388
+ n.kind === 'namespace'
389
+ ? 'namespace'
390
+ : n.kind === 'generic'
391
+ ? 'topic'
392
+ : `${n.kind} doc`;
393
+ const scope = String(node.provider).split('/').pop() ?? '';
394
+ const suggestion = `${
395
+ scope
396
+ .replace(/[^a-z0-9]+/gi, '-')
397
+ .replace(/^-|-$/g, '')
398
+ .toLowerCase() || 'my'
399
+ }-${node.name.toLowerCase()}`;
400
+ report(
401
+ 'duplicate_route',
402
+ at,
403
+ `The ${what(node)} "${node.name}" from ${node.provider} and Astryx's own ${what(taken)} "${taken.name}" both have the route "${node.route}", so \`astryx docs ${node.route}\` cannot open both. Fix: rename "${node.name}" (its \`name\` and its file), such as \`name: '${suggestion}'\`${node.route.includes('/') ? ', or place it elsewhere' : ''}.`,
404
+ );
405
+ return false;
406
+ }
378
407
  if (taken) {
379
408
  report(
380
409
  'duplicate_route',
@@ -171,6 +171,13 @@ describe('buildDocsTree', () => {
171
171
  ['duplicate_route', expect.stringContaining('takes the route "UNORGANIZED", which the CLI\'s own docs keep')],
172
172
  ]),
173
173
  );
174
+ // It names both docs as their authors know them, never by artifact id,
175
+ // and says what to change.
176
+ const taken = problems(tree).find(([, message]) => message.includes('route "CLI"'))?.[1];
177
+ expect(taken).toBe(
178
+ `The topic "CLI" from ${P} and Astryx's own namespace "cli" both have the route "CLI", so \`astryx docs CLI\` cannot open both. Fix: rename "CLI" (its \`name\` and its file), such as \`name: 'kit-cli'\`.`,
179
+ );
180
+ expect(taken).not.toContain('astryx:artifact');
174
181
  expect(tree.getFolded('THEME')?.provider).toBe(CLI);
175
182
  // A CLI topic whose own name has capitals keeps its route against a
176
183
  // lowercase namespace, although namespaces go into the tree first.
@@ -495,7 +502,7 @@ describe("the CLI's own docs tree", () => {
495
502
  async () => {
496
503
  const tree = await loadDocsTree({fresh: true});
497
504
  expect(tree.diagnostics).toEqual([]);
498
- expect(tree.roots().map(node => node.route)).toEqual(['cli', 'internationalization', 'layout', 'migration', 'styling', 'styling-libraries', 'tokens', 'typography']);
505
+ expect(tree.roots().map(node => node.route)).toEqual(['cli', 'global', 'internationalization', 'layout', 'migration', 'styling', 'styling-libraries', 'tokens', 'typography']);
499
506
  // Every slot child is a node one level below the namespace that places
500
507
  // it, and every node but the root is placed. Read from the tree, so a
501
508
  // restructure of the guides does not need this test edited.
@@ -0,0 +1,83 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * Run `fn` with `command` as the door printed commands start with. The slot a
6
+ * host fills for one call. An integration's `cliCommand` still comes first,
7
+ * and conflicting ones still fall back to the guess.
8
+ * @template T
9
+ * @param {string} command e.g. `astryx` for a global launcher
10
+ * @param {() => T} fn
11
+ * @returns {T}
12
+ */
13
+ export function runWithCliDoor<T>(command: string, fn: () => T): T;
14
+ /**
15
+ * The door of the current call, if a host set one.
16
+ * @returns {string | undefined}
17
+ */
18
+ export function cliDoor(): string | undefined;
19
+ /**
20
+ * The commands a set of loaded integrations declares, in load order. An
21
+ * integration that failed to load, or was set aside for a provider conflict,
22
+ * contributes nothing, so it declares nothing either.
23
+ * @param {ReadonlyArray<{name?: string, __spec?: string, cliCommand?: string, __loadError?: string, __providerConflict?: unknown}>} integrations
24
+ * @returns {DeclaredCliCommand[]}
25
+ */
26
+ export function declaredCliCommandsOf(integrations: ReadonlyArray<{
27
+ name?: string;
28
+ __spec?: string;
29
+ cliCommand?: string;
30
+ __loadError?: string;
31
+ __providerConflict?: unknown;
32
+ }>): DeclaredCliCommand[];
33
+ /**
34
+ * Choose among declarations: one command, declared by one or more
35
+ * integrations, is used; different commands are a conflict.
36
+ * @param {DeclaredCliCommand[]} declared
37
+ * @returns {{command?: string, conflict: boolean, declared: DeclaredCliCommand[]}}
38
+ */
39
+ export function chooseDeclaredCliCommand(declared: DeclaredCliCommand[]): {
40
+ command?: string;
41
+ conflict: boolean;
42
+ declared: DeclaredCliCommand[];
43
+ };
44
+ /**
45
+ * Record the commands a project's loaded integrations declare. Called by
46
+ * `Project.load` each time it resolves the integration set; the latest load
47
+ * wins.
48
+ * @param {string} projectDir
49
+ * @param {Parameters<typeof declaredCliCommandsOf>[0]} integrations
50
+ * @returns {void}
51
+ */
52
+ export function recordDeclaredCliCommands(projectDir: string, integrations: Parameters<typeof declaredCliCommandsOf>[0]): void;
53
+ /**
54
+ * Whether this process has loaded the project at `targetDir`.
55
+ * @param {string} [targetDir=process.cwd()]
56
+ * @returns {boolean}
57
+ */
58
+ export function hasRecordedProject(targetDir?: string): boolean;
59
+ /**
60
+ * What the integrations of the project at `targetDir` declare, as recorded by
61
+ * its last `Project.load` in this process; see
62
+ * {@link chooseDeclaredCliCommand}.
63
+ * @param {string} [targetDir=process.cwd()]
64
+ * @returns {{command?: string, conflict: boolean, declared: DeclaredCliCommand[]}}
65
+ */
66
+ export function declaredCliCommand(targetDir?: string): {
67
+ command?: string;
68
+ conflict: boolean;
69
+ declared: DeclaredCliCommand[];
70
+ };
71
+ /**
72
+ * One integration's declared command.
73
+ */
74
+ export type DeclaredCliCommand = {
75
+ /**
76
+ * the integration package that declared it
77
+ */
78
+ package: string;
79
+ /**
80
+ * its `cliCommand` value
81
+ */
82
+ command: string;
83
+ };