@astryxdesign/cli 0.7.2-canary.fdd384f → 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 (450) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +169 -149
  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/kit/kit.mjs +3 -11
  7. package/api/component/_adapter.d.mts +31 -0
  8. package/api/component/_adapter.mjs +119 -11
  9. package/api/component/component.doc.mjs +6 -1
  10. package/api/component/component.mjs +60 -10
  11. package/api/component/doc-links.test.mjs +386 -0
  12. package/api/discover/_adapter.d.mts +132 -12
  13. package/api/discover/_adapter.mjs +488 -27
  14. package/api/discover/_adapter.test.mjs +599 -17
  15. package/api/discover/_catalog-view.d.mts +16 -5
  16. package/api/discover/_catalog-view.mjs +32 -8
  17. package/api/discover/detail/doc/doc.mjs +9 -1
  18. package/api/discover/discover.d.mts +11 -0
  19. package/api/discover/discover.doc.mjs +9 -7
  20. package/api/discover/discover.mjs +121 -19
  21. package/api/discover/discover.type.d.mts +20 -7
  22. package/api/discover/discover.type.mjs +17 -7
  23. package/api/discover/list/list.d.mts +4 -3
  24. package/api/discover/list/list.mjs +17 -10
  25. package/api/docs/_adapter.d.mts +63 -0
  26. package/api/docs/_adapter.mjs +312 -18
  27. package/api/docs/docs.doc.mjs +2 -1
  28. package/api/docs/docs.mjs +2 -1
  29. package/api/docs/docs.test.mjs +5 -2
  30. package/api/docs/integration-tree.test.mjs +22 -0
  31. package/api/docs/list/list.mjs +12 -2
  32. package/api/docs/reference-blocks.test.mjs +1 -3
  33. package/api/doctor/_adapter.d.mts +108 -0
  34. package/api/doctor/_adapter.mjs +223 -4
  35. package/api/doctor/doctor.d.mts +199 -0
  36. package/api/doctor/doctor.doc.mjs +3 -2
  37. package/api/doctor/doctor.mjs +530 -34
  38. package/api/doctor/doctor.test.mjs +180 -14
  39. package/api/doctor/release-checks.test.mjs +156 -0
  40. package/api/doctor/theme-checks.mjs +16 -3
  41. package/api/doctor/theme-checks.test.mjs +223 -7
  42. package/api/error.d.mts +10 -0
  43. package/api/error.mjs +25 -0
  44. package/api/gap-report/gap-report.d.mts +3 -0
  45. package/api/gap-report/gap-report.doc.mjs +11 -3
  46. package/api/gap-report/gap-report.mjs +152 -48
  47. package/api/gap-report/gap-report.test.mjs +233 -0
  48. package/api/gap-report/gap-report.type.d.mts +41 -0
  49. package/api/gap-report/gap-report.type.mjs +18 -0
  50. package/api/hook/_adapter.mjs +3 -11
  51. package/api/hook/hook.doc.mjs +4 -0
  52. package/api/hook/list/list.d.mts +1 -1
  53. package/api/init/_adapter.d.mts +22 -0
  54. package/api/init/_adapter.mjs +59 -0
  55. package/api/init/init.d.mts +7 -5
  56. package/api/init/init.doc.mjs +28 -7
  57. package/api/init/init.mjs +28 -4
  58. package/api/init/init.type.d.mts +48 -3
  59. package/api/init/init.type.mjs +37 -3
  60. package/api/init/run/run.d.mts +1 -1
  61. package/api/init/run/run.mjs +15 -4
  62. package/api/init/user/user.d.mts +14 -0
  63. package/api/init/user/user.mjs +87 -0
  64. package/api/init/user/user.test.mjs +232 -0
  65. package/api/integration/_adapter.d.mts +33 -0
  66. package/api/integration/_adapter.mjs +58 -0
  67. package/api/integration/authoring-checks.mjs +100 -5
  68. package/api/integration/authoring-checks.test.mjs +769 -0
  69. package/api/integration/authoring-checks.type.d.mts +134 -0
  70. package/api/integration/authoring-checks.type.mjs +60 -1
  71. package/api/integration/integrationCheck.doc.mjs +1 -1
  72. package/api/integration/integrationComponentConflicts.doc.mjs +11 -3
  73. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  74. package/api/integration/pack-check.mjs +303 -13
  75. package/api/integration/pack-check.test.mjs +371 -10
  76. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  77. package/api/integration/validate-integration.d.mts +8 -0
  78. package/api/integration/validate-integration.mjs +55 -9
  79. package/api/integration/validate-integration.test.mjs +77 -8
  80. package/api/integration/validate-integration.type.d.mts +8 -0
  81. package/api/integration/validate-integration.type.mjs +3 -1
  82. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  83. package/api/integration/validateIntegration.doc.mjs +4 -2
  84. package/api/search/_adapter.d.mts +11 -0
  85. package/api/search/_adapter.mjs +25 -0
  86. package/api/search/search.d.mts +73 -13
  87. package/api/search/search.doc.mjs +8 -3
  88. package/api/search/search.domain-priority.test.mjs +108 -18
  89. package/api/search/search.mjs +541 -52
  90. package/api/search/search.test.mjs +143 -1
  91. package/api/search/search.type.d.mts +14 -1
  92. package/api/search/search.type.mjs +6 -1
  93. package/api/swizzle/_adapter.mjs +2 -2
  94. package/api/swizzle/copy/copy.mjs +1 -1
  95. package/api/template/template-integration.test.mjs +1 -1
  96. package/api/theme/_adapter.d.mts +16 -0
  97. package/api/theme/_adapter.mjs +32 -5
  98. package/api/theme/build/build.d.mts +2 -1
  99. package/api/theme/build/build.mjs +70 -14
  100. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  101. package/api/theme/build/font-warning.mjs +5 -3
  102. package/api/theme/build/font-warning.test.mjs +2 -1
  103. package/api/theme/remove/remove.test.mjs +2 -2
  104. package/api/theme/targets/targets.mjs +3 -10
  105. package/api/theme/themeTargets.doc.mjs +4 -0
  106. package/api/theme/use/use.test.mjs +1 -1
  107. package/api/upgrade/_adapter.d.mts +40 -2
  108. package/api/upgrade/_adapter.mjs +118 -5
  109. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  110. package/api/upgrade/run/run.mjs +35 -2
  111. package/api/upgrade/status/status.d.mts +21 -5
  112. package/api/upgrade/status/status.mjs +45 -8
  113. package/api/upgrade/upgrade.doc.mjs +6 -3
  114. package/api/upgrade/upgrade.mjs +5 -1
  115. package/api/upgrade/upgrade.type.d.mts +23 -0
  116. package/api/upgrade/upgrade.type.mjs +16 -1
  117. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  118. package/assets/codemods/registry.mjs +1 -0
  119. package/assets/codemods/runner.mjs +7 -1
  120. package/assets/codemods/transforms/next/index.mjs +1 -11
  121. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  122. package/assets/discover/official-packages.json +367 -0
  123. package/assets/docs/author-a-theme.doc.mjs +2 -2
  124. package/assets/docs/browser-support.doc.mjs +1 -1
  125. package/assets/docs/getting-started.doc.mjs +23 -3
  126. package/assets/docs/styling-overview.doc.mjs +1 -1
  127. package/assets/docs/theme.doc.mjs +1 -1
  128. package/assets/docs/tree/add-a-component.doc.mjs +12 -2
  129. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  130. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  131. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  132. package/assets/docs/tree/block-template.doc.mjs +6 -18
  133. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  134. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  135. package/assets/docs/tree/check-the-props.doc.mjs +8 -3
  136. package/assets/docs/tree/check-your-docs.doc.mjs +30 -13
  137. package/assets/docs/tree/checks.doc.mjs +17 -17
  138. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  139. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  140. package/assets/docs/tree/cli.doc.mjs +6 -0
  141. package/assets/docs/tree/codemods.doc.mjs +17 -7
  142. package/assets/docs/tree/commands.doc.mjs +7 -1
  143. package/assets/docs/tree/component-family.doc.mjs +3 -5
  144. package/assets/docs/tree/component-imports.doc.mjs +13 -3
  145. package/assets/docs/tree/components.doc.mjs +7 -1
  146. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  147. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  148. package/assets/docs/tree/configuration.doc.mjs +7 -3
  149. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  150. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  151. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  152. package/assets/docs/tree/docs.doc.mjs +9 -3
  153. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  154. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  155. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  156. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  157. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  158. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  159. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  160. package/assets/docs/tree/global.doc.mjs +131 -0
  161. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  162. package/assets/docs/tree/install-once.doc.mjs +105 -0
  163. package/assets/docs/tree/integrations.doc.mjs +13 -1
  164. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  165. package/assets/docs/tree/layout.doc.mjs +6 -6
  166. package/assets/docs/tree/links.doc.mjs +6 -5
  167. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  168. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  169. package/assets/docs/tree/migration.doc.mjs +0 -1
  170. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  171. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  172. package/assets/docs/tree/page-template.doc.mjs +3 -7
  173. package/assets/docs/tree/problems.doc.mjs +205 -0
  174. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  175. package/assets/docs/tree/publishing.doc.mjs +6 -6
  176. package/assets/docs/tree/quick-start.doc.mjs +52 -26
  177. package/assets/docs/tree/reference.doc.mjs +192 -0
  178. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  179. package/assets/docs/tree/replace-a-core-template.doc.mjs +9 -11
  180. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  181. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  182. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  183. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  184. package/assets/docs/tree/single-component.doc.mjs +14 -6
  185. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  186. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  187. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  188. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  189. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  190. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  191. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  192. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  193. package/assets/docs/tree/templates.doc.mjs +7 -1
  194. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  195. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  196. package/assets/docs/tree/themes.doc.mjs +4 -0
  197. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  198. package/assets/docs/tree/troubleshooting.doc.mjs +22 -14
  199. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  200. package/assets/docs/tree/updates.doc.mjs +92 -0
  201. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  202. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  203. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  204. package/assets/docs/tree/versioning.doc.mjs +9 -9
  205. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  206. package/assets/docs/tree/write-the-template-file.doc.mjs +5 -5
  207. package/assets/docs/working-with-ai.doc.mjs +23 -9
  208. package/authoring/codemod/codemod.doc.mjs +7 -3
  209. package/authoring/config/config.doc.mjs +9 -6
  210. package/authoring/config/type.ts +4 -2
  211. package/authoring/debug/parse.d.mts +5 -5
  212. package/authoring/discover/discover.doc.mjs +7 -1
  213. package/authoring/doctypes/_schema.d.mts +8 -3
  214. package/authoring/doctypes/_schema.mjs +1 -0
  215. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  216. package/authoring/doctypes/command/command.doc.mjs +20 -12
  217. package/authoring/doctypes/command/type.ts +14 -0
  218. package/authoring/doctypes/component/component.doc.mjs +29 -26
  219. package/authoring/doctypes/component/type.ts +15 -5
  220. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  221. package/authoring/doctypes/function/function.doc.mjs +9 -8
  222. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  223. package/authoring/doctypes/hook/type.ts +4 -0
  224. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  225. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  226. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  227. package/authoring/doctypes/template/template.doc.mjs +2 -2
  228. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  229. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  230. package/authoring/identity/identity.doc.mjs +3 -3
  231. package/authoring/index.d.ts +1 -0
  232. package/authoring/integration/integration.doc.mjs +22 -5
  233. package/authoring/integration/parse.test.mjs +11 -0
  234. package/authoring/integration/schema.d.mts +14 -3
  235. package/authoring/integration/schema.mjs +33 -2
  236. package/authoring/integration/type.ts +8 -0
  237. package/clients/cli/bin/astryx.mjs +20 -3
  238. package/clients/cli/cli-command.test.mjs +166 -0
  239. package/clients/cli/commands/blog.doc.mjs +1 -0
  240. package/clients/cli/commands/build-theme.batch-report.test.mjs +14 -2
  241. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  242. package/clients/cli/commands/build-theme.mjs +25 -12
  243. package/clients/cli/commands/build.doc.mjs +1 -0
  244. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  245. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  246. package/clients/cli/commands/component.doc.mjs +1 -0
  247. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  248. package/clients/cli/commands/discover.doc.mjs +9 -1
  249. package/clients/cli/commands/discover.mjs +144 -93
  250. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  251. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  252. package/clients/cli/commands/docs.doc.mjs +1 -0
  253. package/clients/cli/commands/docs.mjs +49 -19
  254. package/clients/cli/commands/docs.test.mjs +2 -2
  255. package/clients/cli/commands/doctor-integration-components.doc.mjs +7 -3
  256. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  257. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  258. package/clients/cli/commands/doctor-integration-validate.doc.mjs +13 -2
  259. package/clients/cli/commands/doctor-integration.test.mjs +52 -4
  260. package/clients/cli/commands/doctor.doc.mjs +12 -2
  261. package/clients/cli/commands/doctor.mjs +102 -7
  262. package/clients/cli/commands/doctor.test.mjs +10 -4
  263. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  264. package/clients/cli/commands/gap-report.mjs +24 -2
  265. package/clients/cli/commands/gap-report.test.mjs +79 -0
  266. package/clients/cli/commands/hook.doc.mjs +1 -0
  267. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  268. package/clients/cli/commands/init.doc.mjs +29 -3
  269. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  270. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  271. package/clients/cli/commands/integration-check.doc.mjs +10 -2
  272. package/clients/cli/commands/integration-check.test.mjs +175 -1
  273. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  274. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  275. package/clients/cli/commands/integration.mjs +15 -9
  276. package/clients/cli/commands/json-contract.test.mjs +11 -2
  277. package/clients/cli/commands/manifest.doc.mjs +1 -0
  278. package/clients/cli/commands/outside-project.test.mjs +774 -0
  279. package/clients/cli/commands/search.doc.mjs +1 -0
  280. package/clients/cli/commands/search.mjs +16 -11
  281. package/clients/cli/commands/search.test.mjs +429 -296
  282. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  283. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  284. package/clients/cli/commands/swizzle.mjs +5 -3
  285. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  286. package/clients/cli/commands/template.doc.mjs +1 -0
  287. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  288. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  289. package/clients/cli/commands/theme-build.doc.mjs +1 -0
  290. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  291. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  292. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  293. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  294. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  295. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  296. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  297. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  298. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  299. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  300. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  301. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  302. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  303. package/clients/cli/index.mjs +111 -16
  304. package/clients/cli/lib/cli-error.mjs +4 -1
  305. package/clients/cli/lib/component-format.test.mjs +1 -1
  306. package/clients/cli/lib/define-command.mjs +2 -2
  307. package/clients/cli/lib/json-shim.mjs +3 -1
  308. package/clients/cli/lib/manifest.d.ts +9 -0
  309. package/clients/cli/lib/manifest.mjs +17 -1
  310. package/clients/cli/lib/manifest.test.mjs +25 -0
  311. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  312. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  313. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  314. package/foundation/agent-docs/agent-docs.mjs +11 -9
  315. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  316. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  317. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  318. package/foundation/config/project.d.mts +35 -3
  319. package/foundation/config/project.mjs +145 -8
  320. package/foundation/config/project.test.mjs +66 -0
  321. package/foundation/config/theme-state.mjs +5 -4
  322. package/foundation/discovery/authoring-self-docs.mjs +1 -1
  323. package/foundation/discovery/cli-self-docs.mjs +20 -3
  324. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  325. package/foundation/discovery/component-replacement.mjs +2 -1
  326. package/foundation/discovery/copied-passages.d.mts +216 -0
  327. package/foundation/discovery/copied-passages.mjs +720 -0
  328. package/foundation/discovery/copied-passages.test.mjs +124 -0
  329. package/foundation/discovery/doc-consistency.d.mts +34 -0
  330. package/foundation/discovery/doc-consistency.mjs +513 -0
  331. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  332. package/foundation/discovery/doc-fluff.d.mts +112 -0
  333. package/foundation/discovery/doc-fluff.mjs +328 -0
  334. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  335. package/foundation/discovery/doc-types.d.mts +1 -1
  336. package/foundation/discovery/doc-types.mjs +15 -8
  337. package/foundation/discovery/doc-types.test.mjs +94 -2
  338. package/foundation/discovery/docs-discovery.d.mts +7 -0
  339. package/foundation/discovery/docs-discovery.mjs +26 -2
  340. package/foundation/discovery/project-typescript.d.mts +11 -2
  341. package/foundation/discovery/project-typescript.mjs +32 -4
  342. package/foundation/discovery/prop-drift.d.mts +26 -7
  343. package/foundation/discovery/prop-drift.mjs +173 -8
  344. package/foundation/discovery/prop-drift.test.mjs +120 -2
  345. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  346. package/foundation/discovery/runner-allowlist.mjs +53 -0
  347. package/foundation/discovery/stray-pointers.d.mts +52 -0
  348. package/foundation/discovery/stray-pointers.mjs +293 -0
  349. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  350. package/foundation/discovery/template-needs.mjs +5 -2
  351. package/foundation/discovery/theme-imports.mjs +8 -4
  352. package/foundation/discovery/theme-imports.test.mjs +1 -1
  353. package/foundation/discovery/type-text.mjs +1 -1
  354. package/foundation/discovery/type-text.test.mjs +1 -1
  355. package/foundation/doc-compiler/doc-loads.test.mjs +42 -2
  356. package/foundation/doc-compiler/inputs.mjs +4 -1
  357. package/foundation/doc-compiler/links.d.mts +32 -0
  358. package/foundation/doc-compiler/links.mjs +163 -26
  359. package/foundation/doc-compiler/links.test.mjs +97 -4
  360. package/foundation/doc-compiler/read.d.mts +1 -1
  361. package/foundation/doc-compiler/tree.mjs +30 -1
  362. package/foundation/doc-compiler/tree.test.mjs +8 -1
  363. package/foundation/env/cli-command.d.mts +83 -0
  364. package/foundation/env/cli-command.mjs +152 -0
  365. package/foundation/env/cli-command.test.mjs +129 -0
  366. package/foundation/env/node-version.test.mjs +15 -3
  367. package/foundation/env/package-manager.d.mts +93 -9
  368. package/foundation/env/package-manager.global.test.mjs +66 -0
  369. package/foundation/env/package-manager.mjs +184 -13
  370. package/foundation/env/package-manager.test.mjs +341 -15
  371. package/foundation/env/project-scope.d.mts +9 -0
  372. package/foundation/env/project-scope.mjs +33 -0
  373. package/foundation/env/search-path.d.mts +42 -0
  374. package/foundation/env/search-path.mjs +205 -0
  375. package/foundation/env/search-path.test.mjs +122 -0
  376. package/foundation/env/user-dirs.d.mts +39 -0
  377. package/foundation/env/user-dirs.mjs +93 -0
  378. package/foundation/env/user-dirs.test.mjs +72 -0
  379. package/foundation/env/user-settings.d.mts +46 -0
  380. package/foundation/env/user-settings.mjs +106 -0
  381. package/foundation/env/user-settings.test.mjs +64 -0
  382. package/foundation/fs/file-protection.mjs +15 -1
  383. package/foundation/fs/module-loader.d.mts +2 -1
  384. package/foundation/fs/module-loader.mjs +60 -4
  385. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  386. package/foundation/fs/module-loader.test.mjs +194 -0
  387. package/foundation/fs/path-compare.d.mts +33 -0
  388. package/foundation/fs/path-compare.mjs +73 -0
  389. package/foundation/fs/paths.d.mts +57 -1
  390. package/foundation/fs/paths.mjs +131 -3
  391. package/foundation/fs/paths.test.mjs +10 -1
  392. package/foundation/integrations/computer-integrations.d.mts +78 -0
  393. package/foundation/integrations/computer-integrations.mjs +316 -0
  394. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  395. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  396. package/foundation/integrations/contribution-inventory.mjs +50 -2
  397. package/foundation/integrations/integration-warnings.mjs +3 -2
  398. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  399. package/foundation/integrations/integrations.d.mts +25 -1
  400. package/foundation/integrations/integrations.mjs +46 -3
  401. package/foundation/integrations/issue.ts +6 -0
  402. package/foundation/integrations/validate-contributions.d.mts +2 -0
  403. package/foundation/integrations/validate-contributions.mjs +41 -1
  404. package/foundation/release/agent-text-worker.d.mts +4 -0
  405. package/foundation/release/agent-text-worker.mjs +52 -0
  406. package/foundation/release/cache.d.mts +310 -0
  407. package/foundation/release/cache.mjs +1216 -0
  408. package/foundation/release/cache.test.mjs +920 -0
  409. package/foundation/release/choose.d.mts +207 -0
  410. package/foundation/release/choose.mjs +677 -0
  411. package/foundation/release/choose.test.mjs +427 -0
  412. package/foundation/release/core-worker.d.mts +4 -0
  413. package/foundation/release/core-worker.mjs +38 -0
  414. package/foundation/release/global-state.d.mts +259 -0
  415. package/foundation/release/global-state.mjs +472 -0
  416. package/foundation/release/global-state.test.mjs +381 -0
  417. package/foundation/release/handoff.d.mts +60 -0
  418. package/foundation/release/handoff.e2e.test.mjs +278 -0
  419. package/foundation/release/handoff.mjs +357 -0
  420. package/foundation/release/lock-lease.d.mts +23 -0
  421. package/foundation/release/lock-lease.mjs +80 -0
  422. package/foundation/release/notices.d.mts +253 -0
  423. package/foundation/release/notices.mjs +596 -0
  424. package/foundation/release/notices.test.mjs +565 -0
  425. package/foundation/release/npm-guard.d.mts +4 -0
  426. package/foundation/release/npm-guard.mjs +126 -0
  427. package/foundation/release/project.d.mts +141 -0
  428. package/foundation/release/project.mjs +466 -0
  429. package/foundation/release/project.test.mjs +288 -0
  430. package/foundation/release/refresh-worker.d.mts +4 -0
  431. package/foundation/release/refresh-worker.mjs +27 -0
  432. package/foundation/release/run-context.d.mts +47 -0
  433. package/foundation/release/run-context.mjs +149 -0
  434. package/foundation/release/update-notice-env.d.mts +28 -0
  435. package/foundation/release/update-notice-env.mjs +39 -0
  436. package/foundation/response/base.d.ts +26 -3
  437. package/foundation/response/error-codes.d.mts +3 -1
  438. package/foundation/response/error-codes.doc.mjs +12 -0
  439. package/foundation/response/error-codes.mjs +17 -0
  440. package/foundation/response/error-codes.test.mjs +2 -0
  441. package/foundation/response/json-contract.test.mjs +7 -2
  442. package/foundation/response/json.mjs +4 -1
  443. package/foundation/response/response-types.doc.mjs +17 -11
  444. package/foundation/response/response-types.doc.test.mjs +46 -0
  445. package/foundation/response/response.doc.mjs +3 -3
  446. package/foundation/xle/browser-imports.test.mjs +74 -0
  447. package/package.json +11 -11
  448. package/scripts/postinstall.mjs +24 -5
  449. /package/assets/codemods/transforms/{next → v0.7.2}/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +0 -0
  450. /package/assets/codemods/transforms/{next → v0.7.2}/migrate-list-edge-compensation-to-edge.mjs +0 -0
@@ -0,0 +1,328 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * Fluff in docs: padding phrases that add words and no meaning, and guides
5
+ * whose prose is mostly function words. Docs are read by people and agents
6
+ * with a budget, so every word should carry something.
7
+ *
8
+ * Only prose is read (the same strings copied passages compare), and inline
9
+ * code and quoted text are set aside, so a phrase inside a command, or one
10
+ * quoted as an example, never counts.
11
+ */
12
+
13
+ import fs from 'node:fs';
14
+
15
+ import {builtinGuideFiles} from './docs-discovery.mjs';
16
+ import {
17
+ contentWords,
18
+ fileWords,
19
+ lineAtOffset,
20
+ locateWords,
21
+ loadQuietly,
22
+ proseStrings,
23
+ passageWords,
24
+ textWords,
25
+ } from './copied-passages.mjs';
26
+
27
+ /**
28
+ * Padding phrases: each can be cut, or replaced by a shorter word, without
29
+ * losing meaning. Only phrases that are padding in every use are listed;
30
+ * words such as "just", "simply" or "really", and phrases such as "whether or
31
+ * not" or "keep in mind", which carry meaning in a correct sentence ("not
32
+ * simply a wrapper", "regardless of whether"), are not.
33
+ */
34
+ export const FILLER_PHRASES = [
35
+ "it's important to note",
36
+ 'it is important to note',
37
+ 'important to note that',
38
+ "it's worth noting",
39
+ 'it is worth noting',
40
+ 'worth noting that',
41
+ 'it should be noted',
42
+ 'please note',
43
+ 'note that',
44
+ 'as mentioned above',
45
+ 'as mentioned earlier',
46
+ 'as mentioned before',
47
+ 'as noted above',
48
+ 'as you can see',
49
+ 'needless to say',
50
+ 'in order to',
51
+ 'due to the fact that',
52
+ 'for the purpose of',
53
+ 'at this point in time',
54
+ 'in other words',
55
+ 'basically',
56
+ 'essentially',
57
+ 'obviously',
58
+ ];
59
+
60
+ /** What to write instead, where a phrase is not simply cut. */
61
+ const INSTEAD = {
62
+ 'in order to': 'to',
63
+ 'due to the fact that': 'because',
64
+ 'for the purpose of': 'to or for',
65
+ 'at this point in time': 'now',
66
+ };
67
+
68
+ /** A guide shorter than this many prose words is not scored for density. */
69
+ export const DENSITY_MIN_WORDS = 80;
70
+
71
+ /**
72
+ * A guide whose share of content words is below this is mostly glue. Core's
73
+ * own guides score from about 0.51 up, with a median near 0.62.
74
+ */
75
+ export const MIN_DENSITY = 0.5;
76
+
77
+ /**
78
+ * @param {string} phrase
79
+ * @returns {RegExp}
80
+ */
81
+ function phrasePattern(phrase) {
82
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
83
+ // "note that" is padding as a sentence opener ("Note that X"), not inside
84
+ // one ("a note that explains").
85
+ return phrase === 'note that'
86
+ ? new RegExp(`(?:^|[.;:!?]\\s+)${escaped}\\b`, 'gi')
87
+ : // A hyphen joins words ("non-obviously"), so it bounds no phrase.
88
+ new RegExp(`(?<![a-z'-])${escaped}(?![a-z-])`, 'gi');
89
+ }
90
+
91
+ const PATTERNS = FILLER_PHRASES.map(phrase => ({
92
+ phrase,
93
+ pattern: phrasePattern(phrase),
94
+ }));
95
+
96
+ /**
97
+ * A prose string with what is not prose blanked out, length kept: `{@link}`
98
+ * pointers, inline code, and quoted text. A quoted phrase is mentioned, not
99
+ * used ("cut 'in order to'").
100
+ *
101
+ * @param {string} text
102
+ * @returns {string}
103
+ */
104
+ export function maskProse(text) {
105
+ // A quoted phrase is mentioned, not used ("cut 'in order to'"), so quotes
106
+ // are set aside along with inline code. Each mask keeps the text's length,
107
+ // so an offset in the masked text is an offset in the text.
108
+ const blank = (/** @type {string} */ span) => ' '.repeat(span.length);
109
+ return (
110
+ text
111
+ // A pointer names a doc, and its target is no prose ("{@link
112
+ // generic:obviously-fast}").
113
+ .replace(/\{@link\s[^}]*\}/g, blank)
114
+ .replace(/`[^`]*`/g, blank)
115
+ .replace(/"[^"]*"|“[^”]*”|‘[^’]*’/g, blank)
116
+ .replace(/’/g, "'")
117
+ // A single-quoted span opens and closes beside a non-letter, so the
118
+ // apostrophe in "don't" or "it's" opens nothing.
119
+ .replace(/(?<![A-Za-z])'[^']*'(?![A-Za-z])/g, blank)
120
+ );
121
+ }
122
+
123
+ /**
124
+ * Each padding phrase in a text, outside inline code and quotes, in order,
125
+ * with its offset in the text.
126
+ *
127
+ * `thatAfter` is true when the phrase is followed by a "that" a reader cuts
128
+ * with it ("It is worth noting that the cache is shared").
129
+ *
130
+ * @param {string} text
131
+ * @returns {{phrase: string, at: number, thatAfter: boolean}[]}
132
+ */
133
+ export function fillerAt(text) {
134
+ const prose = maskProse(text);
135
+ /** @type {{phrase: string, at: number, thatAfter: boolean}[]} */
136
+ const found = [];
137
+ for (const {phrase, pattern} of PATTERNS) {
138
+ for (const match of prose.matchAll(pattern)) {
139
+ // A phrase inside a longer listed one ("note that" in "worth noting
140
+ // that") is the longer one's.
141
+ const at = match.index + match[0].toLowerCase().indexOf(phrase);
142
+ if (
143
+ !found.some(each => at >= each.at && at < each.at + each.phrase.length)
144
+ ) {
145
+ found.push({
146
+ phrase,
147
+ at,
148
+ // Only a phrase that ends in note or noting leads into a "that"
149
+ // clause the reader cuts with it; "Obviously that works" and "As
150
+ // noted above that flag" keep their "that".
151
+ thatAfter:
152
+ /\bnot(?:e|ed|ing)$/.test(phrase) &&
153
+ !phrase.endsWith('that') &&
154
+ /^\s+that\b/i.test(prose.slice(at + phrase.length)),
155
+ });
156
+ }
157
+ }
158
+ }
159
+ return found.sort((a, b) => a.at - b.at);
160
+ }
161
+
162
+ /**
163
+ * Each padding phrase in a text, outside inline code and quotes, in order.
164
+ *
165
+ * @param {string} text
166
+ * @returns {string[]}
167
+ */
168
+ export function fillerIn(text) {
169
+ return fillerAt(text).map(each => each.phrase);
170
+ }
171
+
172
+ /**
173
+ * The share of a doc's prose words that are content words.
174
+ *
175
+ * @param {{word: string}[]} words
176
+ * @returns {number}
177
+ */
178
+ export function density(words) {
179
+ if (words.length === 0) return 1;
180
+ let content = 0;
181
+ for (let i = 0; i < words.length; i++) {
182
+ if (contentWords(words, i, 1) === 1) content++;
183
+ }
184
+ return content / words.length;
185
+ }
186
+
187
+ /**
188
+ * A number rounded down to `places` decimals, so a share under the floor
189
+ * never reads as the floor.
190
+ *
191
+ * @param {number} value
192
+ * @param {number} places
193
+ * @returns {number}
194
+ */
195
+ export function floorTo(value, places) {
196
+ const scale = 10 ** places;
197
+ return Math.floor(value * scale + 1e-9) / scale;
198
+ }
199
+
200
+ /**
201
+ * A share as a percent with one decimal, rounded down ("49.5%").
202
+ *
203
+ * @param {number} share
204
+ * @returns {string}
205
+ */
206
+ export function sharePercent(share) {
207
+ return `${floorTo(share * 100, 1).toFixed(1)}%`;
208
+ }
209
+
210
+ /**
211
+ * The words a topic's density is measured on: its prose, without what the
212
+ * phrase check also leaves unread (links, inline code, quoted text).
213
+ *
214
+ * @param {unknown} doc
215
+ * @returns {{word: string}[]}
216
+ */
217
+ export function densityWords(doc) {
218
+ return proseStrings(doc).flatMap(({text}) =>
219
+ passageWords(maskProse(text)).map(word => ({word})),
220
+ );
221
+ }
222
+
223
+ /**
224
+ * The median density of Core's own guides of at least
225
+ * {@link DENSITY_MIN_WORDS} words.
226
+ *
227
+ * @returns {Promise<number>}
228
+ */
229
+ export async function coreDensityMedian() {
230
+ // Core's guides are measured the way a package's topics are.
231
+ /** @type {number[]} */
232
+ const scores = [];
233
+ for (const file of builtinGuideFiles()) {
234
+ const doc = await loadQuietly(file);
235
+ if (typeof doc?.name !== 'string') continue;
236
+ const words = densityWords(doc);
237
+ if (words.length >= DENSITY_MIN_WORDS) scores.push(density(words));
238
+ }
239
+ scores.sort((a, b) => a - b);
240
+ return scores.length === 0 ? 0 : scores[Math.floor(scores.length / 2)];
241
+ }
242
+
243
+ /**
244
+ * Fluff in a package's topics: every padding phrase, with its section and
245
+ * file line, and every topic of at least {@link DENSITY_MIN_WORDS} words
246
+ * whose density is below {@link MIN_DENSITY}.
247
+ *
248
+ * @param {{records: {name: string, path: string}[], guides?: {name: string, ref?: {topicFile?: string}}[]}} discovered
249
+ * the package's docs, as discoverIntegrationDocs returns them
250
+ * @returns {Promise<import('../../api/integration/authoring-checks.type.mjs').DocFluff[]>}
251
+ */
252
+ export async function findFluff(discovered) {
253
+ const topics = [
254
+ ...discovered.records.map(record => ({
255
+ name: record.name,
256
+ file: record.path,
257
+ })),
258
+ ...(discovered.guides ?? []).map(guide => ({
259
+ name: guide.name,
260
+ file: guide.ref?.topicFile,
261
+ })),
262
+ ];
263
+ /** @type {import('../../api/integration/authoring-checks.type.mjs').DocFluff[]} */
264
+ const found = [];
265
+ /** @type {number | null} */
266
+ let median = null;
267
+ for (const {name, file} of topics) {
268
+ const doc = file ? await loadQuietly(file) : null;
269
+ if (!doc || !file) continue;
270
+ /** @type {{word: string, line: number}[]} */
271
+ let source = [];
272
+ try {
273
+ source = fileWords(fs.readFileSync(file, 'utf8'));
274
+ } catch {
275
+ // No line numbers; each finding still names its section.
276
+ }
277
+ // Each string is found by its whole run of words, from where the last
278
+ // one was found, so two strings that open alike keep their own lines.
279
+ let cursor = 0;
280
+ for (const {text, section} of proseStrings(doc)) {
281
+ const words = textWords(text);
282
+ let index = locateWords(source, words, cursor);
283
+ if (index < 0) index = locateWords(source, words);
284
+ if (index >= 0) cursor = index + words.length;
285
+ for (const {phrase, at, thatAfter} of fillerAt(text)) {
286
+ const line = index < 0 ? null : lineAtOffset(source, index, words, at);
287
+ const where = section ? ` (section "${section}")` : '';
288
+ const instead = /** @type {Record<string, string>} */ (INSTEAD)[phrase];
289
+ found.push({
290
+ topic: name,
291
+ kind: 'phrase',
292
+ phrase,
293
+ section,
294
+ file,
295
+ line,
296
+ density: null,
297
+ message:
298
+ `"${name}"${where} pads its prose with "${phrase}"` +
299
+ `${line == null ? '' : ` (line ${line})`}. ` +
300
+ (instead
301
+ ? `Write "${instead}" instead.`
302
+ : thatAfter
303
+ ? 'Cut it and the "that" after it; the sentence says the same without them.'
304
+ : 'Cut it; the sentence says the same without it.'),
305
+ });
306
+ }
307
+ }
308
+ const words = densityWords(doc);
309
+ if (words.length < DENSITY_MIN_WORDS) continue;
310
+ const score = density(words);
311
+ if (score >= MIN_DENSITY) continue;
312
+ median ??= await coreDensityMedian();
313
+ found.push({
314
+ topic: name,
315
+ kind: 'density',
316
+ phrase: null,
317
+ section: null,
318
+ file,
319
+ line: null,
320
+ density: floorTo(score, 3),
321
+ message:
322
+ `"${name}" is mostly glue: ${sharePercent(score)} of its ${words.length} prose words carry content, ` +
323
+ `under the ${Math.round(MIN_DENSITY * 100)}% floor (Core's guides have a median of ${Math.round(median * 100)}%). ` +
324
+ 'Cut filler and restated context until each sentence states one thing.',
325
+ });
326
+ }
327
+ return found;
328
+ }
@@ -0,0 +1,142 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, expect, it} from 'vitest';
4
+
5
+ import {
6
+ density,
7
+ densityWords,
8
+ fillerAt,
9
+ fillerIn,
10
+ floorTo,
11
+ sharePercent,
12
+ } from './doc-fluff.mjs';
13
+ import {passageWords} from './copied-passages.mjs';
14
+
15
+ const words = (/** @type {string} */ text) =>
16
+ passageWords(text).map(word => ({word}));
17
+
18
+ describe('fillerIn', () => {
19
+ it('finds padding phrases in order, the longest one only', () => {
20
+ expect(
21
+ fillerIn(
22
+ 'In order to start, it is worth noting that you basically run it. Note that it builds.',
23
+ ),
24
+ ).toEqual(['in order to', 'it is worth noting', 'basically', 'note that']);
25
+ });
26
+
27
+ it('leaves inline code, quotes, a noun "note", and ambiguous words alone', () => {
28
+ expect(
29
+ fillerIn(
30
+ 'Run `basically --in order to`. Cut "in order to" and “basically”. Add a note that explains it. Copy just this one file; it really is the source.',
31
+ ),
32
+ ).toEqual([]);
33
+ });
34
+
35
+ it('sets aside single-quoted text, but not an apostrophe', () => {
36
+ expect(
37
+ fillerIn(
38
+ "Avoid ‘basically’ and ‘obviously’. Don't write 'it is worth noting that' in a guide.",
39
+ ),
40
+ ).toEqual([]);
41
+ expect(fillerIn("Don't pad: it's important to note the order.")).toEqual([
42
+ "it's important to note",
43
+ ]);
44
+ });
45
+
46
+ it('lists no phrase that carries meaning in a correct sentence', () => {
47
+ expect(
48
+ fillerIn(
49
+ 'It is not simply a wrapper. Every app gets a theme whether or not anyone picks one. Keep in mind the order. Feel free to skip it.',
50
+ ),
51
+ ).toEqual([]);
52
+ });
53
+
54
+ it('reads a curly apostrophe like a straight one', () => {
55
+ expect(fillerIn('It’s important to note the order.')).toEqual([
56
+ "it's important to note",
57
+ ]);
58
+ });
59
+ });
60
+
61
+ describe('fillerAt', () => {
62
+ it('reads no phrase inside a link target or a hyphenated word', () => {
63
+ expect(
64
+ fillerIn(
65
+ 'Read {@link generic:obviously-fast} before you tune the build.',
66
+ ),
67
+ ).toEqual([]);
68
+ expect(fillerIn('A non-obviously slow build still passes.')).toEqual([]);
69
+ expect(fillerIn('It is obviously slow.')).toEqual(['obviously']);
70
+ });
71
+
72
+ it('marks a "that" the reader cuts with the phrase', () => {
73
+ expect(
74
+ fillerAt('It is worth noting that the cache is shared.').map(
75
+ each => each.thatAfter,
76
+ ),
77
+ ).toEqual([true]);
78
+ expect(
79
+ fillerAt('Basically, the cache is shared.').map(each => each.thatAfter),
80
+ ).toEqual([false]);
81
+ // Only a note or noting phrase leads a clause the reader cuts.
82
+ expect(
83
+ fillerAt('Obviously that works only in a browser.').map(
84
+ each => each.thatAfter,
85
+ ),
86
+ ).toEqual([false]);
87
+ expect(
88
+ fillerAt('As noted above that flag is required.').map(
89
+ each => each.thatAfter,
90
+ ),
91
+ ).toEqual([false]);
92
+ });
93
+ });
94
+
95
+ describe('densityWords', () => {
96
+ it('measures prose only, not quoted text, inline code or links', () => {
97
+ const doc = {
98
+ sections: [
99
+ {
100
+ title: 'Overview',
101
+ content: [
102
+ {
103
+ type: 'prose',
104
+ text: 'Theme tokens set colors. Write "it is in the" or `of the` as {@link generic:theme} shows.',
105
+ },
106
+ ],
107
+ },
108
+ ],
109
+ };
110
+ expect(densityWords(doc).map(each => each.word)).toEqual([
111
+ 'theme',
112
+ 'tokens',
113
+ 'set',
114
+ 'colors.',
115
+ 'write',
116
+ 'or',
117
+ 'as',
118
+ 'shows.',
119
+ ]);
120
+ });
121
+ });
122
+
123
+ describe('share rounding', () => {
124
+ it('rounds a share under the floor down, never up to it', () => {
125
+ expect(floorTo(0.4995, 3)).toBe(0.499);
126
+ expect(sharePercent(0.4995)).toBe('49.9%');
127
+ expect(sharePercent(0.5)).toBe('50.0%');
128
+ });
129
+ });
130
+
131
+ describe('density', () => {
132
+ it('scores a padded sentence well below a dense one', () => {
133
+ const padded = words(
134
+ 'So that is why it is the one that you would want to use if you can.',
135
+ );
136
+ const dense = words(
137
+ 'Name each codemod folder after the Core version whose upgrade runs it.',
138
+ );
139
+ expect(density(padded)).toBeLessThan(0.3);
140
+ expect(density(dense)).toBeGreaterThan(0.6);
141
+ });
142
+ });
@@ -54,7 +54,7 @@ export function checkDocTypes(files: DocFile[], options: {
54
54
  * or not fully checked
55
55
  */
56
56
  /** The line both surfaces print when the compiler is missing. */
57
- export const DOC_TYPES_TYPESCRIPT_MISSING_REASON: "TypeScript is not installed in this project, so docs were not checked against the authoring types. Install typescript and run this check again.";
57
+ export const DOC_TYPES_TYPESCRIPT_MISSING_REASON: "TypeScript is not installed in this project, so docs were not checked against the authoring types. On TypeScript 7, add its TypeScript 6 API package (`npm install -D @typescript/typescript6`); otherwise install TypeScript 5.0 through 6 (`npm install -D typescript@6`). Then run this check again.";
58
58
  export type DocRoot = "components" | "hooks" | "templates" | "docs" | "docs-overlay" | "themes";
59
59
  export type DocFile = {
60
60
  /**
@@ -41,6 +41,7 @@ import * as path from 'node:path';
41
41
  import {fileURLToPath} from 'node:url';
42
42
  import {
43
43
  resolveProjectTypeScript,
44
+ TYPESCRIPT_FIX,
44
45
  unusableTypeScriptReason,
45
46
  } from './project-typescript.mjs';
46
47
 
@@ -91,8 +92,7 @@ import {
91
92
  */
92
93
 
93
94
  /** The line both surfaces print when the compiler is missing. */
94
- export const DOC_TYPES_TYPESCRIPT_MISSING_REASON =
95
- 'TypeScript is not installed in this project, so docs were not checked against the authoring types. Install typescript and run this check again.';
95
+ export const DOC_TYPES_TYPESCRIPT_MISSING_REASON = `TypeScript is not installed in this project, so docs were not checked against the authoring types. ${TYPESCRIPT_FIX}`;
96
96
 
97
97
  const AUTHORING = '@astryxdesign/cli/authoring';
98
98
  const AUTHORING_TYPES = path.resolve(
@@ -827,16 +827,23 @@ function findingsFor(ts, diagnostic, astPath, where) {
827
827
  named != null && /^[A-Za-z_$][\w$]*$/.test(named)
828
828
  ? named
829
829
  : where.typeName;
830
+ // One diagnostic can list several missing fields; each finding's message
831
+ // names only its own field, so two findings never share one message.
832
+ const source = /^Type '(.*)' is missing the following properties/.exec(
833
+ leaf,
834
+ )?.[1];
830
835
  return missing.map(name => {
831
- const field = formatPath([
832
- ...parts,
833
- unquote(/** @type {string} */ (name)),
834
- ]);
836
+ const key = unquote(/** @type {string} */ (name));
837
+ const field = formatPath([...parts, key]);
838
+ const message =
839
+ missing.length > 1
840
+ ? `Property '${key}' is missing${source == null ? '' : ` in type '${source}'`} but required in type '${ownerType}'.`
841
+ : leaf;
835
842
  return {
836
843
  ...base,
837
844
  field,
838
845
  problem: /** @type {const} */ ('missing_field'),
839
- message: shorten(leaf),
846
+ message: shorten(message),
840
847
  fix: `Add \`${field}\`: ${shorten(ownerType)} requires it.`,
841
848
  };
842
849
  });
@@ -933,7 +940,7 @@ export async function checkDocTypes(files, options) {
933
940
  if (major < needMajor || (major === needMajor && minor < needMinor)) {
934
941
  return {
935
942
  status: 'skipped',
936
- reason: `TypeScript ${version} cannot check ${hasJs ? 'a JavaScript doc' : 'a doc'} against its type (that needs TypeScript ${needMajor}.${needMinor} or later), so docs were not checked. Upgrade typescript and run this check again.`,
943
+ reason: `TypeScript ${version} cannot check ${hasJs ? 'a JavaScript doc' : 'a doc'} against its type (that needs TypeScript ${needMajor}.${needMinor} or later), so docs were not checked. ${TYPESCRIPT_FIX}`,
937
944
  docs: 0,
938
945
  findings: [],
939
946
  unchecked: [],
@@ -40,6 +40,37 @@ function withTypeScript() {
40
40
  );
41
41
  }
42
42
 
43
+ /**
44
+ * TypeScript 7 (no JavaScript compiler API) with the TypeScript 6 API package
45
+ * beside it, which re-exports a real TypeScript.
46
+ */
47
+ function withTypeScript7AndItsTypeScript6Api() {
48
+ const ts7 = path.join(pkg, 'node_modules', 'typescript');
49
+ fs.mkdirSync(ts7, {recursive: true});
50
+ fs.writeFileSync(
51
+ path.join(ts7, 'package.json'),
52
+ JSON.stringify({
53
+ name: 'typescript',
54
+ version: '7.0.2',
55
+ exports: {'./package.json': './package.json'},
56
+ }),
57
+ );
58
+ const ts6 = path.join(pkg, 'node_modules', '@typescript', 'typescript6');
59
+ fs.mkdirSync(ts6, {recursive: true});
60
+ fs.writeFileSync(
61
+ path.join(ts6, 'package.json'),
62
+ JSON.stringify({
63
+ name: '@typescript/typescript6',
64
+ version: '6.0.2',
65
+ main: 'index.js',
66
+ }),
67
+ );
68
+ fs.writeFileSync(
69
+ path.join(ts6, 'index.js'),
70
+ `module.exports = require(${JSON.stringify(TYPESCRIPT_DIR)});\n`,
71
+ );
72
+ }
73
+
43
74
  /**
44
75
  * @param {string} rel
45
76
  * @param {string} text
@@ -117,6 +148,10 @@ describe('checkDocTypes', () => {
117
148
 
118
149
  const report = await check([['components/Badge.doc.mjs', 'components']]);
119
150
 
151
+ // The reason names the version and the fix, so a --strict failure
152
+ // says what to install.
153
+ expect(report.reason).toContain('TypeScript 7.0.0');
154
+ expect(report.reason).toContain('npm install -D @typescript/typescript6');
120
155
  expect(report).toEqual({
121
156
  status: 'skipped',
122
157
  reason: unusableTypeScriptReason('7.0.0', 'docs were not checked'),
@@ -128,6 +163,19 @@ describe('checkDocTypes', () => {
128
163
  }
129
164
  });
130
165
 
166
+ it("checks with TypeScript 7's TypeScript 6 API package when the project has one", async () => {
167
+ write(
168
+ 'components/Badge.doc.mjs',
169
+ `export default ${JSON.stringify(COMPONENT)};\n`,
170
+ );
171
+ withTypeScript7AndItsTypeScript6Api();
172
+
173
+ const report = await check([['components/Badge.doc.mjs', 'components']]);
174
+
175
+ expect(report.status).toBe('checked');
176
+ expect(report.docs).toBe(1);
177
+ });
178
+
131
179
  it('says there is nothing to check when the package has no doc files, with or without TypeScript', async () => {
132
180
  const without = await check([]);
133
181
  withTypeScript();
@@ -517,7 +565,7 @@ describe('checkDocTypes', () => {
517
565
  'components/Badge.doc.mjs',
518
566
  [
519
567
  `export default ${JSON.stringify(COMPONENT)};`,
520
- "export const docsDense = {name: 'Badge', description: 'count chip'};",
568
+ "export const docsDense = {name: 'Badge', displayName: 'Badge', summary: 'chip', description: 'count chip'};",
521
569
  `export const docsZh = ${JSON.stringify({...COMPONENT, displayName: undefined, keyword: []})};`,
522
570
  '',
523
571
  ].join('\n'),
@@ -527,12 +575,56 @@ describe('checkDocTypes', () => {
527
575
 
528
576
  expect(report.docs).toBe(3);
529
577
  expect(summary(report)).toEqual([
530
- 'components/Badge.doc.mjs docsDense name unknown_field',
578
+ 'components/Badge.doc.mjs docsDense summary unknown_field',
531
579
  'components/Badge.doc.mjs docsZh keyword unknown_field',
532
580
  ]);
533
581
  expect(report.findings[0].type).toBe('ComponentTranslationDoc');
534
582
  });
535
583
 
584
+ it('allows a translation overlay to name its component, as Core overlays do', async () => {
585
+ withTypeScript();
586
+ write(
587
+ 'components/Badge.doc.mjs',
588
+ [
589
+ `export default ${JSON.stringify(COMPONENT)};`,
590
+ "export const docsZh = {name: 'Badge', displayName: '徽章', description: '计数'};",
591
+ "export const docsDense = {name: 'Badge', displayName: 'Badge', description: 'count chip'};",
592
+ '',
593
+ ].join('\n'),
594
+ );
595
+
596
+ const report = await check([['components/Badge.doc.mjs', 'components']]);
597
+
598
+ expect(summary(report)).toEqual([]);
599
+ });
600
+
601
+ it('gives each field TypeScript reports missing together a message naming only that field', async () => {
602
+ withTypeScript();
603
+ write(
604
+ 'components/Badge.doc.mjs',
605
+ [
606
+ 'export default {',
607
+ " type: 'component',",
608
+ " name: 'Badge',",
609
+ " props: [{name: 'label', type: 'string', description: 'Text.'}],",
610
+ '};',
611
+ '',
612
+ ].join('\n'),
613
+ );
614
+
615
+ const report = await check([['components/Badge.doc.mjs', 'components']]);
616
+ const missing = report.findings.filter(f => f.problem === 'missing_field');
617
+
618
+ expect(missing.length).toBeGreaterThan(1);
619
+ expect(new Set(missing.map(f => f.message)).size).toBe(missing.length);
620
+ for (const finding of missing) {
621
+ const others = missing.filter(f => f !== finding).map(f => f.field);
622
+ expect(finding.message).toContain(`'${finding.field}'`);
623
+ for (const other of others)
624
+ expect(finding.message).not.toContain(`'${other}'`);
625
+ }
626
+ });
627
+
536
628
  it('checks a template, a docs topic by its stamp, a topic overlay, and a theme', async () => {
537
629
  withTypeScript();
538
630
  write(
@@ -39,6 +39,13 @@
39
39
  * @returns {Record<string, string>} topic name → absolute doc path
40
40
  */
41
41
  export function discoverBuiltinTopics(): Record<string, string>;
42
+ /**
43
+ * The CLI's own guide files: every flat topic and every doc in its docs tree,
44
+ * without the dense, translated, and draft variants.
45
+ *
46
+ * @returns {string[]}
47
+ */
48
+ export function builtinGuideFiles(): string[];
42
49
  /**
43
50
  * Load a topic doc from disk. A `.ts` doc is loaded through jiti, the rest
44
51
  * natively; both the historical `export const docs` and the stamped