@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
@@ -0,0 +1,40 @@
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
+ * @file Where a runner may stand in front of an Astryx command in Astryx's own
6
+ * docs. Docs write commands bare (`astryx <command>`): the CLI prefixes each
7
+ * one with the reader's command when it prints a doc, and the docsite with
8
+ * `npx @astryxdesign/cli`. A few files show a runner on purpose, and some files
9
+ * are not docs at all. The rules, checked by test/runner-commands.test.mjs:
10
+ *
11
+ * - npx (or another download runner) with the unscoped name `astryx` is never
12
+ * written outside `skip`: it fetches an unrelated npm package.
13
+ * - In a doc (`*.doc.mjs`, and Core's docs.mjs), no runner precedes an Astryx
14
+ * command, except in `runnerExamples`, where `npx @astryxdesign/cli …` and a
15
+ * project's runner may (still never the unscoped name through npx).
16
+ *
17
+ * TODO(doctor-runner-check): the Doctor lane's integration-docs runner check
18
+ * (foundation/discovery/runner-commands.mjs) becomes the single rule; then the
19
+ * repo test runs that check over Core's and the CLI's own docs with this
20
+ * allowlist.
21
+ *
22
+ * @input nothing
23
+ * @output the allowlist
24
+ * @position packages/cli/foundation/discovery — data shared by the repo test
25
+ * and the Doctor runner check
26
+ */
27
+ export const RUNNER_COMMAND_ALLOWLIST: Readonly<{
28
+ /** Files never scanned: release notes, tests, and the helpers that build runner forms. */
29
+ skip: readonly RegExp[];
30
+ /** Docs that show a runner on purpose, and why. */
31
+ runnerExamples: Readonly<{
32
+ 'packages/core/docs.mjs': "Core's setup banner, for a project that has no CLI installed yet";
33
+ 'packages/cli/assets/docs/tree/updates.doc.mjs': "quotes the CLI's own update notice, which names the project's runner";
34
+ 'packages/cli/assets/docs/tree/cli-command.doc.mjs': "explains how printed commands start, the pinned `npx @astryxdesign/cli@<version>` among them";
35
+ 'packages/cli/assets/docs/tree/who-answers.doc.mjs': "explains how printed commands start, the pinned `npx @astryxdesign/cli@<version>` among them";
36
+ 'packages/cli/assets/docs/tree/project-global-or-both.doc.mjs': "compares running the CLI with npx to installing it, and is found by searching for that command";
37
+ 'packages/cli/assets/docs/tree/install-once.doc.mjs': "names a pinned release, `npx @astryxdesign/cli@<version>`, which answers as that version";
38
+ 'packages/cli/clients/cli/commands/upgrade.doc.mjs': "quotes the command upgrade prints when the CLI is older than Core, pinned to the matching release";
39
+ }>;
40
+ }>;
@@ -0,0 +1,53 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Where a runner may stand in front of an Astryx command in Astryx's own
5
+ * docs. Docs write commands bare (`astryx <command>`): the CLI prefixes each
6
+ * one with the reader's command when it prints a doc, and the docsite with
7
+ * `npx @astryxdesign/cli`. A few files show a runner on purpose, and some files
8
+ * are not docs at all. The rules, checked by test/runner-commands.test.mjs:
9
+ *
10
+ * - npx (or another download runner) with the unscoped name `astryx` is never
11
+ * written outside `skip`: it fetches an unrelated npm package.
12
+ * - In a doc (`*.doc.mjs`, and Core's docs.mjs), no runner precedes an Astryx
13
+ * command, except in `runnerExamples`, where `npx @astryxdesign/cli …` and a
14
+ * project's runner may (still never the unscoped name through npx).
15
+ *
16
+ * TODO(doctor-runner-check): the Doctor lane's integration-docs runner check
17
+ * (foundation/discovery/runner-commands.mjs) becomes the single rule; then the
18
+ * repo test runs that check over Core's and the CLI's own docs with this
19
+ * allowlist.
20
+ *
21
+ * @input nothing
22
+ * @output the allowlist
23
+ * @position packages/cli/foundation/discovery — data shared by the repo test
24
+ * and the Doctor runner check
25
+ */
26
+
27
+ export const RUNNER_COMMAND_ALLOWLIST = Object.freeze({
28
+ /** Files never scanned: release notes, tests, and the helpers that build runner forms. */
29
+ skip: Object.freeze([
30
+ /(^|\/)CHANGELOG\.md$/u,
31
+ /\.test\.[cm]?[jt]sx?$/u,
32
+ /(^|\/)__tests__\//u,
33
+ /^packages\/cli\/test\//u,
34
+ /^packages\/cli\/foundation\/env\/package-manager\.mjs$/u,
35
+ ]),
36
+ /** Docs that show a runner on purpose, and why. */
37
+ runnerExamples: Object.freeze({
38
+ 'packages/core/docs.mjs':
39
+ "Core's setup banner, for a project that has no CLI installed yet",
40
+ 'packages/cli/assets/docs/tree/updates.doc.mjs':
41
+ "quotes the CLI's own update notice, which names the project's runner",
42
+ 'packages/cli/assets/docs/tree/cli-command.doc.mjs':
43
+ 'explains how printed commands start, the pinned `npx @astryxdesign/cli@<version>` among them',
44
+ 'packages/cli/assets/docs/tree/who-answers.doc.mjs':
45
+ 'explains how printed commands start, the pinned `npx @astryxdesign/cli@<version>` among them',
46
+ 'packages/cli/assets/docs/tree/project-global-or-both.doc.mjs':
47
+ 'compares running the CLI with npx to installing it, and is found by searching for that command',
48
+ 'packages/cli/assets/docs/tree/install-once.doc.mjs':
49
+ 'names a pinned release, `npx @astryxdesign/cli@<version>`, which answers as that version',
50
+ 'packages/cli/clients/cli/commands/upgrade.doc.mjs':
51
+ 'quotes the command upgrade prints when the CLI is older than Core, pinned to the matching release',
52
+ }),
53
+ });
@@ -0,0 +1,52 @@
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
+ * The strings of a doc the CLI resolves links in (spec:AST-047 FR9). In a
6
+ * topic: its description and summary, then the text of prose and heading
7
+ * blocks, list items, and table cells in each section's content, with the
8
+ * section's title. In a component doc (any doc with no sections): every text
9
+ * field, such as its description, usage, best practices, and each prop's and
10
+ * example's description, with the field's path. Code is never read.
11
+ *
12
+ * @param {unknown} doc an authored doc object
13
+ * @returns {{text: string, section: string | null, field: string | null}[]}
14
+ */
15
+ export function linkableStrings(doc: unknown): {
16
+ text: string;
17
+ section: string | null;
18
+ field: string | null;
19
+ }[];
20
+ /**
21
+ * Every pointer span in one prose string, in order.
22
+ * @param {string} text
23
+ * @returns {string[]} the doc name each one points at
24
+ */
25
+ export function pointersIn(text: string): string[];
26
+ /**
27
+ * Each stray pointer in a package's topics and component docs.
28
+ *
29
+ * @param {{records: {name: string, path: string}[], guides?: {name: string, ref?: {topicFile?: string}}[], namespaces?: {doc?: {name?: string}}[], components?: {name: string, file: string}[]}} discovered
30
+ * @returns {Promise<import('../../api/integration/authoring-checks.type.mjs').StrayPointer[]>}
31
+ */
32
+ export function findStrayPointers(discovered: {
33
+ records: {
34
+ name: string;
35
+ path: string;
36
+ }[];
37
+ guides?: {
38
+ name: string;
39
+ ref?: {
40
+ topicFile?: string;
41
+ };
42
+ }[];
43
+ namespaces?: {
44
+ doc?: {
45
+ name?: string;
46
+ };
47
+ }[];
48
+ components?: {
49
+ name: string;
50
+ file: string;
51
+ }[];
52
+ }): Promise<import("../../api/integration/authoring-checks.type.mjs").StrayPointer[]>;
@@ -0,0 +1,293 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Stray doc pointers (architecture:cli-surface DEC-8): a code span in
5
+ * prose that is exactly `astryx docs <name>` for a doc that exists. The doc's
6
+ * `{@link}` prints the command that opens the doc and is checked by doctor's
7
+ * link check, so a written command is a pointer nothing checks.
8
+ *
9
+ * @input the docs a package ships, as discoverIntegrationDocs returns them
10
+ * @output each stray pointer, with the link that replaces it
11
+ * @position foundation/discovery, called through api/integration/_adapter.mjs
12
+ */
13
+
14
+ import fs from 'node:fs';
15
+
16
+ import {textFields} from '../doc-compiler/links.mjs';
17
+ import {builtinGuideFiles} from './docs-discovery.mjs';
18
+ import {
19
+ fileWords,
20
+ lineAtOffset,
21
+ loadQuietly,
22
+ locateWords,
23
+ textWords,
24
+ } from './copied-passages.mjs';
25
+
26
+ /**
27
+ * A code span that is exactly the command opening one doc. A command with
28
+ * more words, a flag, or a route is a walkthrough, not a pointer. A name holds
29
+ * what a topic name may (letters, digits, `_` and `-`), never opening with `-`.
30
+ */
31
+ const POINTER = /`astryx docs ([A-Za-z0-9_][A-Za-z0-9_-]*)`/g;
32
+
33
+ /** @type {Promise<Map<string, string>> | undefined} */
34
+ let coreKinds;
35
+
36
+ /**
37
+ * The CLI's own docs by name, each with the kind a link names it by: a
38
+ * namespace doc as `namespace`, a topic or guide as `generic`.
39
+ * @returns {Promise<Map<string, string>>}
40
+ */
41
+ function loadCoreKinds() {
42
+ coreKinds ??= (async () => {
43
+ const kinds = new Map();
44
+ for (const file of builtinGuideFiles()) {
45
+ const doc = await loadQuietly(file);
46
+ if (typeof doc?.name === 'string') {
47
+ kinds.set(doc.name, doc.type === 'namespace' ? 'namespace' : 'generic');
48
+ }
49
+ }
50
+ return kinds;
51
+ })();
52
+ return coreKinds;
53
+ }
54
+
55
+ /**
56
+ * The strings of a doc the CLI resolves links in (spec:AST-047 FR9). In a
57
+ * topic: its description and summary, then the text of prose and heading
58
+ * blocks, list items, and table cells in each section's content, with the
59
+ * section's title. In a component doc (any doc with no sections): every text
60
+ * field, such as its description, usage, best practices, and each prop's and
61
+ * example's description, with the field's path. Code is never read.
62
+ *
63
+ * @param {unknown} doc an authored doc object
64
+ * @returns {{text: string, section: string | null, field: string | null}[]}
65
+ */
66
+ export function linkableStrings(doc) {
67
+ /** @type {{text: string, section: string | null, field: string | null}[]} */
68
+ const strings = [];
69
+ if (doc == null || typeof doc !== 'object') return strings;
70
+ const top = /** @type {Record<string, any>} */ (doc);
71
+ if (!Array.isArray(top.sections)) {
72
+ for (const {field, text} of textFields(doc)) {
73
+ strings.push({text, section: null, field});
74
+ }
75
+ return strings;
76
+ }
77
+ for (const field of ['description', 'summary']) {
78
+ if (typeof top[field] === 'string') {
79
+ strings.push({text: top[field], section: null, field});
80
+ }
81
+ }
82
+ /**
83
+ * @param {unknown} value
84
+ * @param {string | null} section
85
+ */
86
+ const walk = (value, section) => {
87
+ if (Array.isArray(value)) {
88
+ for (const item of value) walk(item, section);
89
+ return;
90
+ }
91
+ if (value == null || typeof value !== 'object') return;
92
+ const record = /** @type {Record<string, any>} */ (value);
93
+ const title = typeof record.title === 'string' ? record.title : section;
94
+ if (Array.isArray(record.sections)) walk(record.sections, section);
95
+ if (!Array.isArray(record.content)) return;
96
+ for (const block of record.content) {
97
+ const add = (/** @type {unknown} */ text) => {
98
+ if (typeof text === 'string') {
99
+ strings.push({text, section: title, field: null});
100
+ }
101
+ };
102
+ switch (block?.type) {
103
+ case 'prose':
104
+ case 'heading':
105
+ add(block.text);
106
+ break;
107
+ case 'list':
108
+ (block.items ?? []).forEach(add);
109
+ break;
110
+ case 'table':
111
+ (block.headers ?? []).forEach(add);
112
+ for (const row of block.rows ?? []) row.forEach(add);
113
+ break;
114
+ default:
115
+ walk(block, title);
116
+ }
117
+ }
118
+ };
119
+ walk(doc, null);
120
+ return strings;
121
+ }
122
+
123
+ /**
124
+ * Every pointer span in one prose string, in order.
125
+ * @param {string} text
126
+ * @returns {string[]} the doc name each one points at
127
+ */
128
+ export function pointersIn(text) {
129
+ return [...text.matchAll(POINTER)].map(match => match[1]);
130
+ }
131
+
132
+ /**
133
+ * Where a string's words start among a file's words, from `from` on. A
134
+ * one-line string the file holds as a quoted literal is found at that
135
+ * literal, so a list item that is only `astryx docs theme` is not placed at a
136
+ * summary or code block that writes the same words.
137
+ *
138
+ * @param {string[]} lines the file's lines
139
+ * @param {{word: string, line: number}[]} file the file's words
140
+ * @param {string} text
141
+ * @param {{word: string}[]} words the string's words
142
+ * @param {number} from
143
+ * @returns {number}
144
+ */
145
+ function locateString(lines, file, text, words, from) {
146
+ /** @type {number[]} */
147
+ const starts = [];
148
+ for (let at = locateWords(file, words); at >= 0;) {
149
+ starts.push(at);
150
+ at = locateWords(file, words, at + 1);
151
+ }
152
+ if (starts.length === 0) return -1;
153
+ const literals = text.includes('\n')
154
+ ? []
155
+ : [`'${text}'`, `"${text}"`, `'${text.replace(/'/g, "\\'")}'`];
156
+ const quoted = starts.filter(at =>
157
+ literals.some(literal => lines[file[at].line - 1]?.includes(literal)),
158
+ );
159
+ const pool = quoted.length > 0 ? quoted : starts;
160
+ return pool.find(at => at >= from) ?? pool[0];
161
+ }
162
+
163
+ /**
164
+ * Each stray pointer in a package's topics and component docs.
165
+ *
166
+ * @param {{records: {name: string, path: string}[], guides?: {name: string, ref?: {topicFile?: string}}[], namespaces?: {doc?: {name?: string}}[], components?: {name: string, file: string}[]}} discovered
167
+ * @returns {Promise<import('../../api/integration/authoring-checks.type.mjs').StrayPointer[]>}
168
+ */
169
+ export async function findStrayPointers(discovered) {
170
+ /** @type {{name: string, file: string | undefined, kind: 'topic' | 'component'}[]} */
171
+ const topics = [
172
+ ...discovered.records.map(record => ({
173
+ name: record.name,
174
+ file: record.path,
175
+ kind: /** @type {const} */ ('topic'),
176
+ })),
177
+ ...(discovered.guides ?? []).map(guide => ({
178
+ name: guide.name,
179
+ file: guide.ref?.topicFile,
180
+ kind: /** @type {const} */ ('topic'),
181
+ })),
182
+ ];
183
+ /** @type {Map<string, string>} */
184
+ const own = new Map(topics.map(topic => [topic.name, 'generic']));
185
+ for (const namespace of discovered.namespaces ?? []) {
186
+ const name = namespace?.doc?.name;
187
+ if (typeof name === 'string' && !own.has(name)) own.set(name, 'namespace');
188
+ }
189
+ for (const component of discovered.components ?? []) {
190
+ topics.push({
191
+ name: component.name,
192
+ file: component.file,
193
+ kind: 'component',
194
+ });
195
+ }
196
+ const core = await loadCoreKinds();
197
+ // A topic that extends another has no route of its own: a link to it names
198
+ // the page it shows on, the topic it extends.
199
+ /** @type {Map<string, string>} */
200
+ const extendsOf = new Map();
201
+ for (const topic of topics) {
202
+ if (!topic.file) continue;
203
+ const doc = /** @type {any} */ (await loadQuietly(topic.file));
204
+ if (typeof doc?.extends === 'string')
205
+ extendsOf.set(topic.name, doc.extends);
206
+ }
207
+ /** @param {string} target */
208
+ const linkFor = target => {
209
+ const page = extendsOf.get(target);
210
+ if (page != null) {
211
+ if (own.has(page) && !extendsOf.has(page)) {
212
+ return `{@link ${own.get(page)}:${page}}`;
213
+ }
214
+ return core.has(page)
215
+ ? `{@link @astryxdesign/cli:${core.get(page)}:${page}}`
216
+ : null;
217
+ }
218
+ return own.has(target)
219
+ ? `{@link ${own.get(target)}:${target}}`
220
+ : core.has(target)
221
+ ? `{@link @astryxdesign/cli:${core.get(target)}:${target}}`
222
+ : null;
223
+ };
224
+ /** @type {import('../../api/integration/authoring-checks.type.mjs').StrayPointer[]} */
225
+ const found = [];
226
+ for (const topic of topics) {
227
+ if (!topic.file) continue;
228
+ const doc = await loadQuietly(topic.file);
229
+ if (!doc) continue;
230
+ /** @type {{word: string, line: number}[]} */
231
+ let source = [];
232
+ /** @type {string[]} */
233
+ let lines = [];
234
+ try {
235
+ const raw = fs.readFileSync(topic.file, 'utf8');
236
+ source = fileWords(raw);
237
+ lines = raw.split('\n');
238
+ } catch {
239
+ // No line numbers; the finding still names the section.
240
+ }
241
+ // A component doc is named by its own name: a family doc by the family.
242
+ const name =
243
+ topic.kind === 'component' && typeof doc.name === 'string'
244
+ ? doc.name
245
+ : topic.name;
246
+ const label =
247
+ topic.kind === 'component' ? `component "${name}"` : `"${name}"`;
248
+ // Each string is found by its whole run of words, from where the last
249
+ // one was found, so two strings that open alike keep their own lines.
250
+ let cursor = 0;
251
+ for (const {text, section, field} of linkableStrings(doc)) {
252
+ const words = textWords(text);
253
+ const index = locateString(lines, source, text, words, cursor);
254
+ if (index >= 0) cursor = index + words.length;
255
+ for (const match of text.matchAll(POINTER)) {
256
+ const target = match[1];
257
+ const link = linkFor(target);
258
+ if (!link) continue;
259
+ const command = `astryx docs ${target}`;
260
+ const line =
261
+ index < 0
262
+ ? null
263
+ : lineAtOffset(
264
+ source,
265
+ index,
266
+ words,
267
+ /** @type {number} */ (match.index) + 1,
268
+ );
269
+ const where = [
270
+ section ? `section "${section}"` : null,
271
+ field ? `field ${field}` : null,
272
+ line == null ? null : `line ${line}`,
273
+ ]
274
+ .filter(Boolean)
275
+ .join(', ');
276
+ found.push({
277
+ topic: name,
278
+ kind: topic.kind,
279
+ section,
280
+ field,
281
+ file: topic.file,
282
+ line,
283
+ pointer: command,
284
+ link,
285
+ message:
286
+ `${label}${where ? ` (${where})` : ''} points at a doc with the written command \`${command}\`. ` +
287
+ `Replace \`${command}\`, code ticks included, with ${link}: the CLI prints the command that opens the doc, and doctor checks the link.`,
288
+ });
289
+ }
290
+ }
291
+ }
292
+ return found;
293
+ }
@@ -0,0 +1,86 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, expect, it} from 'vitest';
4
+
5
+ import {linkableStrings, pointersIn} from './stray-pointers.mjs';
6
+
7
+ describe('pointersIn', () => {
8
+ it('finds a code span that is exactly the command opening one doc', () => {
9
+ expect(
10
+ pointersIn(
11
+ 'Read `astryx docs theme`, then `astryx docs layout-spacing`.',
12
+ ),
13
+ ).toEqual(['theme', 'layout-spacing']);
14
+ });
15
+
16
+ it('leaves a walkthrough alone: more words, a flag, or a route', () => {
17
+ expect(
18
+ pointersIn(
19
+ 'Run `astryx docs theme overview`, `astryx docs --json`, `astryx docs cli/integrations`, or `astryx docs`.',
20
+ ),
21
+ ).toEqual([]);
22
+ });
23
+
24
+ it('leaves the bare name and a runner-written command alone', () => {
25
+ expect(
26
+ pointersIn('See astryx docs theme, or `pnpm astryx docs theme`.'),
27
+ ).toEqual([]);
28
+ });
29
+ });
30
+
31
+ describe('linkableStrings', () => {
32
+ it("reads a topic's description and summary, then prose, headings, list items and table cells, not code", () => {
33
+ const strings = linkableStrings({
34
+ name: 'acme',
35
+ description: 'Read `astryx docs theme`.',
36
+ summary: 'In short.',
37
+ sections: [
38
+ {
39
+ title: 'Use it',
40
+ content: [
41
+ {type: 'prose', text: 'prose'},
42
+ {type: 'heading', level: 3, text: 'heading'},
43
+ {type: 'list', style: 'unordered', items: ['item']},
44
+ {type: 'table', headers: ['head'], rows: [['cell']]},
45
+ {type: 'code', lang: 'bash', code: 'astryx docs theme'},
46
+ ],
47
+ },
48
+ ],
49
+ });
50
+ expect(strings).toEqual([
51
+ {text: 'Read `astryx docs theme`.', section: null, field: 'description'},
52
+ {text: 'In short.', section: null, field: 'summary'},
53
+ {text: 'prose', section: 'Use it', field: null},
54
+ {text: 'heading', section: 'Use it', field: null},
55
+ {text: 'item', section: 'Use it', field: null},
56
+ {text: 'head', section: 'Use it', field: null},
57
+ {text: 'cell', section: 'Use it', field: null},
58
+ ]);
59
+ });
60
+
61
+ it("reads a component doc's text fields, not its names, types, defaults, or code", () => {
62
+ const strings = linkableStrings({
63
+ type: 'component',
64
+ name: 'AcmeButton',
65
+ displayName: 'Acme Button',
66
+ usage: {
67
+ description: 'usage',
68
+ bestPractices: [{guidance: true, description: 'practice'}],
69
+ },
70
+ props: [
71
+ {name: 'tone', type: 'string', default: "'a'", description: 'prop'},
72
+ ],
73
+ examples: [{label: 'example', code: 'astryx docs theme'}],
74
+ });
75
+ expect(strings).toEqual([
76
+ {text: 'usage', section: null, field: 'usage.description'},
77
+ {
78
+ text: 'practice',
79
+ section: null,
80
+ field: 'usage.bestPractices[0].description',
81
+ },
82
+ {text: 'prop', section: null, field: 'props[0].description'},
83
+ {text: 'example', section: null, field: 'examples[0].label'},
84
+ ]);
85
+ });
86
+ });
@@ -15,7 +15,8 @@
15
15
  * needs it: `Project` assembles templates from it. While this sat in api/, that
16
16
  * made a cycle across the layer boundary — Project reached up for discovery and
17
17
  * this module imported Project back. Nothing here depends on api/; every import
18
- * is node:*, jiti, foundation/*, or authoring/*.
18
+ * is node:*, jiti, foundation/*, or authoring/*, plus a lazily required
19
+ * @babel/parser for extractComponents' local-declaration scan.
19
20
  *
20
21
  * @position packages/cli/foundation/discovery — shared template discovery/IO;
21
22
  * consumed by Project and by the api/template leaves.
@@ -512,6 +513,19 @@ function expressionUse(source, open, close) {
512
513
  return null;
513
514
  }
514
515
 
516
+ /** @type {typeof import('@babel/parser') | undefined} */
517
+ let babelParser;
518
+
519
+ /**
520
+ * The template source parser, loaded on first use so listing templates
521
+ * never pays for it.
522
+ * @returns {typeof import('@babel/parser')}
523
+ */
524
+ function templateSourceParser() {
525
+ babelParser ??= require('@babel/parser');
526
+ return /** @type {typeof import('@babel/parser')} */ (babelParser);
527
+ }
528
+
515
529
  /**
516
530
  * The [start, end) range of every comment in `source`, or none when it does
517
531
  * not parse as TSX.
@@ -520,8 +534,7 @@ function expressionUse(source, open, close) {
520
534
  */
521
535
  function commentRanges(source) {
522
536
  try {
523
- const {parse} = require('@babel/parser');
524
- const {comments} = parse(source, {
537
+ const {comments} = templateSourceParser().parse(source, {
525
538
  sourceType: 'module',
526
539
  plugins: ['jsx', 'typescript'],
527
540
  });
@@ -1267,7 +1280,8 @@ export async function discoverIntegrationTemplatesForOne(integration) {
1267
1280
  template: id,
1268
1281
  message: `Template "${id}" is missing a "type" of "page" or "block". Stamp the default export with type: 'page' or type: 'block'.`,
1269
1282
  });
1270
- const declared = doc?.replaces ?? (await declaredReplacement(docPath, id));
1283
+ const declared =
1284
+ doc?.replaces ?? (await declaredReplacement(docPath, id));
1271
1285
  if (declared != null) {
1272
1286
  errors.push({
1273
1287
  code: 'invalid_template_replacement',
@@ -1423,6 +1437,51 @@ const UBIQUITOUS = new Set([
1423
1437
  'Icon',
1424
1438
  ]);
1425
1439
 
1440
+ /**
1441
+ * Names bound by top-level `function`, `class`, `const`, `let` or `var`
1442
+ * declarations (exported or not). Returns an empty set when the source does
1443
+ * not parse, so a parse failure never drops a real component.
1444
+ *
1445
+ * @param {string} src
1446
+ * @returns {Set<string>}
1447
+ */
1448
+ function topLevelBindingNames(src) {
1449
+ /** @type {Set<string>} */
1450
+ const names = new Set();
1451
+ let program;
1452
+ try {
1453
+ program = templateSourceParser().parse(src, {
1454
+ sourceType: 'module',
1455
+ plugins: ['jsx', 'typescript'],
1456
+ errorRecovery: true,
1457
+ }).program;
1458
+ } catch {
1459
+ return names;
1460
+ }
1461
+ for (const statement of program.body) {
1462
+ const node =
1463
+ statement.type === 'ExportNamedDeclaration' ||
1464
+ statement.type === 'ExportDefaultDeclaration'
1465
+ ? statement.declaration
1466
+ : statement;
1467
+ if (node == null) continue;
1468
+ if (
1469
+ (node.type === 'FunctionDeclaration' ||
1470
+ node.type === 'ClassDeclaration') &&
1471
+ node.id
1472
+ ) {
1473
+ names.add(node.id.name);
1474
+ } else if (node.type === 'VariableDeclaration') {
1475
+ for (const declarator of node.declarations) {
1476
+ if (declarator.id.type === 'Identifier') {
1477
+ names.add(declarator.id.name);
1478
+ }
1479
+ }
1480
+ }
1481
+ }
1482
+ return names;
1483
+ }
1484
+
1426
1485
  /**
1427
1486
  * @param {string} pagePath
1428
1487
  * @returns {string[]}
@@ -1449,14 +1508,24 @@ export function extractComponents(pagePath) {
1449
1508
  while ((m = tagRegex.exec(src)) !== null) {
1450
1509
  matches.push(m[2]);
1451
1510
  }
1511
+
1512
+ // Exclude names bound by a top-level declaration in the same file: those are
1513
+ // page-local helpers (e.g. `function TimelineSection()`), not components a
1514
+ // builder can import. Read real bindings from the syntax tree so comments,
1515
+ // strings and type annotations cannot add or hide a name.
1516
+ const localDeclNames = topLevelBindingNames(src);
1517
+
1452
1518
  return [
1453
1519
  ...new Set(
1454
1520
  matches
1521
+ .filter(n => !localDeclNames.has(n))
1455
1522
  .filter(n => !['Theme', 'ThemeProvider'].includes(n))
1456
1523
  .filter(n => !UBIQUITOUS.has(n))
1457
1524
  .map(n =>
1458
1525
  n.replace(
1459
- /(Item|Section|Header|Content|Footer|Panel|Heading|CollapseButton|Column|Sortable|Selection|Group|Source)$/,
1526
+ // Strip only when a stem remains, so a bare `<Section>` keeps its
1527
+ // own name instead of emptying out.
1528
+ /(?<=.)(Item|Section|Header|Content|Footer|Panel|Heading|CollapseButton|Column|Sortable|Selection|Group|Source)$/,
1460
1529
  '',
1461
1530
  ),
1462
1531
  )
@@ -20,7 +20,10 @@ import * as path from 'node:path';
20
20
  import module from 'node:module';
21
21
  import {CLI_ROOT} from '../fs/paths.mjs';
22
22
  import {detectStylingSystem} from '../agent-docs/agent-docs.mjs';
23
- import {decidePackageManagerIn} from '../env/package-manager.mjs';
23
+ import {
24
+ decidePackageManagerIn,
25
+ formatCliCommand,
26
+ } from '../env/package-manager.mjs';
24
27
 
25
28
  // ── Known packages that a template may import without the user installing them.
26
29
  // react and react-dom are the framework; @astryxdesign/* is the design system
@@ -230,7 +233,7 @@ export function analyzeTemplateNeeds(source, cwd) {
230
233
  }
231
234
  if (stylexCompilerNeeded) {
232
235
  notes.push(
233
- 'This template uses StyleX. Your project needs a StyleX compiler plugin \u2014 see `astryx docs styling-overview` for setup steps.',
236
+ `This template uses StyleX. Your project needs a StyleX compiler plugin \u2014 see \`${formatCliCommand('docs styling-overview')}\` for setup steps.`,
234
237
  );
235
238
  }
236
239