@astryxdesign/cli 0.1.7 → 0.1.8-canary.07362a9

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 (389) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +115 -19
  3. package/{src/api → api/blog}/blog.mjs +42 -6
  4. package/{src/api → api/blog}/blog.test.mjs +2 -2
  5. package/api/build/build.mjs +92 -0
  6. package/api/build/build.test.mjs +92 -0
  7. package/{src/api → api/component}/component.mjs +49 -26
  8. package/{src/api → api/discover}/discover.mjs +55 -18
  9. package/{src/api → api/docs}/docs.mjs +47 -21
  10. package/{src/api → api/doctor}/doctor.mjs +13 -10
  11. package/{src/api → api}/error.mjs +2 -2
  12. package/{src/api → api/hook}/hook.mjs +11 -11
  13. package/{src/api → api}/index.mjs +16 -9
  14. package/{src/api → api/integration}/validate-integration.mjs +48 -15
  15. package/{src/api → api/integration}/validate-integration.test.mjs +1 -1
  16. package/{src/api → api/layout}/layout.mjs +66 -17
  17. package/{src/api → api/layout}/layout.test.mjs +1 -1
  18. package/{src/api → api/search}/search.mjs +71 -12
  19. package/api/swizzle/swizzle.mjs +283 -0
  20. package/{src/commands → api/swizzle}/swizzle.test.mjs +26 -1
  21. package/{src/api → api/template}/template-integration.test.mjs +2 -2
  22. package/{src/api → api/template}/template-suffix.test.mjs +3 -3
  23. package/{src/api → api/template}/template.mjs +96 -19
  24. package/{src/api → api/theme}/theme-add.mjs +35 -7
  25. package/{src → authoring}/codemod.mjs +6 -6
  26. package/{src → authoring}/config.test.mjs +1 -1
  27. package/{src → authoring}/doc.mjs +1 -1
  28. package/{src → authoring}/doc.test.mjs +1 -1
  29. package/{src → authoring}/template.mjs +1 -1
  30. package/bin/astryx.mjs +12 -10
  31. package/{src → cli}/commands/blog.mjs +48 -7
  32. package/{src → cli}/commands/build-theme.mjs +133 -115
  33. package/cli/commands/build.mjs +172 -0
  34. package/cli/commands/cli-postinstall.test.mjs +42 -0
  35. package/{src → cli}/commands/component/index.mjs +52 -25
  36. package/{src → cli}/commands/component-ownership.test.mjs +3 -3
  37. package/{src → cli}/commands/component-package.test.mjs +1 -1
  38. package/{src → cli}/commands/component-resolution.test.mjs +2 -2
  39. package/{src → cli}/commands/discover.mjs +23 -12
  40. package/{src → cli}/commands/docs.mjs +42 -10
  41. package/{src → cli}/commands/docs.test.mjs +1 -1
  42. package/{src → cli}/commands/doctor.mjs +3 -3
  43. package/{src → cli}/commands/doctor.test.mjs +2 -2
  44. package/{src → cli}/commands/ensure-core-built.mjs +3 -2
  45. package/{src → cli}/commands/external-showcase.test.mjs +1 -1
  46. package/{src → cli}/commands/hook/index.mjs +40 -17
  47. package/{src → cli}/commands/import-hint-correctness.test.mjs +1 -1
  48. package/cli/commands/init.mjs +195 -0
  49. package/{src → cli}/commands/init.next-steps.test.mjs +1 -1
  50. package/cli/commands/interactive-guard.test.mjs +66 -0
  51. package/{src → cli}/commands/json-contract.test.mjs +2 -2
  52. package/{src → cli}/commands/layout.mjs +59 -19
  53. package/{src → cli}/commands/search.mjs +22 -15
  54. package/{src → cli}/commands/search.test.mjs +1 -1
  55. package/cli/commands/setup-nudge.test.mjs +108 -0
  56. package/cli/commands/swizzle.mjs +103 -0
  57. package/{src → cli}/commands/template.mjs +84 -53
  58. package/{src → cli}/commands/template.path-safety.test.mjs +2 -2
  59. package/{src → cli}/commands/upgrade.integration-policy.test.mjs +1 -1
  60. package/{src → cli}/commands/upgrade.mjs +291 -77
  61. package/cli/commands/upgrade.test.mjs +314 -0
  62. package/{src → cli}/commands/validate-integration.mjs +17 -37
  63. package/{src → cli}/index.mjs +55 -19
  64. package/{src/codemods → codemods}/__tests__/registry.test.mjs +1 -0
  65. package/{src/codemods → codemods}/ensure-jscodeshift.mjs +12 -28
  66. package/{src/codemods → codemods}/integration-discovery.mjs +6 -3
  67. package/{src/codemods → codemods}/integration-discovery.test.mjs +1 -1
  68. package/{src/codemods → codemods}/integration-runner.mjs +8 -3
  69. package/{src/codemods → codemods}/registry.mjs +4 -1
  70. package/{src/codemods → codemods}/run-codemod.mjs +18 -11
  71. package/{src/codemods → codemods}/runner.mjs +24 -14
  72. package/{src/codemods → codemods}/transforms/v0.0.10/remove-size-props.mjs +9 -4
  73. package/{src/codemods → codemods}/transforms/v0.0.12/add-is-icon-only.mjs +27 -22
  74. package/{src/codemods → codemods}/transforms/v0.0.13/icon-name-deprecations.mjs +12 -5
  75. package/{src/codemods → codemods}/transforms/v0.0.13/rename-attachments-to-drawer.mjs +15 -8
  76. package/{src/codemods → codemods}/transforms/v0.0.13/toolbar-density-to-size.mjs +10 -4
  77. package/{src/codemods → codemods}/transforms/v0.0.14/rename-action-props.mjs +11 -5
  78. package/{src/codemods → codemods}/transforms/v0.0.14/rename-section-wash-to-muted.mjs +11 -6
  79. package/{src/codemods → codemods}/transforms/v0.0.14/rename-status-variants.mjs +15 -10
  80. package/{src/codemods → codemods}/transforms/v0.0.15/migrate-item-children-to-endcontent.mjs +13 -8
  81. package/{src/codemods → codemods}/transforms/v0.0.15/migrate-selector-children-to-render-option.mjs +18 -13
  82. package/{src/codemods → codemods}/transforms/v0.0.15/migrate-theme-selectors-to-data-attrs.mjs +8 -4
  83. package/{src/codemods → codemods}/transforms/v0.0.15/rename-date-picker-to-input.mjs +11 -6
  84. package/{src/codemods → codemods}/transforms/v0.0.15/rename-imperative-ref-to-handleRef.mjs +6 -1
  85. package/{src/codemods → codemods}/transforms/v0.0.15/rename-isStreaming-to-isStopShown.mjs +6 -1
  86. package/{src/codemods → codemods}/transforms/v0.0.15/rename-stack-element-to-as.mjs +8 -3
  87. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-badge-dot-to-statusdot.mjs +17 -10
  88. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-gap-to-numeric.mjs +7 -1
  89. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-isFullBleed-to-padding.mjs +9 -4
  90. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-useXDSIcon-to-getIcon.mjs +12 -7
  91. package/{src/codemods → codemods}/transforms/v0.0.2/rename-banner-endButton-to-endContent.mjs +7 -2
  92. package/{src/codemods → codemods}/transforms/v0.0.2/rename-form-tooltip-startIcon.mjs +8 -2
  93. package/{src/codemods → codemods}/transforms/v0.0.2/rename-isShown-to-isOpen.mjs +7 -2
  94. package/{src/codemods → codemods}/transforms/v0.0.2/rename-selector-items-to-options.mjs +7 -2
  95. package/{src/codemods → codemods}/transforms/v0.0.2/rename-sidenav-header-to-heading.mjs +11 -4
  96. package/{src/codemods → codemods}/transforms/v0.0.2/rename-topnav-title-to-heading.mjs +10 -4
  97. package/{src/codemods → codemods}/transforms/v0.0.2/unify-uncontrolled-to-defaultX.mjs +7 -2
  98. package/{src/codemods → codemods}/transforms/v0.0.2/unify-visibility-to-onOpenChange.mjs +10 -5
  99. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-badge-children-to-label.mjs +7 -2
  100. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-collapse-to-collapsible.mjs +9 -3
  101. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-radius-tokens.mjs +11 -5
  102. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-shadow-tokens.mjs +13 -6
  103. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-skeleton-radius.mjs +7 -1
  104. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-token-names.mjs +11 -5
  105. package/{src/codemods → codemods}/transforms/v0.0.7/rename-banner-variant-to-container.mjs +8 -3
  106. package/{src/codemods → codemods}/transforms/v0.0.8/migrate-token-renames.mjs +15 -8
  107. package/{src/codemods → codemods}/transforms/v0.0.8/rename-endslot-to-endcontent.mjs +7 -2
  108. package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/drop-xds-prefix-imports.test.mjs +42 -7
  109. package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +43 -0
  110. package/{src/codemods → codemods}/transforms/v0.1.0/drop-xds-prefix-imports.mjs +117 -13
  111. package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-css-surfaces.mjs +21 -17
  112. package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-declare-module.mjs +7 -2
  113. package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +86 -15
  114. package/{src/codemods → codemods}/transforms/v0.1.2/rename-text-color-active-to-accent.mjs +15 -9
  115. package/{src/codemods → codemods}/transforms/v0.1.3/migrate-layout-components-to-experimental.mjs +17 -12
  116. package/{src/codemods → codemods}/transforms/v0.1.5/rename-switch-label-spacing-default-to-hug.mjs +15 -9
  117. package/{src/codemods → codemods}/transforms/v0.1.7/migrate-table-tableprops-to-direct-props.mjs +17 -10
  118. package/{src/codemods → codemods}/transforms/v0.1.7/rename-table-renderprops-styles-to-xstyle.mjs +18 -13
  119. package/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +161 -0
  120. package/codemods/transforms/v0.1.8/index.mjs +19 -0
  121. package/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +239 -0
  122. package/docs/cli-integrations.doc.mjs +150 -0
  123. package/docs/elevation.doc.mjs +79 -1
  124. package/docs/getting-started.doc.mjs +9 -9
  125. package/docs/internationalization.doc.mjs +92 -46
  126. package/docs/migration.doc.mjs +18 -18
  127. package/docs/principles.doc.dense.mjs +1 -1
  128. package/docs/principles.doc.mjs +6 -6
  129. package/docs/principles.doc.zh.mjs +1 -1
  130. package/docs/styling-libraries.doc.mjs +3 -3
  131. package/docs/styling.doc.mjs +4 -4
  132. package/docs/theme.doc.dense.mjs +2 -2
  133. package/docs/theme.doc.mjs +7 -7
  134. package/docs/theme.doc.zh.mjs +1 -1
  135. package/docs/tokens.doc.mjs +1 -1
  136. package/docs/working-with-ai.doc.mjs +19 -19
  137. package/{src/commands → lib/agent-docs}/agent-docs.mjs +164 -29
  138. package/{src/commands → lib/agent-docs}/agent-docs.path-safety.test.mjs +1 -1
  139. package/{src/commands → lib/agent-docs}/agent-docs.test.mjs +123 -10
  140. package/{src/lib → lib}/cli-error.mjs +2 -2
  141. package/{src/lib → lib}/component-discovery.mjs +44 -2
  142. package/{src/lib → lib}/component-format.mjs +67 -25
  143. package/{src/lib → lib}/component-loader.mjs +27 -13
  144. package/{src/lib → lib}/componentDocOverlay.test.mjs +0 -1
  145. package/{src/lib → lib}/error-codes.test.mjs +1 -1
  146. package/{src/lib → lib}/hook-discovery.mjs +14 -1
  147. package/{src/lib → lib}/hook-format.mjs +29 -5
  148. package/{src/lib → lib}/integration-warnings.mjs +2 -2
  149. package/{src/lib → lib}/integrations.mjs +20 -0
  150. package/{src/lib → lib}/integrations.test.mjs +1 -1
  151. package/{src/lib → lib}/json-contract.test.mjs +1 -1
  152. package/{src/lib → lib}/json-shim.mjs +11 -6
  153. package/{src/lib → lib}/json-shim.test.mjs +1 -1
  154. package/{src/lib → lib}/json.mjs +3 -3
  155. package/{src/lib → lib}/levenshtein.mjs +6 -0
  156. package/{src/lib → lib}/manifest.mjs +7 -5
  157. package/{src/lib → lib}/manifest.test.mjs +2 -2
  158. package/{src/lib → lib}/module-loader.mjs +1 -0
  159. package/{src/lib → lib}/package-scanner.mjs +47 -0
  160. package/{src/lib → lib}/project.mjs +48 -18
  161. package/{src/lib → lib}/project.test.mjs +1 -1
  162. package/{src/lib → lib}/resolve-theme.mjs +5 -0
  163. package/{src/lib → lib}/string-utils.mjs +15 -0
  164. package/lib/term-log.mjs +57 -0
  165. package/{src/lib → lib}/xle/browser.mjs +18 -8
  166. package/{src/lib → lib}/xle/expand.mjs +155 -22
  167. package/{src/lib → lib}/xle/parse.mjs +119 -13
  168. package/{src/lib → lib}/xle/print.mjs +54 -14
  169. package/{src/lib → lib}/xle/registry-core.mjs +28 -3
  170. package/{src/lib → lib}/xle/registry.mjs +9 -4
  171. package/{src/lib → lib}/xle/splice.mjs +13 -2
  172. package/{src/lib → lib}/xle/validate.mjs +95 -14
  173. package/lib/xle/xle-ast.d.ts +214 -0
  174. package/package.json +37 -28
  175. package/scripts/postinstall.mjs +74 -0
  176. package/templates/blocks/components/Avatar/AvatarFallbackChain.tsx +4 -4
  177. package/templates/blocks/components/Avatar/AvatarGroup.tsx +2 -2
  178. package/templates/blocks/components/Avatar/AvatarInitialsFallback.tsx +1 -1
  179. package/templates/blocks/components/Avatar/AvatarShowcase.tsx +5 -5
  180. package/templates/blocks/components/Avatar/AvatarUserCard.tsx +1 -1
  181. package/templates/blocks/components/Avatar/AvatarWithImage.tsx +4 -4
  182. package/templates/blocks/components/Avatar/AvatarWithStatus.tsx +3 -3
  183. package/templates/blocks/components/AvatarGroup/AvatarGroupShowcase.tsx +2 -2
  184. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowCustomText.tsx +1 -1
  185. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowDefault.tsx +1 -1
  186. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowShowcase.tsx +2 -2
  187. package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotShowcase.tsx +3 -3
  188. package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotVariants.tsx +3 -3
  189. package/templates/blocks/components/Banner/BannerFloating.doc.mjs +14 -0
  190. package/templates/blocks/components/Banner/BannerFloating.tsx +16 -0
  191. package/templates/blocks/components/Button/ButtonFloating.doc.mjs +14 -0
  192. package/templates/blocks/components/Button/ButtonFloating.tsx +37 -0
  193. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.doc.mjs +14 -0
  194. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.tsx +23 -0
  195. package/templates/blocks/components/Card/CardElevations.doc.mjs +14 -0
  196. package/templates/blocks/components/Card/CardElevations.tsx +32 -0
  197. package/templates/blocks/components/Card/ClickableCardElevated.doc.mjs +14 -0
  198. package/templates/blocks/components/Card/ClickableCardElevated.tsx +21 -0
  199. package/templates/blocks/components/Card/SelectableCardElevated.doc.mjs +14 -0
  200. package/templates/blocks/components/Card/SelectableCardElevated.tsx +37 -0
  201. package/templates/blocks/components/Carousel/CarouselSnap.tsx +1 -1
  202. package/templates/blocks/components/ChatComposer/ChatComposerFlat.doc.mjs +14 -0
  203. package/templates/blocks/components/ChatComposer/ChatComposerFlat.tsx +75 -0
  204. package/templates/blocks/components/ChatMessage/ChatMessageAvatarName.tsx +2 -2
  205. package/templates/blocks/components/ChatMessage/ChatMessageMultiBubble.tsx +1 -1
  206. package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleGrouping.tsx +1 -1
  207. package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleMetadata.tsx +1 -1
  208. package/templates/blocks/components/ChatMessageList/ChatMessageListDensity.tsx +5 -9
  209. package/templates/blocks/components/ChatMessageList/ChatMessageListFullFeatured.tsx +1 -1
  210. package/templates/blocks/components/CodeBlock/CodeBlockTerminal.tsx +1 -1
  211. package/templates/blocks/components/HoverCard/HoverCardShowcase.tsx +1 -1
  212. package/templates/blocks/components/IconButton/IconButtonFloating.doc.mjs +14 -0
  213. package/templates/blocks/components/IconButton/IconButtonFloating.tsx +37 -0
  214. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.doc.mjs +19 -0
  215. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +46 -0
  216. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider02Overrides.doc.mjs +14 -0
  217. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider02Overrides.tsx +33 -0
  218. package/templates/blocks/components/Item/ItemWithMedia.tsx +2 -2
  219. package/templates/blocks/components/ListItem/ListItemWithMedia.tsx +2 -2
  220. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.doc.mjs +14 -0
  221. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.tsx +39 -0
  222. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.doc.mjs +14 -0
  223. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.tsx +44 -0
  224. package/templates/blocks/components/Pagination/PaginationDotsCarousel.tsx +2 -6
  225. package/templates/blocks/components/Stack/StackFillItem.tsx +2 -6
  226. package/templates/blocks/components/TabList/TabListTabsWithActions.doc.mjs +1 -1
  227. package/templates/blocks/components/TabList/TabListTabsWithActions.tsx +2 -7
  228. package/templates/blocks/components/Table/TableRowStatusTable.doc.mjs +14 -0
  229. package/templates/blocks/components/Table/TableRowStatusTable.tsx +59 -0
  230. package/templates/blocks/components/Thumbnail/ThumbnailDisabled.tsx +6 -1
  231. package/templates/blocks/components/Thumbnail/ThumbnailGallery.tsx +1 -0
  232. package/templates/blocks/components/Thumbnail/ThumbnailRemovable.tsx +1 -0
  233. package/templates/blocks/components/TypeaheadItem/TypeaheadItemShowcase.tsx +1 -1
  234. package/templates/pages/ai-chat/page.tsx +4 -4
  235. package/templates/pages/dashboard-portfolio/page.tsx +3 -11
  236. package/templates/pages/detail-page/page.tsx +5 -12
  237. package/templates/pages/documentation-design/page.tsx +1 -1
  238. package/templates/pages/messaging-shell/page.tsx +6 -8
  239. package/templates/pages/table-grouped/page.tsx +9 -14
  240. package/templates/pages/table-page/page.tsx +7 -9
  241. package/templates/pages/table-page-heatmap-status/page.tsx +5 -13
  242. package/{src/types → types}/api.contract.assert.ts +17 -8
  243. package/{src/types → types}/api.d.ts +110 -9
  244. package/{src/types → types}/base.d.ts +40 -4
  245. package/types/build.d.ts +53 -0
  246. package/types/codemod.d.ts +166 -0
  247. package/{src/types → types}/component.d.ts +1 -1
  248. package/{src/types → types}/discover.d.ts +1 -1
  249. package/{src/types → types}/docs.d.ts +1 -4
  250. package/{src/types → types}/hook.d.ts +1 -1
  251. package/{src/types → types}/index.d.ts +1 -0
  252. package/types/jscodeshift.d.ts +19 -0
  253. package/types/layout.d.ts +74 -0
  254. package/{src/types → types}/swizzle.d.ts +4 -0
  255. package/{src/types → types}/template-api.d.ts +4 -1
  256. package/types/theme.d.ts +53 -0
  257. package/types/upgrade.d.ts +108 -0
  258. package/utils/package-manager.mjs +178 -0
  259. package/utils/package-manager.test.mjs +220 -0
  260. package/{src/utils → utils}/path-safety.mjs +0 -18
  261. package/{src/utils → utils}/paths.mjs +4 -1
  262. package/{src/utils → utils}/update-check.mjs +5 -4
  263. package/{src/utils → utils}/update-check.test.mjs +3 -3
  264. package/docs/integration-authoring.md +0 -105
  265. package/src/commands/build.mjs +0 -196
  266. package/src/commands/init.mjs +0 -288
  267. package/src/commands/interactive-guard.test.mjs +0 -69
  268. package/src/commands/swizzle.mjs +0 -440
  269. package/src/commands/upgrade.test.mjs +0 -160
  270. package/src/types/codemod.d.ts +0 -81
  271. package/src/types/theme.d.ts +0 -23
  272. package/src/types/upgrade.d.ts +0 -64
  273. package/src/utils/interactive.mjs +0 -76
  274. package/src/utils/interactive.test.mjs +0 -70
  275. package/src/utils/package-manager.mjs +0 -74
  276. package/src/utils/package-manager.test.mjs +0 -113
  277. /package/{src/api → api/docs}/docOverlays.test.mjs +0 -0
  278. /package/{src/api → api/integration}/integration-block-exports.test.mjs +0 -0
  279. /package/{src/api → api/template}/template.test.mjs +0 -0
  280. /package/{src → authoring}/codemod.test.mjs +0 -0
  281. /package/{src → authoring}/config.mjs +0 -0
  282. /package/{src → authoring}/integration.mjs +0 -0
  283. /package/{src → authoring}/template.test.mjs +0 -0
  284. /package/{src → cli}/cli-exit-codes.test.mjs +0 -0
  285. /package/{src → cli}/commands/build-theme.color-scheme.test.mjs +0 -0
  286. /package/{src → cli}/commands/build-theme.import-path.test.mjs +0 -0
  287. /package/{src → cli}/commands/build-theme.path-safety.test.mjs +0 -0
  288. /package/{src → cli}/commands/build-theme.prose.test.mjs +0 -0
  289. /package/{src → cli}/commands/build-theme.variants.test.mjs +0 -0
  290. /package/{src → cli}/commands/build-theme.watch.test.mjs +0 -0
  291. /package/{src → cli}/commands/component.test.mjs +0 -0
  292. /package/{src → cli}/commands/detail-levels.test.mjs +0 -0
  293. /package/{src → cli}/commands/swizzle.path-safety.test.mjs +0 -0
  294. /package/{src → cli}/commands/swizzle.routing.test.mjs +0 -0
  295. /package/{src → cli}/commands/template.test.mjs +0 -0
  296. /package/{src → cli}/commands/upgrade.config-ordering.test.mjs +0 -0
  297. /package/{src → cli}/commands/validate-integration.test.mjs +0 -0
  298. /package/{src → cli}/update-hint-commands.test.mjs +0 -0
  299. /package/{src/codemods → codemods}/__tests__/rename-imperative-ref-to-handleRef.test.mjs +0 -0
  300. /package/{src/codemods → codemods}/__tests__/rename-isStreaming-to-isStopShown.test.mjs +0 -0
  301. /package/{src/codemods → codemods}/__tests__/rename-section-wash-to-muted.test.mjs +0 -0
  302. /package/{src/codemods → codemods}/__tests__/runner.test.mjs +0 -0
  303. /package/{src/codemods → codemods}/__tests__/toolbar-density-to-size.test.mjs +0 -0
  304. /package/{src/codemods → codemods}/__tests__/validation.test.mjs +0 -0
  305. /package/{src/codemods → codemods}/transforms/v0.0.10/__tests__/remove-size-props.test.mjs +0 -0
  306. /package/{src/codemods → codemods}/transforms/v0.0.10/index.mjs +0 -0
  307. /package/{src/codemods → codemods}/transforms/v0.0.12/__tests__/add-is-icon-only.test.mjs +0 -0
  308. /package/{src/codemods → codemods}/transforms/v0.0.12/index.mjs +0 -0
  309. /package/{src/codemods → codemods}/transforms/v0.0.13/__tests__/icon-name-deprecations.test.mjs +0 -0
  310. /package/{src/codemods → codemods}/transforms/v0.0.13/__tests__/rename-attachments-to-drawer.test.mjs +0 -0
  311. /package/{src/codemods → codemods}/transforms/v0.0.13/index.mjs +0 -0
  312. /package/{src/codemods → codemods}/transforms/v0.0.14/__tests__/rename-action-props.test.mjs +0 -0
  313. /package/{src/codemods → codemods}/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +0 -0
  314. /package/{src/codemods → codemods}/transforms/v0.0.14/index.mjs +0 -0
  315. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-item-children-to-endcontent.test.mjs +0 -0
  316. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-selector-children-to-render-option.test.mjs +0 -0
  317. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-theme-selectors-to-data-attrs.test.mjs +0 -0
  318. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/rename-date-picker-to-input.test.mjs +0 -0
  319. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/rename-stack-element-to-as.test.mjs +0 -0
  320. /package/{src/codemods → codemods}/transforms/v0.0.15/index.mjs +0 -0
  321. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-badge-dot-to-statusdot.test.mjs +0 -0
  322. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-gap-to-numeric.test.mjs +0 -0
  323. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-isFullBleed-to-padding.test.mjs +0 -0
  324. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-useXDSIcon-to-getIcon.test.mjs +0 -0
  325. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-banner-endButton-to-endContent.test.mjs +0 -0
  326. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-form-tooltip-startIcon.test.mjs +0 -0
  327. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-isShown-to-isOpen.test.mjs +0 -0
  328. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-selector-items-to-options.test.mjs +0 -0
  329. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-sidenav-header-to-heading.test.mjs +0 -0
  330. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-topnav-title-to-heading.test.mjs +0 -0
  331. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/unify-uncontrolled-to-defaultX.test.mjs +0 -0
  332. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/unify-visibility-to-onOpenChange.test.mjs +0 -0
  333. /package/{src/codemods → codemods}/transforms/v0.0.2/index.mjs +0 -0
  334. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-collapse-to-collapsible.test.mjs +0 -0
  335. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-radius-tokens.test.mjs +0 -0
  336. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-shadow-tokens.test.mjs +0 -0
  337. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-skeleton-radius.test.mjs +0 -0
  338. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-token-names.test.mjs +0 -0
  339. /package/{src/codemods → codemods}/transforms/v0.0.6/index.mjs +0 -0
  340. /package/{src/codemods → codemods}/transforms/v0.0.7/__tests__/rename-banner-variant-to-container.test.mjs +0 -0
  341. /package/{src/codemods → codemods}/transforms/v0.0.7/index.mjs +0 -0
  342. /package/{src/codemods → codemods}/transforms/v0.0.8/__tests__/migrate-token-renames.test.mjs +0 -0
  343. /package/{src/codemods → codemods}/transforms/v0.0.8/__tests__/rename-endslot-to-endcontent.test.mjs +0 -0
  344. /package/{src/codemods → codemods}/transforms/v0.0.8/index.mjs +0 -0
  345. /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-css-surfaces.test.mjs +0 -0
  346. /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-declare-module.test.mjs +0 -0
  347. /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +0 -0
  348. /package/{src/codemods → codemods}/transforms/v0.1.0/index.mjs +0 -0
  349. /package/{src/codemods → codemods}/transforms/v0.1.2/__tests__/rename-text-color-active-to-accent.test.mjs +0 -0
  350. /package/{src/codemods → codemods}/transforms/v0.1.2/index.mjs +0 -0
  351. /package/{src/codemods → codemods}/transforms/v0.1.3/__tests__/migrate-layout-components-to-experimental.test.mjs +0 -0
  352. /package/{src/codemods → codemods}/transforms/v0.1.3/index.mjs +0 -0
  353. /package/{src/codemods → codemods}/transforms/v0.1.5/__tests__/rename-switch-label-spacing-default-to-hug.test.mjs +0 -0
  354. /package/{src/codemods → codemods}/transforms/v0.1.5/index.mjs +0 -0
  355. /package/{src/codemods → codemods}/transforms/v0.1.7/__tests__/migrate-table-tableprops-to-direct-props.test.mjs +0 -0
  356. /package/{src/codemods → codemods}/transforms/v0.1.7/__tests__/rename-table-renderprops-styles-to-xstyle.test.mjs +0 -0
  357. /package/{src/codemods → codemods}/transforms/v0.1.7/index.mjs +0 -0
  358. /package/{src/lib → lib}/cli-error.test.mjs +0 -0
  359. /package/{src/lib → lib}/component-discovery.importpath.test.mjs +0 -0
  360. /package/{src/lib → lib}/component-format.test.mjs +0 -0
  361. /package/{src/lib → lib}/component-loader.test.mjs +0 -0
  362. /package/{src/lib → lib}/config-cache.mjs +0 -0
  363. /package/{src/lib → lib}/config-cache.test.mjs +0 -0
  364. /package/{src/lib → lib}/config-schema.mjs +0 -0
  365. /package/{src/lib → lib}/error-codes.mjs +0 -0
  366. /package/{src/lib → lib}/integration-warnings.test.mjs +0 -0
  367. /package/{src/lib → lib}/module-loader.test.mjs +0 -0
  368. /package/{src/lib → lib}/node-version.mjs +0 -0
  369. /package/{src/lib → lib}/node-version.test.mjs +0 -0
  370. /package/{src/lib → lib}/parse.mjs +0 -0
  371. /package/{src/lib → lib}/site.mjs +0 -0
  372. /package/{src/lib → lib}/xle/browser.d.ts +0 -0
  373. /package/{src/lib → lib}/xle/xle.test.mjs +0 -0
  374. /package/{src/schemas → schemas}/doc-schema.mjs +0 -0
  375. /package/{src/schemas → schemas}/template-schema.mjs +0 -0
  376. /package/{src/types → types}/config.d.ts +0 -0
  377. /package/{src/types → types}/doc.d.ts +0 -0
  378. /package/{src/types → types}/doctor.d.ts +0 -0
  379. /package/{src/types → types}/error-codes.d.ts +0 -0
  380. /package/{src/types → types}/integration.d.ts +0 -0
  381. /package/{src/types → types}/manifest.d.ts +0 -0
  382. /package/{src/types → types}/search.d.ts +0 -0
  383. /package/{src/types → types}/template.d.ts +0 -0
  384. /package/{src/types → types}/validate-integration.d.ts +0 -0
  385. /package/{src/utils → utils}/github.mjs +0 -0
  386. /package/{src/utils → utils}/path-safety.test.mjs +0 -0
  387. /package/{src/utils → utils}/paths.test.mjs +0 -0
  388. /package/{src/utils → utils}/semver.mjs +0 -0
  389. /package/{src/utils → utils}/semver.test.mjs +0 -0
@@ -0,0 +1,283 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file swizzle API — copy a component's source into the consumer project for
5
+ * customization, rewriting escaping relative imports to the OWNER package's
6
+ * subpaths.
7
+ *
8
+ * Side-effecting: `swizzle(name, ...)` writes files and returns a
9
+ * `swizzle.copy` receipt describing what it did; with no name (or `list`) it
10
+ * returns `swizzle.list`. Errors throw AstryxError (stable code + suggestions).
11
+ * All human prose / package-manager prefixing lives in the CLI renderer.
12
+ */
13
+
14
+ import * as fs from 'node:fs';
15
+ import * as path from 'node:path';
16
+ import {findCoreDir, listComponents} from '../../utils/paths.mjs';
17
+ import {assertWithin, PathSafetyError} from '../../utils/path-safety.mjs';
18
+ import {checkGhCli} from '../../utils/github.mjs';
19
+ import {Project} from '../../lib/project.mjs';
20
+ import {
21
+ CORE_PACKAGE,
22
+ findIntegrationComponentDoc,
23
+ findIntegrationComponentSource,
24
+ } from '../../lib/component-discovery.mjs';
25
+ import {ERROR_CODES} from '../../lib/error-codes.mjs';
26
+ import {AstryxError} from '../error.mjs';
27
+
28
+ /** Default issue tracker for maintainer feedback after swizzling. */
29
+ const DEFAULT_ISSUES_URL = 'https://github.com/facebook/astryx/issues/new';
30
+
31
+ /**
32
+ * Rewrite relative imports that point outside the component directory to use
33
+ * the OWNER package's subpaths. Imports within the copied directory (./x) are
34
+ * left untouched.
35
+ *
36
+ * e.g. with ownerPackage '@astryxdesign/core':
37
+ * '../theme/tokens.stylex' -> '@astryxdesign/core/theme'
38
+ * '../utils/mergeProps' -> '@astryxdesign/core/utils'
39
+ *
40
+ * @param {string} content
41
+ * @param {string} [ownerPackage]
42
+ */
43
+ export function rewriteImports(content, ownerPackage = CORE_PACKAGE) {
44
+ return content.replace(
45
+ /(from\s+['"])(\.\.\/.+?)(['"])/g,
46
+ (match, prefix, importPath, suffix) => {
47
+ const parts = importPath.replace(/^\.\.\//, '').split('/');
48
+ const topDir = parts[0];
49
+ return `${prefix}${ownerPackage}/${topDir}${suffix}`;
50
+ },
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Build the maintainer feedback note for a swizzled component.
56
+ * @param {string} component
57
+ * @param {string|undefined} issuesUrl
58
+ * @returns {{issuesUrl: string, ghCommand?: string} | null}
59
+ */
60
+ function buildFeedback(component, issuesUrl) {
61
+ if (!issuesUrl) return null;
62
+ /** @type {{issuesUrl: string, ghCommand?: string}} */
63
+ const feedback = {issuesUrl};
64
+ const match = issuesUrl.match(
65
+ /^https:\/\/github\.com\/([^/]+)\/([^/]+)\/issues(?:\/new)?\/?$/,
66
+ );
67
+ if (match && checkGhCli()) {
68
+ const [, owner, repo] = match;
69
+ feedback.ghCommand = `gh issue create --repo ${owner}/${repo} --title "[${component}] Swizzle feedback"`;
70
+ }
71
+ return feedback;
72
+ }
73
+
74
+ /**
75
+ * Load the configured integrations + core issues URL for `cwd`, swallowing any
76
+ * config errors so swizzle never hard-fails on a malformed/absent config.
77
+ * @param {string} cwd
78
+ * @returns {Promise<{loadedIntegrations: import('../../lib/integrations.mjs').LoadedIntegration[], issuesUrl: string|undefined, project: Project|null}>}
79
+ */
80
+ async function loadConfigSafely(cwd) {
81
+ try {
82
+ const project = await Project.load(cwd);
83
+ return {
84
+ loadedIntegrations: project.loadedIntegrations,
85
+ issuesUrl: project.config.issuesUrl,
86
+ project,
87
+ };
88
+ } catch {
89
+ return {loadedIntegrations: [], issuesUrl: undefined, project: null};
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Build the set of OWNER packages that provide a component named `name` across
95
+ * core + every loaded integration.
96
+ * @param {string} coreDir
97
+ * @param {Array<{name: string, components?: string, issuesUrl?: string}>} loadedIntegrations
98
+ * @param {string} name
99
+ * @param {string|undefined} coreIssuesUrl
100
+ * @returns {Array<{package: string, sourceDir: string|null, ownerPackage: string, issuesUrl: string|undefined}>}
101
+ */
102
+ function resolveOwners(coreDir, loadedIntegrations, name, coreIssuesUrl) {
103
+ const owners = [];
104
+ const coreComponentDir = path.join(coreDir, 'src', name);
105
+ if (fs.existsSync(coreComponentDir)) {
106
+ owners.push({
107
+ package: CORE_PACKAGE,
108
+ sourceDir: coreComponentDir,
109
+ ownerPackage: CORE_PACKAGE,
110
+ issuesUrl: coreIssuesUrl || DEFAULT_ISSUES_URL,
111
+ });
112
+ }
113
+ for (const integration of loadedIntegrations) {
114
+ const docPath = findIntegrationComponentDoc(integration, name);
115
+ if (!docPath) continue;
116
+ const sourcePath = findIntegrationComponentSource(integration, name);
117
+ owners.push({
118
+ package: integration.name,
119
+ sourceDir: sourcePath ? path.dirname(sourcePath) : null,
120
+ ownerPackage: integration.name,
121
+ issuesUrl: integration.issuesUrl,
122
+ });
123
+ }
124
+ return owners;
125
+ }
126
+
127
+ /** @param {string} file */
128
+ function isExcludedFromCopy(file) {
129
+ return (
130
+ file.includes('.test.') || file.includes('.doc.') || file === 'README.md'
131
+ );
132
+ }
133
+
134
+ /**
135
+ * List swizzlable components, or copy one component's source for customization.
136
+ *
137
+ * @param {string} [component] bare or XDS-prefixed component name; omit to list
138
+ * @param {{cwd?: string, output?: string, package?: string, list?: boolean, overwrite?: boolean}} [options]
139
+ * @returns {Promise<import('../../types/swizzle').SwizzleListResponse | import('../../types/swizzle').SwizzleCopyResponse>}
140
+ */
141
+ export async function swizzle(component, options = {}) {
142
+ const {
143
+ cwd = process.cwd(),
144
+ output = './components/astryx',
145
+ package: pkg,
146
+ list = false,
147
+ overwrite = false,
148
+ } = options;
149
+
150
+ const coreDir = findCoreDir(cwd);
151
+ if (!coreDir) {
152
+ throw new AstryxError(
153
+ 'Could not find @astryxdesign/core package. Make sure you are inside the design system monorepo or have @astryxdesign/core installed.',
154
+ [],
155
+ ERROR_CODES.ERR_CORE_NOT_FOUND,
156
+ );
157
+ }
158
+
159
+ const components = listComponents(coreDir);
160
+
161
+ if (list || !component) {
162
+ return {type: 'swizzle.list', data: components};
163
+ }
164
+
165
+ const dirName = component.replace(/^XDS/, '');
166
+
167
+ const {loadedIntegrations, project} = await loadConfigSafely(cwd);
168
+ const coreIssuesUrl = project
169
+ ? project.issuesUrl({package: CORE_PACKAGE})
170
+ : undefined;
171
+ const allOwners = resolveOwners(coreDir, loadedIntegrations, dirName, coreIssuesUrl);
172
+
173
+ if (allOwners.length === 0) {
174
+ throw new AstryxError(
175
+ `Component "${component}" not found.`,
176
+ components.slice(0, 10).map(n => ({name: n})),
177
+ ERROR_CODES.ERR_UNKNOWN_COMPONENT,
178
+ );
179
+ }
180
+
181
+ let owner;
182
+ if (pkg) {
183
+ owner = allOwners.find(o => o.package === pkg);
184
+ if (!owner) {
185
+ throw new AstryxError(
186
+ `Component "${dirName}" is not provided by package "${pkg}".`,
187
+ allOwners.map(o => ({name: o.package, reason: 'provides this component'})),
188
+ ERROR_CODES.ERR_UNKNOWN_COMPONENT,
189
+ );
190
+ }
191
+ } else if (allOwners.length > 1) {
192
+ throw new AstryxError(
193
+ `Component "${dirName}" is provided by multiple packages. Re-run with --package <pkg> to choose one.`,
194
+ allOwners.map(o => ({name: o.package, reason: 'provides this component'})),
195
+ ERROR_CODES.ERR_AMBIGUOUS_COMPONENT,
196
+ );
197
+ } else {
198
+ owner = allOwners[0];
199
+ }
200
+
201
+ if (!owner.sourceDir || !fs.existsSync(owner.sourceDir)) {
202
+ throw new AstryxError(
203
+ `No source found for "${dirName}" in package "${owner.package}".`,
204
+ [],
205
+ ERROR_CODES.ERR_NO_SOURCE,
206
+ );
207
+ }
208
+
209
+ const componentDir = owner.sourceDir;
210
+
211
+ // Path-safety: --output must resolve inside cwd.
212
+ let outputBase;
213
+ try {
214
+ outputBase = assertWithin(output, cwd, {label: 'output directory'});
215
+ } catch (err) {
216
+ if (err instanceof PathSafetyError) {
217
+ throw new AstryxError(err.message, [], ERROR_CODES.ERR_PATH_TRAVERSAL);
218
+ }
219
+ throw err;
220
+ }
221
+ const outputDir = path.join(outputBase, dirName);
222
+
223
+ // Pre-flight overwrite check before any mkdir/writeFile.
224
+ const sourceFiles = fs.readdirSync(componentDir).filter(file => {
225
+ if (isExcludedFromCopy(file)) return false;
226
+ return fs.statSync(path.join(componentDir, file)).isFile();
227
+ });
228
+ const existingFiles = sourceFiles.filter(f =>
229
+ fs.existsSync(path.join(outputDir, f)),
230
+ );
231
+ if (existingFiles.length > 0 && !overwrite) {
232
+ const relOutputForMsg = path.relative(cwd, outputDir) || '.';
233
+ throw new AstryxError(
234
+ `Refusing to overwrite ${existingFiles.length} existing file(s) in ${relOutputForMsg}/. ` +
235
+ `Re-run with --overwrite (or -f) to replace them.`,
236
+ [],
237
+ ERROR_CODES.ERR_FILE_EXISTS,
238
+ );
239
+ }
240
+
241
+ fs.mkdirSync(outputDir, {recursive: true});
242
+
243
+ const files = fs.readdirSync(componentDir);
244
+ let copied = 0;
245
+ let usesStyleX = false;
246
+ for (const file of files) {
247
+ if (isExcludedFromCopy(file)) continue;
248
+ const srcPath = path.join(componentDir, file);
249
+ if (!fs.statSync(srcPath).isFile()) continue;
250
+ let content = fs.readFileSync(srcPath, 'utf-8');
251
+ if (file.endsWith('.ts') || file.endsWith('.tsx')) {
252
+ content = rewriteImports(content, owner.ownerPackage);
253
+ }
254
+ if (
255
+ (file.endsWith('.ts') || file.endsWith('.tsx')) &&
256
+ content.includes('@stylexjs/stylex')
257
+ ) {
258
+ usesStyleX = true;
259
+ }
260
+ fs.writeFileSync(path.join(outputDir, file), content);
261
+ copied++;
262
+ }
263
+
264
+ const relOutput = path.relative(cwd, outputDir);
265
+ const copiedFiles = files.filter(
266
+ f =>
267
+ !isExcludedFromCopy(f) &&
268
+ fs.statSync(path.join(componentDir, f)).isFile(),
269
+ );
270
+ const feedback = buildFeedback(dirName, owner.issuesUrl);
271
+
272
+ /** @type {import('../../types/swizzle').SwizzleCopyResponse['data']} */
273
+ const data = {
274
+ component: dirName,
275
+ package: owner.package,
276
+ outputDir: relOutput,
277
+ filesCopied: copied,
278
+ files: copiedFiles.map(f => f),
279
+ usesStyleX,
280
+ };
281
+ if (feedback) data.feedback = feedback;
282
+ return {type: 'swizzle.copy', data};
283
+ }
@@ -1,7 +1,12 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  import {describe, it, expect} from 'vitest';
4
- import {rewriteImports} from './swizzle.mjs';
4
+ import * as path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ import {rewriteImports, swizzle} from './swizzle.mjs';
7
+
8
+ // api/swizzle/ -> up 3 = packages/cli, up 4 = repo root (has packages/core).
9
+ const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
5
10
 
6
11
  describe('rewriteImports', () => {
7
12
  it('rewrites ../theme/tokens to @astryxdesign/core/theme', () => {
@@ -51,3 +56,23 @@ describe('rewriteImports', () => {
51
56
  );
52
57
  });
53
58
  });
59
+
60
+ describe('swizzle() API', () => {
61
+ it('no component → swizzle.list of core components', async () => {
62
+ const r = await swizzle(undefined, {cwd: REPO});
63
+ expect(r.type).toBe('swizzle.list');
64
+ expect(Array.isArray(r.data)).toBe(true);
65
+ expect(r.data).toContain('Button');
66
+ });
67
+
68
+ it('--list → swizzle.list even with a component arg', async () => {
69
+ const r = await swizzle('Button', {cwd: REPO, list: true});
70
+ expect(r.type).toBe('swizzle.list');
71
+ });
72
+
73
+ it('unknown component → AstryxError ERR_UNKNOWN_COMPONENT with suggestions', async () => {
74
+ await expect(swizzle('NotARealComponent99', {cwd: REPO})).rejects.toMatchObject({
75
+ code: 'ERR_UNKNOWN_COMPONENT',
76
+ });
77
+ });
78
+ });
@@ -62,7 +62,7 @@ function writeTemplate(pkgDir, id, {kind, body, withSource = true}) {
62
62
  fs.writeFileSync(
63
63
  docPath,
64
64
  body ??
65
- `import {${create}} from '${CLI_PKG}/src/template.mjs';\n` +
65
+ `import {${create}} from '${CLI_PKG}/authoring/template.mjs';\n` +
66
66
  `export default ${create}({name: '${id} name', description: '${id} desc'});\n`,
67
67
  );
68
68
  if (withSource) {
@@ -166,7 +166,7 @@ describe('integration template discovery', () => {
166
166
  );
167
167
  fs.writeFileSync(
168
168
  path.join(pkg2, 'templates', 'hero.doc.mjs'),
169
- `import {createBlockTemplate} from '${CLI_PKG}/src/template.mjs';\n` +
169
+ `import {createBlockTemplate} from '${CLI_PKG}/authoring/template.mjs';\n` +
170
170
  `export default createBlockTemplate({name: 'Hero block', description: 'b'});\n`,
171
171
  );
172
172
  fs.writeFileSync(
@@ -21,7 +21,7 @@ import {
21
21
  findRelatedBlocks,
22
22
  } from './template.mjs';
23
23
 
24
- /** Absolute path to the CLI package so fixtures can import /src/template.mjs. */
24
+ /** Absolute path to the CLI package so fixtures can import /authoring/template.mjs. */
25
25
  const CLI_PKG = path.resolve(import.meta.dirname, '..', '..');
26
26
 
27
27
  let tmpDir;
@@ -68,7 +68,7 @@ function writeTemplate(pkgDir, id, {kind, suffix}) {
68
68
  kind === 'page' ? 'createPageTemplate' : 'createBlockTemplate';
69
69
  fs.writeFileSync(
70
70
  docPath,
71
- `import {${create}} from '${CLI_PKG}/src/template.mjs';\n` +
71
+ `import {${create}} from '${CLI_PKG}/authoring/template.mjs';\n` +
72
72
  `export default ${create}({name: '${id} name', description: '${id} desc'});\n`,
73
73
  );
74
74
  fs.writeFileSync(
@@ -177,7 +177,7 @@ describe('external showcase blocks: .template.* discovered like legacy .doc.*',
177
177
  fs.mkdirSync(fooDir, {recursive: true});
178
178
  fs.writeFileSync(
179
179
  path.join(fooDir, 'FooShowcase.template.ts'),
180
- `import {createBlockTemplate} from '${CLI_PKG}/src/template.mjs';\n` +
180
+ `import {createBlockTemplate} from '${CLI_PKG}/authoring/template.mjs';\n` +
181
181
  `export default createBlockTemplate({\n` +
182
182
  ` name: 'Foo — Showcase',\n` +
183
183
  ` description: 'Foo showcase.',\n` +
@@ -7,21 +7,54 @@
7
7
  import * as fs from 'node:fs';
8
8
  import * as path from 'node:path';
9
9
  import {createJiti} from 'jiti';
10
- import {loadModuleWithSchema} from '../lib/module-loader.mjs';
11
- import {TemplateEnvelopeSchema} from '../schemas/template-schema.mjs';
12
- import {CLI_ROOT, discoverExternalPackages} from '../utils/paths.mjs';
10
+ import {loadModuleWithSchema} from '../../lib/module-loader.mjs';
11
+ import {TemplateEnvelopeSchema} from '../../schemas/template-schema.mjs';
12
+ import {CLI_ROOT, discoverExternalPackages} from '../../utils/paths.mjs';
13
13
  import {
14
14
  assertWithin,
15
15
  isFilePathArg,
16
16
  PathSafetyError,
17
- } from '../utils/path-safety.mjs';
18
- import {AstryxError} from './error.mjs';
19
- import {ERROR_CODES} from '../lib/error-codes.mjs';
20
- import {Project} from '../lib/project.mjs';
17
+ } from '../../utils/path-safety.mjs';
18
+ import {AstryxError} from '../error.mjs';
19
+ import {ERROR_CODES} from '../../lib/error-codes.mjs';
20
+ import {Project} from '../../lib/project.mjs';
21
21
 
22
22
  /** Identity used for core (built-in) templates in package-scoped listings. */
23
23
  const CORE_PACKAGE = '@astryxdesign/core';
24
24
 
25
+ /**
26
+ * A discovered template (page or block), normalized across core, external, and
27
+ * integration sources. Not every source populates every field, so
28
+ * source-specific extras (`aspectRatio`, `isShowcase`, `package`) are optional.
29
+ * @typedef {object} DiscoveredTemplate
30
+ * @property {'page'|'block'} type
31
+ * @property {string} dirName
32
+ * @property {string} name
33
+ * @property {string} description
34
+ * @property {string} [category]
35
+ * @property {boolean} [isReady]
36
+ * @property {boolean} [scaffold]
37
+ * @property {number} [aspectRatio]
38
+ * @property {string[]} [componentsUsed]
39
+ * @property {boolean} [isShowcase]
40
+ * @property {string} filePath
41
+ * @property {string} docPath
42
+ * @property {string} [package]
43
+ */
44
+
45
+ /**
46
+ * An integration-template discovery error.
47
+ * @typedef {object} TemplateDiscoveryError
48
+ * @property {string} package
49
+ * @property {string} [template]
50
+ * @property {string} message
51
+ */
52
+
53
+ /**
54
+ * The loaded metadata object from a template spec (loose — authored shape).
55
+ * @typedef {Record<string, any> | undefined | null} TemplateDocModule
56
+ */
57
+
25
58
  /**
26
59
  * Canonical basename suffixes for template-spec files, in precedence order.
27
60
  * A template spec exports a `createBlockTemplate`/`createPageTemplate` result
@@ -60,6 +93,7 @@ function matchedTemplateSuffix(file) {
60
93
  */
61
94
  const TEMPLATE_SUFFIX_RE = /\.(template|doc)\.(ts|mjs|js)$/;
62
95
 
96
+ /** @type {ReturnType<typeof createJiti> | undefined} */
63
97
  let jitiInstance;
64
98
  /** Lazily-created jiti for loading `.ts` template specs (JSX-capable). */
65
99
  function getJiti() {
@@ -69,7 +103,6 @@ function getJiti() {
69
103
  return jitiInstance;
70
104
  }
71
105
 
72
-
73
106
  /**
74
107
  * Load an integration template doc module and validate it against the template
75
108
  * envelope at the load boundary. Default export only — `.ts` via jiti,
@@ -145,7 +178,7 @@ export function stripTemplateAssetRefs(source) {
145
178
  * native dynamic import. Returns null if the file does not exist.
146
179
  *
147
180
  * @param {string} docPath absolute path to the spec file
148
- * @returns {Promise<Record<string, unknown> | undefined | null>}
181
+ * @returns {Promise<TemplateDocModule>}
149
182
  */
150
183
  async function loadDocModule(docPath) {
151
184
  if (!fs.existsSync(docPath)) return null;
@@ -159,6 +192,7 @@ async function loadDocModule(docPath) {
159
192
  export {discoverAll as discoverTemplates};
160
193
 
161
194
  export function listTemplates() {
195
+ /** @type {string[]} */
162
196
  const all = [];
163
197
  if (fs.existsSync(PAGES_DIR)) {
164
198
  all.push(
@@ -171,7 +205,13 @@ export function listTemplates() {
171
205
  return all.sort();
172
206
  }
173
207
 
208
+ /**
209
+ * @param {string} dir
210
+ * @param {RegExp} pattern
211
+ * @returns {string[]}
212
+ */
174
213
  function findDocFiles(dir, pattern) {
214
+ /** @type {string[]} */
175
215
  const results = [];
176
216
  if (!fs.existsSync(dir)) return results;
177
217
  for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
@@ -205,6 +245,7 @@ async function discoverPages() {
205
245
  .readdirSync(PAGES_DIR, {withFileTypes: true})
206
246
  .filter(e => e.isDirectory());
207
247
 
248
+ /** @type {DiscoveredTemplate[]} */
208
249
  const templates = [];
209
250
  for (const dir of dirs) {
210
251
  const dirPath = path.join(PAGES_DIR, dir.name);
@@ -228,9 +269,11 @@ async function discoverPages() {
228
269
 
229
270
  async function discoverBlocks() {
230
271
  const docFiles = findDocFiles(BLOCKS_DIR, TEMPLATE_SUFFIX_RE);
272
+ /** @type {DiscoveredTemplate[]} */
231
273
  const blocks = [];
232
274
  for (const docPath of docFiles) {
233
275
  const suffix = matchedTemplateSuffix(docPath);
276
+ if (!suffix) continue;
234
277
  const basename = path.basename(docPath, suffix);
235
278
  const tsxPath = path.join(path.dirname(docPath), basename + '.tsx');
236
279
  if (!fs.existsSync(tsxPath)) continue;
@@ -262,6 +305,7 @@ async function discoverBlocks() {
262
305
  */
263
306
  async function discoverExternalBlocks(cwd = process.cwd()) {
264
307
  const externals = discoverExternalPackages(cwd);
308
+ /** @type {DiscoveredTemplate[]} */
265
309
  const blocks = [];
266
310
 
267
311
  for (const ext of externals) {
@@ -269,6 +313,7 @@ async function discoverExternalBlocks(cwd = process.cwd()) {
269
313
  const docFiles = findDocFiles(ext.blocksDir, TEMPLATE_SUFFIX_RE);
270
314
  for (const docPath of docFiles) {
271
315
  const suffix = matchedTemplateSuffix(docPath);
316
+ if (!suffix) continue;
272
317
  const basename = path.basename(docPath, suffix);
273
318
  const tsxPath = path.join(path.dirname(docPath), basename + '.tsx');
274
319
  if (!fs.existsSync(tsxPath)) continue;
@@ -297,6 +342,7 @@ async function discoverExternalBlocks(cwd = process.cwd()) {
297
342
  /**
298
343
  * Discover all blocks — core + external packages.
299
344
  * @param {string} [cwd]
345
+ * @returns {Promise<DiscoveredTemplate[]>}
300
346
  */
301
347
  async function discoverAllBlocks(cwd = process.cwd()) {
302
348
  const [core, external] = await Promise.all([
@@ -306,6 +352,10 @@ async function discoverAllBlocks(cwd = process.cwd()) {
306
352
  return [...core, ...external];
307
353
  }
308
354
 
355
+ /**
356
+ * @param {string} [cwd]
357
+ * @returns {Promise<DiscoveredTemplate[]>}
358
+ */
309
359
  async function discoverAll(cwd = process.cwd()) {
310
360
  const [pages, blocks, integration] = await Promise.all([
311
361
  discoverPages(),
@@ -323,7 +373,7 @@ async function discoverAll(cwd = process.cwd()) {
323
373
  * when the caller wants to warn about malformed integration templates.
324
374
  *
325
375
  * @param {string} [cwd]
326
- * @returns {Promise<{templates: object[], errors: {package: string, template?: string, message: string}[]}>}
376
+ * @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
327
377
  */
328
378
  async function discoverAllWithErrors(cwd = process.cwd()) {
329
379
  const [pages, blocks, integration] = await Promise.all([
@@ -348,8 +398,10 @@ export {discoverAllWithErrors};
348
398
  * @returns {string[]}
349
399
  */
350
400
  function findIntegrationDocFiles(root) {
401
+ /** @type {string[]} */
351
402
  const results = [];
352
403
  if (!fs.existsSync(root)) return results;
404
+ /** @param {string} dir */
353
405
  const walk = dir => {
354
406
  for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
355
407
  const full = path.join(dir, entry.name);
@@ -377,16 +429,21 @@ function findIntegrationDocFiles(root) {
377
429
  * template is skipped (recorded in `errors`).
378
430
  *
379
431
  * @param {string} [cwd]
380
- * @returns {Promise<{templates: object[], errors: {package: string, template?: string, message: string}[]}>}
432
+ * @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
381
433
  */
382
434
  async function discoverIntegrationTemplates(cwd = process.cwd()) {
435
+ /** @type {DiscoveredTemplate[]} */
383
436
  const templates = [];
437
+ /** @type {TemplateDiscoveryError[]} */
384
438
  const errors = [];
385
439
 
386
440
  let loadedIntegrations;
387
441
  try {
388
442
  const project = await Project.load(cwd);
389
- loadedIntegrations = project.loadedIntegrations;
443
+ loadedIntegrations =
444
+ /** @type {import('../../lib/integrations.mjs').LoadedIntegration[]} */ (
445
+ project.loadedIntegrations
446
+ );
390
447
  } catch {
391
448
  // Config load failures are surfaced elsewhere (discover/doctor); here we
392
449
  // simply contribute no integration templates.
@@ -409,10 +466,12 @@ async function discoverIntegrationTemplates(cwd = process.cwd()) {
409
466
  * than thrown. Exposed for `validate-integration`.
410
467
  *
411
468
  * @param {{name?: string, __spec?: string, templates?: string}} integration
412
- * @returns {Promise<{templates: object[], errors: {package: string, template?: string, message: string}[]}>}
469
+ * @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
413
470
  */
414
471
  export async function discoverIntegrationTemplatesForOne(integration) {
472
+ /** @type {DiscoveredTemplate[]} */
415
473
  const templates = [];
474
+ /** @type {TemplateDiscoveryError[]} */
416
475
  const errors = [];
417
476
 
418
477
  const root = integration?.templates;
@@ -421,6 +480,7 @@ export async function discoverIntegrationTemplatesForOne(integration) {
421
480
 
422
481
  for (const docPath of findIntegrationDocFiles(root)) {
423
482
  const suffix = matchedTemplateSuffix(docPath);
483
+ if (!suffix) continue;
424
484
  const id = path
425
485
  .relative(root, docPath)
426
486
  .slice(0, -suffix.length)
@@ -444,7 +504,7 @@ export async function discoverIntegrationTemplatesForOne(integration) {
444
504
  errors.push({
445
505
  package: pkgLabel,
446
506
  template: id,
447
- message: `Template "${id}" failed to load: ${err.message}`,
507
+ message: `Template "${id}" failed to load: ${/** @type {any} */ (err).message}`,
448
508
  });
449
509
  continue;
450
510
  }
@@ -480,10 +540,15 @@ export async function discoverIntegrationTemplatesForOne(integration) {
480
540
  return {templates, errors};
481
541
  }
482
542
 
543
+ /**
544
+ * @param {string} componentName
545
+ * @param {string} [cwd]
546
+ * @returns {Promise<DiscoveredTemplate[]>}
547
+ */
483
548
  export async function findRelatedBlocks(componentName, cwd) {
484
549
  const blocks = await discoverAllBlocks(cwd);
485
550
  return blocks.filter(b =>
486
- b.componentsUsed.some(c => c.toLowerCase() === componentName.toLowerCase()),
551
+ (b.componentsUsed ?? []).some(c => c.toLowerCase() === componentName.toLowerCase()),
487
552
  );
488
553
  }
489
554
 
@@ -506,7 +571,7 @@ export async function findShowcase(componentName, cwd, options) {
506
571
  return true;
507
572
  });
508
573
 
509
- const toResult = b => ({
574
+ const toResult = (/** @type {DiscoveredTemplate} */ b) => ({
510
575
  name: b.name,
511
576
  aspectRatio: b.aspectRatio,
512
577
  filePath: b.filePath,
@@ -515,14 +580,14 @@ export async function findShowcase(componentName, cwd, options) {
515
580
 
516
581
  // Priority 1: own directory (components/Badge/ for "Badge")
517
582
  const dirMatch = showcases.find(b => {
518
- const catDir = path.basename(b.category).toLowerCase();
583
+ const catDir = path.basename(b.category ?? '').toLowerCase();
519
584
  return catDir === lc;
520
585
  });
521
586
  if (dirMatch) return toResult(dirMatch);
522
587
 
523
588
  // Priority 2: componentsUsed in any directory (ClickableCard in Card/)
524
589
  const usedMatch = showcases.find(b =>
525
- b.componentsUsed.some(c => c.toLowerCase() === lc),
590
+ (b.componentsUsed ?? []).some(c => c.toLowerCase() === lc),
526
591
  );
527
592
  if (usedMatch) return toResult(usedMatch);
528
593
 
@@ -540,6 +605,10 @@ const UBIQUITOUS = new Set([
540
605
  'Icon',
541
606
  ]);
542
607
 
608
+ /**
609
+ * @param {string} pagePath
610
+ * @returns {string[]}
611
+ */
543
612
  export function extractComponents(pagePath) {
544
613
  const src = fs.readFileSync(pagePath, 'utf-8');
545
614
  // Match JSX opening tags, e.g. `<Section` or the legacy `<XDSSection`.
@@ -548,6 +617,7 @@ export function extractComponents(pagePath) {
548
617
  // JSX-tag boundary keeps this precise (avoids matching imports/comments/
549
618
  // identifiers) while remaining prefix-agnostic.
550
619
  const tagRegex = /<(XDS)?([A-Z]\w+)/g;
620
+ /** @type {string[]} */
551
621
  const matches = [];
552
622
  let m;
553
623
  while ((m = tagRegex.exec(src)) !== null) {
@@ -617,10 +687,11 @@ const SPATIAL_PROPS = [
617
687
  * @returns {string[]}
618
688
  */
619
689
  function extractSpatialAttrs(tagText) {
690
+ /** @type {string[]} */
620
691
  const attrs = [];
621
692
  for (const name of SPATIAL_PROPS) {
622
693
  const eqMatch = tagText.match(new RegExp(`\\b${name}\\s*=\\s*`));
623
- if (eqMatch) {
694
+ if (eqMatch && eqMatch.index != null) {
624
695
  const start = eqMatch.index;
625
696
  let i = eqMatch.index + eqMatch[0].length;
626
697
  const rest = tagText.slice(i);
@@ -662,13 +733,19 @@ function extractSpatialAttrs(tagText) {
662
733
  return attrs;
663
734
  }
664
735
 
736
+ /**
737
+ * @param {string} source
738
+ * @returns {string}
739
+ */
665
740
  function extractSkeleton(source) {
666
741
  const lines = source.split('\n');
742
+ /** @type {string[]} */
667
743
  const out = [];
668
744
  let depth = 0;
669
745
  let capturing = false;
670
746
  let inDefaultExport = false;
671
747
  const MAX_LINES = 35;
748
+ /** @type {string[]} */
672
749
  const depthStack = [];
673
750
 
674
751
  for (let i = 0; i < lines.length; i++) {