@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
@@ -36,6 +36,7 @@ import {
36
36
  tokenizeQuery,
37
37
  SEARCH_DOMAINS,
38
38
  } from './search.mjs';
39
+ import {linkThisCliAsProjectCli} from '../../test-utils/release-fixtures.mjs';
39
40
 
40
41
  const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
41
42
  const cwd = REPO;
@@ -470,8 +471,10 @@ describe('search leaf — error paths (pinned)', () => {
470
471
 
471
472
  it('searches the docs without @astryxdesign/core, and throws for a domain that needs it', async () => {
472
473
  const empty = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-search-no-core-'));
474
+ // A project whose own CLI answers, with no Core installed.
475
+ linkThisCliAsProjectCli(empty);
473
476
  try {
474
- // An open search outside an app covers the docs, as `astryx docs` does.
477
+ // An open search without Core covers the docs, as `astryx docs` does.
475
478
  const open = await search('make an integration', {cwd: empty});
476
479
  expect(open.data.results.length).toBeGreaterThan(0);
477
480
  expect(new Set(open.data.results.map(r => r.domain))).toEqual(
@@ -616,6 +619,42 @@ describe('search leaf — a theme is found by its names; its description is pros
616
619
  );
617
620
  });
618
621
 
622
+ describe('search leaf — components whose docs live in a family folder', () => {
623
+ it("indexes a family member from its own entry, not the family's keywords", async () => {
624
+ // HStack and VStack are documented in Stack's doc, whose keywords
625
+ // (horizontal, vertical, row, column, ...) describe Stack.
626
+ const reasons = async (/** @type {string} */ query) =>
627
+ Object.fromEntries(
628
+ (await search(query, {cwd, type: 'component'})).data.results.map(r => [
629
+ r.name,
630
+ r.reason,
631
+ ]),
632
+ );
633
+ const column = await reasons('column');
634
+ expect(column.HStack ?? '').not.toMatch(/keyword/);
635
+ expect(column.VStack ?? '').not.toMatch(/keyword/);
636
+ expect(column.Stack).toMatch(/keyword/);
637
+ const vertical = (await search('vertical', {cwd, type: 'component'})).data.results.map(
638
+ r => r.name,
639
+ );
640
+ expect(vertical.indexOf('VStack')).toBeGreaterThanOrEqual(0);
641
+ if (vertical.includes('HStack')) {
642
+ expect(vertical.indexOf('VStack')).toBeLessThan(vertical.indexOf('HStack'));
643
+ }
644
+ }, SLOW);
645
+
646
+ it('ranks VStack, HStack and Heading first for their own names, with their own import', async () => {
647
+ for (const name of ['VStack', 'HStack', 'Heading']) {
648
+ const r = await search(name.toLowerCase(), {cwd, type: 'component'});
649
+ expect(r.data.results[0]).toMatchObject({
650
+ domain: 'component',
651
+ name,
652
+ import: `@astryxdesign/core/${name}`,
653
+ });
654
+ }
655
+ }, SLOW);
656
+ });
657
+
619
658
  describe('search leaf — integration components', () => {
620
659
  /**
621
660
  * A minimal consumer project: a stub `@astryxdesign/core` (so `findCoreDir`
@@ -827,3 +866,142 @@ describe('search — usage guidance is indexed, a tier below description', () =>
827
866
  expect(hit?.score).toBe(45);
828
867
  });
829
868
  });
869
+
870
+ describe('search leaf — compound names, other names, and word forms', () => {
871
+ it('finds a compound name typed as separate words inside a longer query', async () => {
872
+ const {data} = await search('table header cell with sort arrow', {cwd});
873
+ expect(
874
+ data.results.slice(0, 3).map(r => r.name),
875
+ ).toContain('TableHeaderCell');
876
+ }, SLOW);
877
+
878
+ it('finds a one-word keyword typed as separate words', async () => {
879
+ // MetadataList declares `keyvalue`.
880
+ const {data} = await search('key value pairs', {cwd});
881
+ expect(data.results[0]).toMatchObject({
882
+ domain: 'component',
883
+ name: 'MetadataList',
884
+ });
885
+ }, SLOW);
886
+
887
+ it('finds a hook named by its words without the `use` prefix', async () => {
888
+ const {data} = await search('persisted table column settings', {cwd});
889
+ expect(data.results[0]).toMatchObject({
890
+ domain: 'hook',
891
+ name: 'useTableColumnSettings',
892
+ });
893
+ }, SLOW);
894
+
895
+ it('reads a camelCase identifier as the words it spells', async () => {
896
+ const {data} = await search('useTablePaginationState', {cwd});
897
+ expect(data.results[0]).toMatchObject({
898
+ domain: 'hook',
899
+ name: 'useTablePagination',
900
+ });
901
+ }, SLOW);
902
+
903
+ it('keeps each identifier of a multi-word query whole', async () => {
904
+ const {data} = await search('ChatMessageList ChatComposer', {cwd});
905
+ expect(data.results.slice(0, 2).map(r => r.name).sort()).toEqual([
906
+ 'ChatComposer',
907
+ 'ChatMessageList',
908
+ ]);
909
+ }, SLOW);
910
+
911
+ it("finds a component by another library's name for it", async () => {
912
+ for (const [q, name] of [
913
+ ['ScrollArea viewport', 'ScrollableArea'],
914
+ ['toggle group of buttons', 'ToggleButtonGroup'],
915
+ ['circular progress for uploads', 'Spinner'],
916
+ ]) {
917
+ const {data} = await search(q, {cwd});
918
+ expect(data.results[0], q).toMatchObject({domain: 'component', name});
919
+ }
920
+ }, SLOW);
921
+
922
+ it('reads a one-identifier other name as typed', async () => {
923
+ const {data} = await search('AppHeader', {cwd});
924
+ expect(data.results[0]).toMatchObject({
925
+ domain: 'component',
926
+ name: 'LayoutHeader',
927
+ });
928
+ }, SLOW);
929
+
930
+ it('adds no near siblings beside an entry the query names outright', async () => {
931
+ const {data} = await search('top nav mega menu', {cwd});
932
+ expect(data.results[0]).toMatchObject({name: 'TopNavMegaMenu'});
933
+ // Its sub-parts start with its name; read joined they would score in the
934
+ // whole-name tier right under it.
935
+ const siblings = data.results.filter(
936
+ r => r.name !== 'TopNavMegaMenu' && r.name.startsWith('TopNavMegaMenu'),
937
+ );
938
+ for (const sibling of siblings) expect(sibling.score).toBeLessThan(190);
939
+ }, SLOW);
940
+
941
+ it('ranks an entry the query names above a match on part of another name', async () => {
942
+ // `table header` names TableHeader; useTableStickyColumns only holds the
943
+ // words `sticky columns`.
944
+ const {data} = await search('table header with sticky columns', {cwd});
945
+ const names = data.results.map(r => r.name);
946
+ expect(names.indexOf('TableHeader')).toBeLessThan(
947
+ names.indexOf('useTableStickyColumns') === -1
948
+ ? Infinity
949
+ : names.indexOf('useTableStickyColumns'),
950
+ );
951
+ }, SLOW);
952
+
953
+ it('keeps an entry a query word names by its whole name at its own rank', async () => {
954
+ // Two words join into TextInput, and TextArea also matches two words
955
+ // through its joined keyword; one word is still TextArea's whole name.
956
+ const {data} = await search('text input and textarea with multi line', {
957
+ cwd,
958
+ });
959
+ expect(data.results[0]).toMatchObject({name: 'TextArea'});
960
+ }, SLOW);
961
+
962
+ it('ranks a match on more of the query above a shorter named entry', async () => {
963
+ const {data} = await search('dropdown menu radio options', {cwd});
964
+ expect(data.results[0]).toMatchObject({name: 'DropdownMenuRadioItem'});
965
+ }, SLOW);
966
+
967
+ it('lets a plural start a name through its singular', () => {
968
+ expect(
969
+ scoreCandidate('tokens', {domain: 'component', name: 'Tokenizer'}),
970
+ ).toMatchObject({score: 60});
971
+ });
972
+
973
+ it('finds Icon by a glyph name', async () => {
974
+ for (const q of ['cog', 'lightning bolt']) {
975
+ const {data} = await search(q, {cwd});
976
+ expect(data.results[0], q).toMatchObject({
977
+ domain: 'component',
978
+ name: 'Icon',
979
+ });
980
+ }
981
+ }, SLOW);
982
+
983
+ it('matches a verb form against its verb', () => {
984
+ // "spacing" lost the final e of "space".
985
+ expect(
986
+ scoreCandidate('spacing', {domain: 'component', name: 'Gap', keywords: ['space']}),
987
+ ).toMatchObject({score: 88});
988
+ expect(
989
+ scoreCandidate('dragging', {domain: 'component', name: 'Gap', keywords: ['drag']}),
990
+ ).toMatchObject({score: 88});
991
+ });
992
+
993
+ it('lets a plural start a longer word through its singular', () => {
994
+ expect(
995
+ scoreCandidate('chips', {domain: 'component', name: 'Chipset'}),
996
+ ).toMatchObject({score: 60});
997
+ // A four-letter singular is too short: "stats" does not start "statusbar".
998
+ expect(
999
+ scoreCandidate('stats', {domain: 'component', name: 'Statusbar'}),
1000
+ ).toBeNull();
1001
+ });
1002
+
1003
+ it('leaves an identifier joined with _ unmatched when nothing has it', async () => {
1004
+ const {data} = await search('qqzz_no_match_here', {cwd});
1005
+ expect(data.results).toEqual([]);
1006
+ }, SLOW);
1007
+ });
@@ -30,7 +30,7 @@ export type SearchResultEntry = {
30
30
  */
31
31
  description: string;
32
32
  /**
33
- * - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme).
33
+ * - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme; `astryx theme eject neutral` outside a project).
34
34
  */
35
35
  command: string;
36
36
  /**
@@ -72,6 +72,19 @@ export type SearchResponse = {
72
72
  matchCount: number;
73
73
  results: SearchResultEntry[];
74
74
  };
75
+ /**
76
+ * -
77
+ * `partial`, when an open search could not get the Core that components,
78
+ * hooks, and templates are read from (such as outside a project, offline,
79
+ * before the release's Core was downloaded): the domains it skipped and why.
80
+ * The results are the docs and themes.
81
+ */
82
+ meta?: {
83
+ partial?: {
84
+ skipped: SearchDomain[];
85
+ reason: string;
86
+ };
87
+ } | undefined;
75
88
  };
76
89
  /**
77
90
  * Options for `search()`.
@@ -20,7 +20,7 @@
20
20
  * @property {number} score - Relevance score (higher is better).
21
21
  * @property {string} reason - Human-readable reason the candidate matched (e.g. `keyword "button"`).
22
22
  * @property {string} description - One-line description, when available.
23
- * @property {string} command - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme).
23
+ * @property {string} command - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme; `astryx theme eject neutral` outside a project).
24
24
  * @property {string} [import] - Import path — present for component and hook results.
25
25
  * @property {string} [title] - Doc title — present for doc results.
26
26
  * @property {string} [section] - Section key — present when a doc result is one section of a topic; `command` reads only that section.
@@ -38,6 +38,11 @@
38
38
  * @property {string} data.query
39
39
  * @property {number} data.matchCount - How many candidates matched the query in total, before `limit` was applied. `results` is the bounded slice of that set, so `matchCount > results.length` means the answer was capped.
40
40
  * @property {SearchResultEntry[]} data.results
41
+ * @property {{partial?: {skipped: SearchDomain[], reason: string}}} [meta] -
42
+ * `partial`, when an open search could not get the Core that components,
43
+ * hooks, and templates are read from (such as outside a project, offline,
44
+ * before the release's Core was downloaded): the domains it skipped and why.
45
+ * The results are the docs and themes.
41
46
  */
42
47
 
43
48
  /**
@@ -11,7 +11,7 @@
11
11
  * (ERR_CORE_NOT_FOUND) when core can't be located.
12
12
  */
13
13
 
14
- import {findCoreDir, listComponents} from '../../foundation/fs/paths.mjs';
14
+ import {findProjectCoreDir, listComponents} from '../../foundation/fs/paths.mjs';
15
15
  import {resolveComponentReplacements} from '../../foundation/discovery/component-replacement.mjs';
16
16
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
17
17
  import {AstryxError} from '../error.mjs';
@@ -44,7 +44,7 @@ export async function resolveComponentReplacement(
44
44
  * @returns {{coreDir: string, components: string[]}}
45
45
  */
46
46
  export function resolveCore(cwd) {
47
- const coreDir = findCoreDir(cwd);
47
+ const coreDir = findProjectCoreDir(cwd);
48
48
  if (!coreDir) {
49
49
  throw new AstryxError(
50
50
  'Could not find @astryxdesign/core package. Make sure you are inside the design system monorepo or have @astryxdesign/core installed.',
@@ -21,8 +21,10 @@ import {checkGhCli} from '../_github.mjs';
21
21
  import {Project} from '../../../foundation/config/project.mjs';
22
22
  import {
23
23
  CORE_PACKAGE,
24
- findIntegrationComponentDoc,
25
- findIntegrationComponentSource,
24
+ docStem,
25
+ findComponentReadme,
26
+ findIntegrationComponent,
27
+ unscopedComponentOwners,
26
28
  } from '../../../foundation/discovery/component-discovery.mjs';
27
29
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
28
30
  import {AstryxError, writeFailed} from '../../error.mjs';
@@ -113,7 +115,7 @@ function buildFeedback(component, issuesUrl) {
113
115
  */
114
116
  async function loadConfigSafely(cwd) {
115
117
  try {
116
- const project = await Project.load(cwd);
118
+ const project = await Project.load(cwd, {core: 'project'});
117
119
  return {
118
120
  loadedIntegrations: project.loadedIntegrations,
119
121
  issuesUrl: project.config.issuesUrl,
@@ -126,33 +128,38 @@ async function loadConfigSafely(cwd) {
126
128
 
127
129
  /**
128
130
  * Build the set of OWNER packages that provide a component named `name` across
129
- * core + every loaded integration.
131
+ * core + every loaded integration. Core holds every name it documents, but has
132
+ * source to copy (`sourceDir`) only for a name with its own folder.
130
133
  * @param {string} coreDir
131
134
  * @param {Array<{name: string, components?: string, issuesUrl?: string}>} loadedIntegrations
132
135
  * @param {string} name
133
136
  * @param {string|undefined} coreIssuesUrl
134
- * @returns {Array<{package: string, sourceDir: string|null, ownerPackage: string, issuesUrl: string|undefined}>}
137
+ * @returns {Array<{package: string, sourceDir: string|null, docPath: string|null, ownerPackage: string, issuesUrl: string|undefined, hold: string}>}
135
138
  */
136
139
  function resolveOwners(coreDir, loadedIntegrations, name, coreIssuesUrl) {
137
140
  const owners = [];
138
141
  const coreComponentDir = path.join(coreDir, 'src', name);
139
- if (fs.existsSync(coreComponentDir)) {
142
+ const coreFolder = fs.existsSync(coreComponentDir);
143
+ if (coreFolder || findComponentReadme(coreDir, name)) {
140
144
  owners.push({
141
145
  package: CORE_PACKAGE,
142
- sourceDir: coreComponentDir,
146
+ sourceDir: coreFolder ? coreComponentDir : null,
147
+ docPath: null,
143
148
  ownerPackage: CORE_PACKAGE,
144
149
  issuesUrl: coreIssuesUrl || DEFAULT_ISSUES_URL,
150
+ hold: 'core',
145
151
  });
146
152
  }
147
153
  for (const integration of loadedIntegrations) {
148
- const docPath = findIntegrationComponentDoc(integration, name);
149
- if (!docPath) continue;
150
- const sourcePath = findIntegrationComponentSource(integration, name);
154
+ const found = findIntegrationComponent(integration, name);
155
+ if (!found) continue;
151
156
  owners.push({
152
157
  package: integration.name,
153
- sourceDir: sourcePath ? path.dirname(sourcePath) : null,
158
+ sourceDir: found.sourcePath ? path.dirname(found.sourcePath) : null,
159
+ docPath: found.docPath,
154
160
  ownerPackage: integration.name,
155
161
  issuesUrl: integration.issuesUrl,
162
+ hold: found.hold,
156
163
  });
157
164
  }
158
165
  return owners;
@@ -222,25 +229,32 @@ export async function swizzleCopy(component, options = {}) {
222
229
  : undefined;
223
230
  // An active integration replacement answers to the Core name it replaces,
224
231
  // bare or qualified by its own package (spec:AST-035 FR11): copy it under its
225
- // own name. `--package @astryxdesign/core` still copies the Core original.
232
+ // doc's file name, the folder it has always gone to. `--package
233
+ // @astryxdesign/core` still copies the Core original.
226
234
  if (pkg !== CORE_PACKAGE) {
227
235
  const replacement = await resolveComponentReplacement(
228
236
  coreDir,
229
237
  loadedIntegrations,
230
238
  dirName,
231
239
  );
240
+ const copyName = replacement ? docStem(replacement.docPath) : null;
232
241
  if (
233
242
  replacement &&
234
- (!pkg || (pkg === replacement.package && replacement.name !== dirName))
243
+ copyName &&
244
+ (!pkg || (pkg === replacement.package && copyName !== dirName))
235
245
  ) {
236
- return swizzleCopy(replacement.name, {
246
+ return swizzleCopy(copyName, {
237
247
  ...options,
238
248
  package: replacement.package,
239
249
  });
240
250
  }
241
251
  }
242
252
 
243
- const allOwners = resolveOwners(coreDir, loadedIntegrations, dirName, coreIssuesUrl);
253
+ const found = resolveOwners(coreDir, loadedIntegrations, dirName, coreIssuesUrl);
254
+ // The owner rule, then a Core name without its own folder drops out.
255
+ const allOwners = (pkg ? found : unscopedComponentOwners(found)).filter(
256
+ owner => owner.package !== CORE_PACKAGE || owner.sourceDir != null,
257
+ );
244
258
 
245
259
  if (allOwners.length === 0) {
246
260
  throw new AstryxError(
@@ -736,7 +736,7 @@ describe('integration template discovery', () => {
736
736
  const command = await runCli(['template', 'shell-side-nav'], tmpDir);
737
737
  expect(command.status).toBe(0);
738
738
  expect(command.stderr).toContain('@acme/extra has 1 integration issue');
739
- expect(command.stderr).toContain('astryx doctor');
739
+ expect(command.stderr).toMatch(/Run: .+ doctor/);
740
740
 
741
741
  const doctor = await runCli(['doctor'], tmpDir);
742
742
  expect(doctor.status).toBe(0);
@@ -1,7 +1,14 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
3
6
  import {describe, it, expect} from 'vitest';
4
- import {stripTemplateAssetRefs, template} from './template.mjs';
7
+ import {
8
+ stripTemplateAssetRefs,
9
+ template,
10
+ extractComponents,
11
+ } from './template.mjs';
5
12
 
6
13
  describe('stripTemplateAssetRefs', () => {
7
14
  it('replaces a /template-assets image path with an inline data URI', () => {
@@ -31,8 +38,7 @@ describe('stripTemplateAssetRefs', () => {
31
38
  });
32
39
 
33
40
  it('preserves surrounding source structure', () => {
34
- const src =
35
- "const data = [{src: '/template-assets/x.png', alt: 'X'}];";
41
+ const src = "const data = [{src: '/template-assets/x.png', alt: 'X'}];";
36
42
  const out = stripTemplateAssetRefs(src);
37
43
  expect(out).toContain("alt: 'X'");
38
44
  expect(out).toContain('const data = [{src:');
@@ -102,4 +108,115 @@ describe('template --skeleton component extraction (prefix-agnostic)', () => {
102
108
 
103
109
  expect(result.data.skeleton).toContain('columns={{minWidth: 200}}');
104
110
  });
111
+
112
+ it('filters out local helper components declared in the same file', () => {
113
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
114
+ const pagePath = path.join(tmpDir, 'page.tsx');
115
+ fs.writeFileSync(
116
+ pagePath,
117
+ `
118
+ import {Card} from '@astryxdesign/core/Layout';
119
+
120
+ function TimelineSection() {
121
+ return <div />;
122
+ }
123
+
124
+ export default function Page() {
125
+ return (
126
+ <Card>
127
+ <TimelineSection />
128
+ </Card>
129
+ );
130
+ }
131
+ `,
132
+ );
133
+
134
+ try {
135
+ const components = extractComponents(pagePath);
136
+ expect(components).toContain('Card');
137
+ expect(components).not.toContain('Timeline');
138
+ } finally {
139
+ fs.rmSync(tmpDir, {recursive: true, force: true});
140
+ }
141
+ });
142
+
143
+ it('filters out a type-annotated local helper', () => {
144
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
145
+ const pagePath = path.join(tmpDir, 'page.tsx');
146
+ fs.writeFileSync(
147
+ pagePath,
148
+ `
149
+ import type {FC} from 'react';
150
+ import {Card} from '@astryxdesign/core/Layout';
151
+
152
+ const TimelineSection: FC = () => <div />;
153
+
154
+ export default function Page() {
155
+ return (
156
+ <Card>
157
+ <TimelineSection />
158
+ </Card>
159
+ );
160
+ }
161
+ `,
162
+ );
163
+ try {
164
+ const components = extractComponents(pagePath);
165
+ expect(components).toContain('Card');
166
+ expect(components).not.toContain('Timeline');
167
+ } finally {
168
+ fs.rmSync(tmpDir, {recursive: true, force: true});
169
+ }
170
+ });
171
+
172
+ it('keeps an imported component when declaration-like text appears in a comment or string', () => {
173
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
174
+ const pagePath = path.join(tmpDir, 'page.tsx');
175
+ fs.writeFileSync(
176
+ pagePath,
177
+ `
178
+ import {Card} from '@astryxdesign/core/Layout';
179
+
180
+ // const Card = () => null;
181
+ const note = 'function Card() {}';
182
+
183
+ export default function Page() {
184
+ return <Card>{note}</Card>;
185
+ }
186
+ `,
187
+ );
188
+ try {
189
+ expect(extractComponents(pagePath)).toContain('Card');
190
+ } finally {
191
+ fs.rmSync(tmpDir, {recursive: true, force: true});
192
+ }
193
+ });
194
+
195
+ it('keeps a bare imported component whose name is itself a stripped suffix', () => {
196
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-test-'));
197
+ const pagePath = path.join(tmpDir, 'page.tsx');
198
+ fs.writeFileSync(
199
+ pagePath,
200
+ `
201
+ import {Section} from '@astryxdesign/core/Section';
202
+
203
+ function SectionHeading() {
204
+ return <div />;
205
+ }
206
+
207
+ export default function Page() {
208
+ return (
209
+ <Section>
210
+ <SectionHeading />
211
+ </Section>
212
+ );
213
+ }
214
+ `,
215
+ );
216
+ try {
217
+ expect(extractComponents(pagePath)).toContain('Section');
218
+ } finally {
219
+ fs.rmSync(tmpDir, {recursive: true, force: true});
220
+ }
221
+ });
105
222
  });
@@ -129,6 +129,22 @@ export function removeThemeFromApp(slug: string, options?: {
129
129
  export function useThemeInApp(slug: string, options?: {
130
130
  cwd?: string;
131
131
  }): Promise<import("./theme.type.mjs").ThemeAppResponse>;
132
+ /**
133
+ * The Core theme targets are read from: the project's, else the release's
134
+ * own, installed into the per-user cache first when needed (a network install
135
+ * and an npm subprocess; spec AST-081 FR3). Throws as requireCoreDir does.
136
+ * @param {string} cwd
137
+ * @returns {string}
138
+ */
139
+ export function requireThemeCoreDir(cwd: string): string;
140
+ /**
141
+ * The Core a theme build reads outside a project: the one the answering copy
142
+ * carries, installed into the per-user cache first when needed (a network
143
+ * install and an npm subprocess; spec AST-081 FR3); null when there is none.
144
+ * @param {string} cwd
145
+ * @returns {string | null}
146
+ */
147
+ export function fetchCarriedThemeCoreDir(cwd: string): string | null;
132
148
  /**
133
149
  * A bundled theme entry kept for the historical public `listThemes()` helper.
134
150
  */
@@ -11,6 +11,10 @@
11
11
 
12
12
  import * as fs from 'node:fs';
13
13
  import * as path from 'node:path';
14
+ import {
15
+ findCarriedCoreDir,
16
+ requireCoreDir,
17
+ } from '../../foundation/fs/paths.mjs';
14
18
  import {Project} from '../../foundation/config/project.mjs';
15
19
  import {
16
20
  BUNDLED_THEME_PACKAGE,
@@ -41,6 +45,7 @@ import {stripCopyrightHeader} from '../../foundation/text/copyright-header.mjs';
41
45
  import {loadIntegrations} from '../../foundation/integrations/integrations.mjs';
42
46
  import {importThemeArtifact} from '../../foundation/fs/module-loader.mjs';
43
47
  import {applyWrites} from '../integration/add-helpers.mjs';
48
+ import {formatCliCommand} from '../../foundation/env/package-manager.mjs';
44
49
 
45
50
  export {BUNDLED_THEME_PACKAGE, THEMES_DIR};
46
51
 
@@ -565,7 +570,7 @@ export function resolveRecordedThemes(state, available) {
565
570
  );
566
571
  if (!theme) {
567
572
  throw new AstryxError(
568
- `Added theme "${slug}" from ${owner} is unavailable. Reinstall the package or restore the local theme, then run \`astryx theme add ${slug} --package ${owner}\`.`,
573
+ `Added theme "${slug}" from ${owner} is unavailable. Reinstall the package or restore the local theme, then run \`${formatCliCommand(`theme add ${slug} --package ${owner}`, state.projectDir)}\`.`,
569
574
  undefined,
570
575
  ERROR_CODES.ERR_UNKNOWN_THEME,
571
576
  );
@@ -732,14 +737,14 @@ export async function removeThemeFromApp(slug, options = {}) {
732
737
  const recordedSlug = recordedThemeSlug(state, slug);
733
738
  if (!state.configured || recordedSlug == null) {
734
739
  throw new AstryxError(
735
- `Theme "${slug}" is not added. Run \`astryx theme add ${slug}\` first.`,
740
+ `Theme "${slug}" is not added. Run \`${formatCliCommand(`theme add ${slug}`, state.projectDir)}\` first.`,
736
741
  undefined,
737
742
  ERROR_CODES.ERR_UNKNOWN_THEME,
738
743
  );
739
744
  }
740
745
  if (state.defaultSlug === recordedSlug) {
741
746
  throw new AstryxError(
742
- `Theme "${recordedSlug}" is the default. Run \`astryx theme use <other-slug>\` before removing it.`,
747
+ `Theme "${recordedSlug}" is the default. Run \`${formatCliCommand('theme use <other-slug>', state.projectDir)}\` before removing it.`,
743
748
  undefined,
744
749
  ERROR_CODES.ERR_THEME_INVALID,
745
750
  );
@@ -748,7 +753,7 @@ export async function removeThemeFromApp(slug, options = {}) {
748
753
  delete themes[recordedSlug];
749
754
  if (state.defaultSlug == null) {
750
755
  throw new AstryxError(
751
- 'The generated theme module has no default theme. Run `astryx theme use <slug>` first.',
756
+ `The generated theme module has no default theme. Run \`${formatCliCommand('theme use <slug>', state.projectDir)}\` first.`,
752
757
  undefined,
753
758
  ERROR_CODES.ERR_THEME_INVALID,
754
759
  );
@@ -776,7 +781,7 @@ export async function useThemeInApp(slug, options = {}) {
776
781
  const recordedSlug = recordedThemeSlug(state, slug);
777
782
  if (!state.configured || recordedSlug == null) {
778
783
  throw new AstryxError(
779
- `Theme "${slug}" is not added. Run \`astryx theme add ${slug}\` first.`,
784
+ `Theme "${slug}" is not added. Run \`${formatCliCommand(`theme add ${slug}`, state.projectDir)}\` first.`,
780
785
  undefined,
781
786
  ERROR_CODES.ERR_UNKNOWN_THEME,
782
787
  );
@@ -792,3 +797,25 @@ export async function useThemeInApp(slug, options = {}) {
792
797
  available,
793
798
  );
794
799
  }
800
+
801
+ /**
802
+ * The Core theme targets are read from: the project's, else the release's
803
+ * own, installed into the per-user cache first when needed (a network install
804
+ * and an npm subprocess; spec AST-081 FR3). Throws as requireCoreDir does.
805
+ * @param {string} cwd
806
+ * @returns {string}
807
+ */
808
+ export function requireThemeCoreDir(cwd) {
809
+ return requireCoreDir(cwd);
810
+ }
811
+
812
+ /**
813
+ * The Core a theme build reads outside a project: the one the answering copy
814
+ * carries, installed into the per-user cache first when needed (a network
815
+ * install and an npm subprocess; spec AST-081 FR3); null when there is none.
816
+ * @param {string} cwd
817
+ * @returns {string | null}
818
+ */
819
+ export function fetchCarriedThemeCoreDir(cwd) {
820
+ return findCarriedCoreDir(cwd, {fetch: true});
821
+ }
@@ -110,13 +110,14 @@ export function themeBuildForReport(file: string, options?: {
110
110
  receipt: import("../theme.type.mjs").ThemeBuildResponse | import("../theme.type.mjs").ThemeBuildCheckResponse | null;
111
111
  trailer: ThemeBuildTrailer | null;
112
112
  }>;
113
- /** @param {string} file @param {{out?: string, check?: boolean, iconsSpecifier?: string}} [options] @param {{cwd?: string}} [ctx] @returns {Promise<import('../theme.type.mjs').ThemeBuildResponse | import('../theme.type.mjs').ThemeBuildCheckResponse | null>} */
113
+ /** @param {string} file @param {{out?: string, check?: boolean, iconsSpecifier?: string}} [options] @param {{cwd?: string, projectCore?: boolean}} [ctx] `projectCore`: read only the project's installed Core, never the carried one (doctor; spec AST-081 FR3) @returns {Promise<import('../theme.type.mjs').ThemeBuildResponse | import('../theme.type.mjs').ThemeBuildCheckResponse | null>} */
114
114
  export function themeBuild(file: string, options?: {
115
115
  out?: string;
116
116
  check?: boolean;
117
117
  iconsSpecifier?: string;
118
118
  }, ctx?: {
119
119
  cwd?: string;
120
+ projectCore?: boolean;
120
121
  }): Promise<import("../theme.type.mjs").ThemeBuildResponse | import("../theme.type.mjs").ThemeBuildCheckResponse | null>;
121
122
  /** @param {string[]} files @param {{familyKey: string, check?: boolean, iconsSpecifier?: string, __onPrivateVarError?: () => void}} options @param {{cwd?: string}} [ctx] */
122
123
  export function themeBuildFamily(files: string[], options: {