@astryxdesign/cli 0.7.2-canary.fe2c655 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (509) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +175 -154
  3. package/api/build/_adapter.d.mts +8 -0
  4. package/api/build/_adapter.mjs +12 -1
  5. package/api/build/build.doc.mjs +4 -0
  6. package/api/build/help/help.mjs +1 -1
  7. package/api/build/kit/kit.mjs +3 -11
  8. package/api/component/_adapter.d.mts +72 -1
  9. package/api/component/_adapter.mjs +212 -18
  10. package/api/component/component.doc.mjs +6 -1
  11. package/api/component/component.mjs +82 -15
  12. package/api/component/component.test.mjs +80 -0
  13. package/api/component/doc-links.test.mjs +386 -0
  14. package/api/component/integration-export-name.test.mjs +644 -0
  15. package/api/component/list/list.mjs +42 -4
  16. package/api/discover/_adapter.d.mts +132 -12
  17. package/api/discover/_adapter.mjs +488 -27
  18. package/api/discover/_adapter.test.mjs +599 -17
  19. package/api/discover/_catalog-view.d.mts +16 -5
  20. package/api/discover/_catalog-view.mjs +32 -8
  21. package/api/discover/detail/doc/doc.mjs +9 -1
  22. package/api/discover/discover.d.mts +11 -0
  23. package/api/discover/discover.doc.mjs +9 -7
  24. package/api/discover/discover.mjs +121 -19
  25. package/api/discover/discover.type.d.mts +20 -7
  26. package/api/discover/discover.type.mjs +17 -7
  27. package/api/discover/list/list.d.mts +4 -3
  28. package/api/discover/list/list.mjs +17 -10
  29. package/api/docs/_adapter.d.mts +74 -0
  30. package/api/docs/_adapter.mjs +553 -16
  31. package/api/docs/docs.doc.mjs +2 -1
  32. package/api/docs/docs.mjs +2 -1
  33. package/api/docs/docs.test.mjs +5 -2
  34. package/api/docs/integration-tree.test.mjs +22 -0
  35. package/api/docs/integrationDocs.test.mjs +124 -0
  36. package/api/docs/list/list.mjs +12 -2
  37. package/api/docs/reference-blocks.test.mjs +1 -3
  38. package/api/doctor/_adapter.d.mts +108 -0
  39. package/api/doctor/_adapter.mjs +223 -4
  40. package/api/doctor/doctor.d.mts +199 -0
  41. package/api/doctor/doctor.doc.mjs +3 -2
  42. package/api/doctor/doctor.mjs +530 -34
  43. package/api/doctor/doctor.test.mjs +180 -14
  44. package/api/doctor/release-checks.test.mjs +156 -0
  45. package/api/doctor/theme-checks.mjs +17 -4
  46. package/api/doctor/theme-checks.test.mjs +223 -7
  47. package/api/error.d.mts +10 -0
  48. package/api/error.mjs +25 -0
  49. package/api/gap-report/gap-report.d.mts +3 -0
  50. package/api/gap-report/gap-report.doc.mjs +11 -3
  51. package/api/gap-report/gap-report.mjs +219 -52
  52. package/api/gap-report/gap-report.test.mjs +233 -0
  53. package/api/gap-report/gap-report.type.d.mts +41 -0
  54. package/api/gap-report/gap-report.type.mjs +18 -0
  55. package/api/hook/_adapter.mjs +3 -11
  56. package/api/hook/hook.doc.mjs +4 -0
  57. package/api/hook/list/list.d.mts +1 -1
  58. package/api/index.d.mts +2 -0
  59. package/api/index.mjs +2 -0
  60. package/api/init/_adapter.d.mts +22 -0
  61. package/api/init/_adapter.mjs +59 -0
  62. package/api/init/init.d.mts +7 -5
  63. package/api/init/init.doc.mjs +28 -7
  64. package/api/init/init.mjs +28 -4
  65. package/api/init/init.type.d.mts +48 -3
  66. package/api/init/init.type.mjs +37 -3
  67. package/api/init/run/run.d.mts +1 -1
  68. package/api/init/run/run.mjs +17 -6
  69. package/api/init/user/user.d.mts +14 -0
  70. package/api/init/user/user.mjs +87 -0
  71. package/api/init/user/user.test.mjs +232 -0
  72. package/api/integration/_adapter.d.mts +45 -0
  73. package/api/integration/_adapter.mjs +112 -0
  74. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  75. package/api/integration/add-contribution.mjs +8 -4
  76. package/api/integration/add-contribution.test.mjs +4 -2
  77. package/api/integration/authoring-checks.mjs +110 -6
  78. package/api/integration/authoring-checks.test.mjs +808 -0
  79. package/api/integration/authoring-checks.type.d.mts +143 -0
  80. package/api/integration/authoring-checks.type.mjs +74 -2
  81. package/api/integration/check/check.d.mts +96 -0
  82. package/api/integration/check/check.mjs +357 -0
  83. package/api/integration/check/check.test.mjs +489 -0
  84. package/api/integration/integration-check.type.d.mts +118 -0
  85. package/api/integration/integration-check.type.mjs +84 -0
  86. package/api/integration/integrationCheck.doc.d.mts +10 -0
  87. package/api/integration/integrationCheck.doc.mjs +73 -0
  88. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  89. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  90. package/api/integration/pack-check.mjs +364 -120
  91. package/api/integration/pack-check.test.mjs +730 -10
  92. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  93. package/api/integration/validate-integration.d.mts +8 -0
  94. package/api/integration/validate-integration.mjs +63 -9
  95. package/api/integration/validate-integration.test.mjs +88 -8
  96. package/api/integration/validate-integration.type.d.mts +16 -0
  97. package/api/integration/validate-integration.type.mjs +14 -1
  98. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  99. package/api/integration/validateIntegration.doc.mjs +19 -3
  100. package/api/search/_adapter.d.mts +11 -0
  101. package/api/search/_adapter.mjs +25 -0
  102. package/api/search/search.d.mts +82 -13
  103. package/api/search/search.doc.mjs +8 -3
  104. package/api/search/search.domain-priority.test.mjs +108 -18
  105. package/api/search/search.integration-check.test.mjs +24 -0
  106. package/api/search/search.mjs +645 -64
  107. package/api/search/search.test.mjs +179 -1
  108. package/api/search/search.type.d.mts +14 -1
  109. package/api/search/search.type.mjs +6 -1
  110. package/api/swizzle/_adapter.mjs +2 -2
  111. package/api/swizzle/copy/copy.mjs +29 -15
  112. package/api/template/template-integration.test.mjs +1 -1
  113. package/api/template/template.test.mjs +120 -3
  114. package/api/theme/_adapter.d.mts +16 -0
  115. package/api/theme/_adapter.mjs +32 -5
  116. package/api/theme/build/build.d.mts +2 -1
  117. package/api/theme/build/build.family.test.mjs +64 -0
  118. package/api/theme/build/build.mjs +250 -38
  119. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  120. package/api/theme/build/font-warning.mjs +5 -3
  121. package/api/theme/build/font-warning.test.mjs +2 -1
  122. package/api/theme/remove/remove.test.mjs +2 -2
  123. package/api/theme/targets/targets.mjs +3 -10
  124. package/api/theme/themeBuild.doc.mjs +4 -4
  125. package/api/theme/themeTargets.doc.mjs +4 -0
  126. package/api/theme/use/use.test.mjs +1 -1
  127. package/api/upgrade/_adapter.d.mts +40 -2
  128. package/api/upgrade/_adapter.mjs +118 -5
  129. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  130. package/api/upgrade/run/run.mjs +35 -2
  131. package/api/upgrade/status/status.d.mts +21 -5
  132. package/api/upgrade/status/status.mjs +45 -8
  133. package/api/upgrade/upgrade.doc.mjs +6 -3
  134. package/api/upgrade/upgrade.mjs +5 -1
  135. package/api/upgrade/upgrade.type.d.mts +23 -0
  136. package/api/upgrade/upgrade.type.mjs +16 -1
  137. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  138. package/assets/codemods/registry.mjs +1 -0
  139. package/assets/codemods/runner.mjs +7 -1
  140. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  141. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  142. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  143. package/assets/discover/official-packages.json +367 -0
  144. package/assets/docs/README.md +10 -0
  145. package/assets/docs/author-a-theme.doc.mjs +3 -3
  146. package/assets/docs/browser-support.doc.mjs +1 -1
  147. package/assets/docs/getting-started.doc.mjs +26 -6
  148. package/assets/docs/styling-overview.doc.mjs +1 -1
  149. package/assets/docs/theme.doc.dense.mjs +1 -1
  150. package/assets/docs/theme.doc.mjs +2 -2
  151. package/assets/docs/theme.doc.zh.mjs +1 -1
  152. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  153. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  154. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  155. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  156. package/assets/docs/tree/block-template.doc.mjs +8 -20
  157. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  158. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  159. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  160. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  161. package/assets/docs/tree/checks.doc.mjs +32 -24
  162. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  163. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  164. package/assets/docs/tree/cli.doc.mjs +6 -0
  165. package/assets/docs/tree/codemods.doc.mjs +17 -7
  166. package/assets/docs/tree/commands.doc.mjs +7 -1
  167. package/assets/docs/tree/component-family.doc.mjs +8 -6
  168. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  169. package/assets/docs/tree/components.doc.mjs +7 -1
  170. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  171. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  172. package/assets/docs/tree/configuration.doc.mjs +7 -3
  173. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  174. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  175. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  176. package/assets/docs/tree/docs.doc.mjs +9 -3
  177. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  178. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  179. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  180. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  181. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  182. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  183. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  184. package/assets/docs/tree/global.doc.mjs +131 -0
  185. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  186. package/assets/docs/tree/install-once.doc.mjs +105 -0
  187. package/assets/docs/tree/integrations.doc.mjs +13 -1
  188. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  189. package/assets/docs/tree/layout.doc.mjs +6 -6
  190. package/assets/docs/tree/links.doc.mjs +7 -6
  191. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  192. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  193. package/assets/docs/tree/migration.doc.mjs +0 -1
  194. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  195. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  196. package/assets/docs/tree/page-template.doc.mjs +4 -8
  197. package/assets/docs/tree/problems.doc.mjs +205 -0
  198. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  199. package/assets/docs/tree/publishing.doc.mjs +6 -6
  200. package/assets/docs/tree/quick-start.doc.mjs +53 -27
  201. package/assets/docs/tree/reference.doc.mjs +192 -0
  202. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  203. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  204. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  205. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  206. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  207. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  208. package/assets/docs/tree/single-component.doc.mjs +15 -6
  209. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  210. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  211. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  212. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  213. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  214. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  215. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  216. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  217. package/assets/docs/tree/templates.doc.mjs +7 -1
  218. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  219. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  220. package/assets/docs/tree/themes.doc.mjs +4 -0
  221. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  222. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  223. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  224. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  225. package/assets/docs/tree/updates.doc.mjs +92 -0
  226. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  227. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  228. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  229. package/assets/docs/tree/versioning.doc.mjs +10 -9
  230. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  231. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  232. package/assets/docs/use-a-theme.doc.mjs +2 -2
  233. package/assets/docs/working-with-ai.doc.mjs +66 -13
  234. package/assets/theme.template.ts +1 -1
  235. package/authoring/codemod/codemod.doc.mjs +7 -3
  236. package/authoring/config/config.doc.mjs +9 -6
  237. package/authoring/config/type.ts +4 -2
  238. package/authoring/debug/parse.d.mts +5 -5
  239. package/authoring/discover/discover.doc.mjs +7 -1
  240. package/authoring/doctypes/_schema.d.mts +8 -3
  241. package/authoring/doctypes/_schema.mjs +1 -0
  242. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  243. package/authoring/doctypes/command/command.doc.mjs +20 -12
  244. package/authoring/doctypes/command/type.ts +14 -0
  245. package/authoring/doctypes/component/component.doc.mjs +30 -27
  246. package/authoring/doctypes/component/type.ts +21 -8
  247. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  248. package/authoring/doctypes/function/function.doc.mjs +9 -8
  249. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  250. package/authoring/doctypes/hook/type.ts +4 -0
  251. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  252. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  253. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  254. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  255. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  256. package/authoring/doctypes/template/parse.d.mts +6 -2
  257. package/authoring/doctypes/template/parse.mjs +3 -1
  258. package/authoring/doctypes/template/template.doc.mjs +3 -3
  259. package/authoring/doctypes/template/type.ts +22 -0
  260. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  261. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  262. package/authoring/identity/identity.doc.mjs +3 -3
  263. package/authoring/index.d.ts +2 -0
  264. package/authoring/integration/integration.doc.mjs +30 -6
  265. package/authoring/integration/parse.test.mjs +21 -0
  266. package/authoring/integration/schema.d.mts +16 -3
  267. package/authoring/integration/schema.mjs +34 -2
  268. package/authoring/integration/type.ts +12 -0
  269. package/clients/cli/bin/astryx.mjs +20 -3
  270. package/clients/cli/cli-command.test.mjs +166 -0
  271. package/clients/cli/commands/blog.doc.mjs +1 -0
  272. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  273. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  274. package/clients/cli/commands/build-theme.mjs +26 -13
  275. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  276. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  277. package/clients/cli/commands/build.doc.mjs +1 -0
  278. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  279. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  280. package/clients/cli/commands/component.doc.mjs +1 -0
  281. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  282. package/clients/cli/commands/discover.doc.mjs +9 -1
  283. package/clients/cli/commands/discover.mjs +144 -93
  284. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  285. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  286. package/clients/cli/commands/docs.doc.mjs +1 -0
  287. package/clients/cli/commands/docs.mjs +49 -19
  288. package/clients/cli/commands/docs.test.mjs +2 -2
  289. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  290. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  291. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  292. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  293. package/clients/cli/commands/doctor-integration.test.mjs +888 -3
  294. package/clients/cli/commands/doctor.doc.mjs +12 -2
  295. package/clients/cli/commands/doctor.mjs +292 -7
  296. package/clients/cli/commands/doctor.test.mjs +10 -4
  297. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  298. package/clients/cli/commands/gap-report.mjs +24 -2
  299. package/clients/cli/commands/gap-report.test.mjs +79 -0
  300. package/clients/cli/commands/hook.doc.mjs +1 -0
  301. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  302. package/clients/cli/commands/init.doc.mjs +29 -3
  303. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  304. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  305. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  306. package/clients/cli/commands/integration-check.test.mjs +441 -0
  307. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  308. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  309. package/clients/cli/commands/integration.doc.mjs +6 -2
  310. package/clients/cli/commands/integration.mjs +119 -7
  311. package/clients/cli/commands/json-contract.test.mjs +11 -2
  312. package/clients/cli/commands/manifest.doc.mjs +1 -0
  313. package/clients/cli/commands/outside-project.test.mjs +774 -0
  314. package/clients/cli/commands/search.doc.mjs +1 -0
  315. package/clients/cli/commands/search.mjs +16 -11
  316. package/clients/cli/commands/search.test.mjs +429 -296
  317. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  318. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  319. package/clients/cli/commands/swizzle.mjs +5 -3
  320. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  321. package/clients/cli/commands/template.doc.mjs +1 -0
  322. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  323. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  324. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  325. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  326. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  327. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  328. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  329. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  330. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  331. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  332. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  333. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  334. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  335. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  336. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  337. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  338. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  339. package/clients/cli/index.mjs +112 -16
  340. package/clients/cli/lib/cli-error.mjs +4 -1
  341. package/clients/cli/lib/component-format.test.mjs +1 -1
  342. package/clients/cli/lib/define-command.mjs +2 -2
  343. package/clients/cli/lib/json-shim.mjs +3 -1
  344. package/clients/cli/lib/manifest.d.ts +9 -0
  345. package/clients/cli/lib/manifest.mjs +17 -1
  346. package/clients/cli/lib/manifest.test.mjs +25 -0
  347. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  348. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  349. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  350. package/foundation/agent-docs/agent-docs.mjs +11 -9
  351. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  352. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  353. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  354. package/foundation/config/project.d.mts +35 -3
  355. package/foundation/config/project.mjs +226 -7
  356. package/foundation/config/project.test.mjs +66 -0
  357. package/foundation/config/theme-state.mjs +5 -4
  358. package/foundation/discovery/authoring-self-docs.mjs +2 -1
  359. package/foundation/discovery/cli-self-docs.mjs +20 -3
  360. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  361. package/foundation/discovery/component-discovery.d.mts +190 -10
  362. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  363. package/foundation/discovery/component-discovery.mjs +864 -56
  364. package/foundation/discovery/component-discovery.test.mjs +96 -0
  365. package/foundation/discovery/component-replacement.mjs +20 -1
  366. package/foundation/discovery/component-replacement.test.mjs +40 -0
  367. package/foundation/discovery/copied-passages.d.mts +216 -0
  368. package/foundation/discovery/copied-passages.mjs +720 -0
  369. package/foundation/discovery/copied-passages.test.mjs +124 -0
  370. package/foundation/discovery/doc-consistency.d.mts +34 -0
  371. package/foundation/discovery/doc-consistency.mjs +513 -0
  372. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  373. package/foundation/discovery/doc-fluff.d.mts +112 -0
  374. package/foundation/discovery/doc-fluff.mjs +328 -0
  375. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  376. package/foundation/discovery/doc-types.d.mts +157 -0
  377. package/foundation/discovery/doc-types.mjs +1231 -0
  378. package/foundation/discovery/doc-types.test.mjs +744 -0
  379. package/foundation/discovery/docs-discovery.d.mts +88 -0
  380. package/foundation/discovery/docs-discovery.mjs +83 -2
  381. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  382. package/foundation/discovery/integration-doc-files.mjs +231 -0
  383. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  384. package/foundation/discovery/module-exports.d.mts +27 -0
  385. package/foundation/discovery/module-exports.mjs +356 -31
  386. package/foundation/discovery/module-exports.test.mjs +172 -0
  387. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  388. package/foundation/discovery/package-tsconfig.mjs +228 -0
  389. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  390. package/foundation/discovery/project-typescript.d.mts +32 -0
  391. package/foundation/discovery/project-typescript.mjs +89 -0
  392. package/foundation/discovery/prop-drift.d.mts +297 -0
  393. package/foundation/discovery/prop-drift.mjs +2428 -0
  394. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  395. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  396. package/foundation/discovery/runner-allowlist.mjs +53 -0
  397. package/foundation/discovery/stray-pointers.d.mts +52 -0
  398. package/foundation/discovery/stray-pointers.mjs +293 -0
  399. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  400. package/foundation/discovery/template-adapter.mjs +74 -5
  401. package/foundation/discovery/template-needs.mjs +5 -2
  402. package/foundation/discovery/theme-imports.mjs +8 -4
  403. package/foundation/discovery/theme-imports.test.mjs +1 -1
  404. package/foundation/discovery/type-match.d.mts +53 -0
  405. package/foundation/discovery/type-match.mjs +660 -0
  406. package/foundation/discovery/type-match.test.mjs +169 -0
  407. package/foundation/discovery/type-text.d.mts +68 -0
  408. package/foundation/discovery/type-text.mjs +658 -0
  409. package/foundation/discovery/type-text.test.mjs +196 -0
  410. package/foundation/discovery/unresolved-references.d.mts +62 -0
  411. package/foundation/discovery/unresolved-references.mjs +790 -0
  412. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  413. package/foundation/doc-compiler/doc-loads.test.mjs +51 -1
  414. package/foundation/doc-compiler/inputs.mjs +27 -1
  415. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  416. package/foundation/doc-compiler/links.d.mts +41 -5
  417. package/foundation/doc-compiler/links.mjs +167 -28
  418. package/foundation/doc-compiler/links.test.mjs +97 -4
  419. package/foundation/doc-compiler/read.d.mts +1 -1
  420. package/foundation/doc-compiler/tree.mjs +30 -1
  421. package/foundation/doc-compiler/tree.test.mjs +8 -1
  422. package/foundation/env/cli-command.d.mts +83 -0
  423. package/foundation/env/cli-command.mjs +152 -0
  424. package/foundation/env/cli-command.test.mjs +129 -0
  425. package/foundation/env/node-version.test.mjs +15 -3
  426. package/foundation/env/package-manager.d.mts +93 -9
  427. package/foundation/env/package-manager.global.test.mjs +66 -0
  428. package/foundation/env/package-manager.mjs +184 -13
  429. package/foundation/env/package-manager.test.mjs +341 -15
  430. package/foundation/env/project-scope.d.mts +9 -0
  431. package/foundation/env/project-scope.mjs +33 -0
  432. package/foundation/env/search-path.d.mts +42 -0
  433. package/foundation/env/search-path.mjs +205 -0
  434. package/foundation/env/search-path.test.mjs +122 -0
  435. package/foundation/env/user-dirs.d.mts +39 -0
  436. package/foundation/env/user-dirs.mjs +93 -0
  437. package/foundation/env/user-dirs.test.mjs +72 -0
  438. package/foundation/env/user-settings.d.mts +46 -0
  439. package/foundation/env/user-settings.mjs +106 -0
  440. package/foundation/env/user-settings.test.mjs +64 -0
  441. package/foundation/fs/file-protection.mjs +15 -1
  442. package/foundation/fs/module-loader.d.mts +2 -1
  443. package/foundation/fs/module-loader.mjs +60 -4
  444. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  445. package/foundation/fs/module-loader.test.mjs +194 -0
  446. package/foundation/fs/path-compare.d.mts +33 -0
  447. package/foundation/fs/path-compare.mjs +73 -0
  448. package/foundation/fs/paths.d.mts +57 -1
  449. package/foundation/fs/paths.mjs +131 -3
  450. package/foundation/fs/paths.test.mjs +10 -1
  451. package/foundation/integrations/computer-integrations.d.mts +78 -0
  452. package/foundation/integrations/computer-integrations.mjs +316 -0
  453. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  454. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  455. package/foundation/integrations/contribution-inventory.mjs +56 -3
  456. package/foundation/integrations/integration-warnings.mjs +3 -2
  457. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  458. package/foundation/integrations/integrations.d.mts +25 -1
  459. package/foundation/integrations/integrations.mjs +46 -3
  460. package/foundation/integrations/issue.ts +6 -0
  461. package/foundation/integrations/validate-contributions.d.mts +2 -0
  462. package/foundation/integrations/validate-contributions.mjs +41 -1
  463. package/foundation/release/agent-text-worker.d.mts +4 -0
  464. package/foundation/release/agent-text-worker.mjs +52 -0
  465. package/foundation/release/cache.d.mts +310 -0
  466. package/foundation/release/cache.mjs +1216 -0
  467. package/foundation/release/cache.test.mjs +920 -0
  468. package/foundation/release/choose.d.mts +207 -0
  469. package/foundation/release/choose.mjs +677 -0
  470. package/foundation/release/choose.test.mjs +427 -0
  471. package/foundation/release/core-worker.d.mts +4 -0
  472. package/foundation/release/core-worker.mjs +38 -0
  473. package/foundation/release/global-state.d.mts +259 -0
  474. package/foundation/release/global-state.mjs +472 -0
  475. package/foundation/release/global-state.test.mjs +381 -0
  476. package/foundation/release/handoff.d.mts +60 -0
  477. package/foundation/release/handoff.e2e.test.mjs +278 -0
  478. package/foundation/release/handoff.mjs +357 -0
  479. package/foundation/release/lock-lease.d.mts +23 -0
  480. package/foundation/release/lock-lease.mjs +80 -0
  481. package/foundation/release/notices.d.mts +253 -0
  482. package/foundation/release/notices.mjs +596 -0
  483. package/foundation/release/notices.test.mjs +565 -0
  484. package/foundation/release/npm-guard.d.mts +4 -0
  485. package/foundation/release/npm-guard.mjs +126 -0
  486. package/foundation/release/project.d.mts +141 -0
  487. package/foundation/release/project.mjs +466 -0
  488. package/foundation/release/project.test.mjs +288 -0
  489. package/foundation/release/refresh-worker.d.mts +4 -0
  490. package/foundation/release/refresh-worker.mjs +27 -0
  491. package/foundation/release/run-context.d.mts +47 -0
  492. package/foundation/release/run-context.mjs +149 -0
  493. package/foundation/release/update-notice-env.d.mts +28 -0
  494. package/foundation/release/update-notice-env.mjs +39 -0
  495. package/foundation/response/base.d.ts +26 -3
  496. package/foundation/response/error-codes.d.mts +3 -1
  497. package/foundation/response/error-codes.doc.mjs +12 -0
  498. package/foundation/response/error-codes.mjs +17 -0
  499. package/foundation/response/error-codes.test.mjs +2 -0
  500. package/foundation/response/json-contract.test.mjs +7 -2
  501. package/foundation/response/json.mjs +4 -1
  502. package/foundation/response/response-types.doc.mjs +22 -11
  503. package/foundation/response/response-types.doc.test.mjs +46 -0
  504. package/foundation/response/response.doc.mjs +3 -3
  505. package/foundation/xle/browser-imports.test.mjs +74 -0
  506. package/foundation/xle/registry.mjs +4 -0
  507. package/foundation/xle/registry.test.mjs +28 -0
  508. package/package.json +11 -11
  509. package/scripts/postinstall.mjs +24 -5
@@ -24,7 +24,6 @@ import * as path from 'node:path';
24
24
  import * as os from 'node:os';
25
25
  import {fileURLToPath} from 'node:url';
26
26
  import {spawnSync} from 'node:child_process';
27
- import jscodeshift from 'jscodeshift';
28
27
  import {assertWithin} from '../../foundation/fs/path-safety.mjs';
29
28
  import {resolvePackageDir} from '../../foundation/integrations/integrations.mjs';
30
29
  import {
@@ -41,6 +40,8 @@ import {
41
40
  } from '../../foundation/discovery/docs-discovery.mjs';
42
41
  import {discoverIntegrationThemes} from '../../foundation/discovery/theme-discovery.mjs';
43
42
  import {inspectPackageExport} from '../../foundation/discovery/package-exports.mjs';
43
+ import {parse as parseModule} from '@babel/parser';
44
+ import {moduleExportsName} from '../../foundation/discovery/module-exports.mjs';
44
45
  import {
45
46
  ThemeImportError,
46
47
  resolveThemeImports,
@@ -51,6 +52,9 @@ import {
51
52
  } from '../theme/build/build.mjs';
52
53
  import {
53
54
  discoverIntegrationComponents,
55
+ docStem,
56
+ familyImports,
57
+ isIntegrationMember,
54
58
  resolveIntegrationImportPath,
55
59
  } from '../../foundation/discovery/component-discovery.mjs';
56
60
  import {loadComponentReplacements} from '../component/_adapter.mjs';
@@ -65,6 +69,7 @@ import {
65
69
  collectIdentities,
66
70
  compareIdentities,
67
71
  } from '../../foundation/integrations/contribution-inventory.mjs';
72
+ import {formatCliCommand} from '../../foundation/env/package-manager.mjs';
68
73
 
69
74
  /**
70
75
  * @typedef {import('../../foundation/integrations/issue').AstryxIntegrationIssue} Issue
@@ -307,98 +312,6 @@ function extractTarball(tgzPath, extractDir) {
307
312
  }
308
313
  }
309
314
 
310
- const MODULE_EXTENSIONS = ['.mjs', '.js', '.mts', '.ts', '.tsx', '.jsx'];
311
- const moduleParser = jscodeshift.withParser('tsx');
312
-
313
- /** @param {unknown} specifier @param {string} fromFile */
314
- function resolveLocalModule(specifier, fromFile) {
315
- if (typeof specifier !== 'string' || !specifier.startsWith('.')) return null;
316
- const base = path.resolve(path.dirname(fromFile), specifier);
317
- const candidates = [
318
- base,
319
- ...MODULE_EXTENSIONS.map(extension => `${base}${extension}`),
320
- ...MODULE_EXTENSIONS.map(extension => path.join(base, `index${extension}`)),
321
- ];
322
- return candidates.find(candidate => fs.existsSync(candidate)) ?? null;
323
- }
324
-
325
- /** @param {any} declaration @param {string} exportName */
326
- function declarationExports(declaration, exportName) {
327
- if (!declaration) return false;
328
- if (['FunctionDeclaration', 'ClassDeclaration'].includes(declaration.type)) {
329
- return declaration.id?.name === exportName;
330
- }
331
- if (declaration.type !== 'VariableDeclaration') return false;
332
- return declaration.declarations.some(
333
- (/** @type {any} */ item) =>
334
- item.id?.type === 'Identifier' && item.id.name === exportName,
335
- );
336
- }
337
-
338
- /**
339
- * Check one resolved module's static export surface without executing package
340
- * code. Local ESM re-exports are followed recursively.
341
- *
342
- * @param {string} file
343
- * @param {string} exportName
344
- * @param {Set<string>} [seen]
345
- */
346
- function moduleExportsName(file, exportName, seen = new Set()) {
347
- const identity = `${file}\0${exportName}`;
348
- if (seen.has(identity) || !fs.existsSync(file)) return false;
349
- seen.add(identity);
350
-
351
- let root;
352
- try {
353
- root = moduleParser(fs.readFileSync(file, 'utf-8'));
354
- } catch {
355
- return false;
356
- }
357
-
358
- let found = false;
359
- root
360
- .find(moduleParser.ExportNamedDeclaration)
361
- .forEach((/** @type {any} */ exportPath) => {
362
- if (found) return;
363
- const node = exportPath.node;
364
- if (declarationExports(node.declaration, exportName)) {
365
- found = true;
366
- return;
367
- }
368
- for (const specifier of node.specifiers ?? []) {
369
- const exported = specifier.exported?.name ?? specifier.exported?.value;
370
- if (exported !== exportName) continue;
371
- if (!node.source) {
372
- found = true;
373
- return;
374
- }
375
- const target = resolveLocalModule(node.source.value, file);
376
- const imported =
377
- specifier.local?.name ?? specifier.local?.value ?? exportName;
378
- if (target && moduleExportsName(target, imported, seen)) {
379
- found = true;
380
- return;
381
- }
382
- }
383
- });
384
- if (found) return true;
385
- if (
386
- exportName === 'default' &&
387
- root.find(moduleParser.ExportDefaultDeclaration).size() > 0
388
- ) {
389
- return true;
390
- }
391
-
392
- root
393
- .find(moduleParser.ExportAllDeclaration)
394
- .forEach((/** @type {any} */ exportPath) => {
395
- if (found) return;
396
- const target = resolveLocalModule(exportPath.node.source?.value, file);
397
- if (target && moduleExportsName(target, exportName, seen)) found = true;
398
- });
399
- return found;
400
- }
401
-
402
315
  /**
403
316
  * Whether any of these doc files has a section that sets `id`.
404
317
  * @param {string[]} files
@@ -472,15 +385,20 @@ async function validatePackedComponentExports(integration, scratchBase) {
472
385
  const issues = [];
473
386
  /** @type {Array<{name: string, specifier: string}>} */
474
387
  const components = [];
388
+ // A family doc backs several records: load it, and report it, once.
389
+ const loads = new Map();
475
390
  for (const record of records) {
391
+ const first = !loads.has(record.docPath);
392
+ if (first) loads.set(record.docPath, loadComponentDoc(record.docPath));
476
393
  let docs;
477
394
  try {
478
- docs = await loadComponentDoc(record.docPath);
395
+ docs = await loads.get(record.docPath);
479
396
  } catch (err) {
397
+ if (!first) continue;
480
398
  issues.push(
481
399
  error(
482
400
  'component_doc_unloadable',
483
- `Component "${record.name}" doc at "${record.docPath}" could not be loaded: ${err instanceof Error ? err.message : String(err)}`,
401
+ `Component "${docStem(record.docPath)}" doc at "${record.docPath}" could not be loaded: ${err instanceof Error ? err.message : String(err)}`,
484
402
  ),
485
403
  );
486
404
  continue;
@@ -658,16 +576,22 @@ async function validateIncompleteThemeExports(
658
576
  path.join(os.tmpdir(), `astryx-theme-${localTheme.slug}-`),
659
577
  );
660
578
  const expectedCss = path.join(expectedDir, `${localTheme.slug}.css`);
579
+ let expectedModule = path.join(expectedDir, `${localTheme.slug}.js`);
661
580
  try {
662
581
  if (outputs.some(output => output.kind !== 'font stylesheet')) {
663
- const receipt = await themeBuild(
664
- source,
665
- {out: expectedCss},
666
- {cwd: packageDir},
667
- );
582
+ // The scratch directory is the write root; the package stays the cwd,
583
+ // so the build selects the package's Core and its paths as before.
584
+ const scratchBuild = {out: expectedCss, __writeRoot: expectedDir};
585
+ const receipt = await themeBuild(source, scratchBuild, {cwd: packageDir});
668
586
  if (receipt == null || receipt.type !== 'theme.build') {
669
587
  throw new Error('the theme build produced no complete receipt');
670
588
  }
589
+ expectedModule = builtModuleFile(
590
+ receipt,
591
+ packageDir,
592
+ expectedDir,
593
+ localTheme.slug,
594
+ );
671
595
  }
672
596
  for (const output of outputs) {
673
597
  const localFile = /** @type {string} */ (output.inspected.target);
@@ -684,9 +608,7 @@ async function validateIncompleteThemeExports(
684
608
  continue;
685
609
  }
686
610
  const expectedFile =
687
- output.kind === 'module'
688
- ? path.join(expectedDir, `${localTheme.slug}.js`)
689
- : expectedCss;
611
+ output.kind === 'module' ? expectedModule : expectedCss;
690
612
  const expected = normalizeThemeVerifyForCompare(
691
613
  fs.readFileSync(expectedFile, 'utf-8'),
692
614
  );
@@ -702,7 +624,7 @@ async function validateIncompleteThemeExports(
702
624
  output.kind === 'module'
703
625
  ? 'theme_module_stale'
704
626
  : 'theme_stylesheet_stale',
705
- `Theme "${localTheme.slug}" ${output.kind} does not match ${sourceRelative}. This incomplete export remains packable, but run \`astryx theme build ${sourceRelative}\` before adding its matching export.`,
627
+ `Theme "${localTheme.slug}" ${output.kind} does not match ${sourceRelative}. This incomplete export remains packable, but run \`${formatCliCommand(`theme build ${sourceRelative}`, packageDir)}\` before adding its matching export.`,
706
628
  ),
707
629
  );
708
630
  }
@@ -721,7 +643,7 @@ async function validateIncompleteThemeExports(
721
643
  issues.push(
722
644
  error(
723
645
  'theme_build_failed',
724
- `Theme "${localTheme.slug}" could not be rebuilt from ${sourceRelative}: ${err instanceof Error ? err.message : String(err)} Run \`astryx theme build ${sourceRelative}\` before packing.`,
646
+ `Theme "${localTheme.slug}" could not be rebuilt from ${sourceRelative}: ${err instanceof Error ? err.message : String(err)} Run \`${formatCliCommand(`theme build ${sourceRelative}`, packageDir)}\` before packing.`,
725
647
  ),
726
648
  );
727
649
  } finally {
@@ -730,6 +652,250 @@ async function validateIncompleteThemeExports(
730
652
  return issues;
731
653
  }
732
654
 
655
+ /**
656
+ * The module a scratch `theme build` wrote. Its file is named by the theme's
657
+ * own name, which can differ from the slug the package exports it under. A
658
+ * receipt that names no file inside the scratch directory falls back to the
659
+ * slug.
660
+ * @param {any} receipt
661
+ * @param {string} cwd the directory the build ran in
662
+ * @param {string} expectedDir
663
+ * @param {string} slug
664
+ * @returns {string}
665
+ */
666
+ function builtModuleFile(receipt, cwd, expectedDir, slug) {
667
+ const written = receipt?.data?.outputs?.js;
668
+ if (typeof written === 'string') {
669
+ const file = path.resolve(cwd, written);
670
+ const relative = path.relative(expectedDir, file);
671
+ if (relative && !relative.startsWith('..') && !path.isAbsolute(relative)) {
672
+ return file;
673
+ }
674
+ }
675
+ return path.join(expectedDir, `${slug}.js`);
676
+ }
677
+
678
+ /**
679
+ * The `.js` file a relative module specifier names from `fromFile`, the
680
+ * extension `theme build` gives the built module: the exact path, the path
681
+ * with `.js`, or its `index.js`.
682
+ * @param {string} specifier
683
+ * @param {string} fromFile
684
+ * @returns {string | null}
685
+ */
686
+ function resolveRelativeJsModule(specifier, fromFile) {
687
+ if (!specifier.startsWith('./') && !specifier.startsWith('../')) return null;
688
+ const base = path.resolve(path.dirname(fromFile), specifier);
689
+ const candidates = [base, `${base}.js`, path.join(base, 'index.js')];
690
+ return (
691
+ candidates.find(candidate => {
692
+ try {
693
+ return candidate.endsWith('.js') && fs.statSync(candidate).isFile();
694
+ } catch {
695
+ return false;
696
+ }
697
+ }) ?? null
698
+ );
699
+ }
700
+
701
+ /** @param {string} file */
702
+ function realFile(file) {
703
+ try {
704
+ return fs.realpathSync(file);
705
+ } catch {
706
+ return null;
707
+ }
708
+ }
709
+
710
+ /**
711
+ * The file a stylesheet import names: a relative path from the module, or a
712
+ * subpath of the package itself, resolved through its own exports.
713
+ * @param {string} specifier
714
+ * @param {string} moduleFile
715
+ * @param {{name?: string}} pkg
716
+ * @param {string} packageDir
717
+ * @returns {string | null}
718
+ */
719
+ function resolveStylesheetImport(specifier, moduleFile, pkg, packageDir) {
720
+ if (specifier.startsWith('./') || specifier.startsWith('../')) {
721
+ const file = path.resolve(path.dirname(moduleFile), specifier);
722
+ try {
723
+ return fs.statSync(file).isFile() ? realFile(file) : null;
724
+ } catch {
725
+ return null;
726
+ }
727
+ }
728
+ if (typeof pkg.name !== 'string' || !specifier.startsWith(`${pkg.name}/`)) {
729
+ return null;
730
+ }
731
+ const exported = inspectPackageExport(
732
+ pkg,
733
+ packageDir,
734
+ `.${specifier.slice(pkg.name.length)}`,
735
+ );
736
+ return exported.exported && exported.target
737
+ ? realFile(exported.target)
738
+ : null;
739
+ }
740
+
741
+ /**
742
+ * The theme binding of a built module: the name of its `export const`.
743
+ * @param {string} file
744
+ * @returns {string | null}
745
+ */
746
+ function builtThemeBinding(file) {
747
+ try {
748
+ const ast = parseModule(fs.readFileSync(file, 'utf-8'), {
749
+ sourceType: 'module',
750
+ plugins: ['importAttributes'],
751
+ });
752
+ for (const node of ast.program.body) {
753
+ if (
754
+ node.type === 'ExportNamedDeclaration' &&
755
+ node.declaration?.type === 'VariableDeclaration'
756
+ ) {
757
+ const id = node.declaration.declarations[0]?.id;
758
+ return id?.type === 'Identifier' ? id.name : null;
759
+ }
760
+ }
761
+ } catch {
762
+ // An unreadable module has no binding to match.
763
+ }
764
+ return null;
765
+ }
766
+
767
+ /** @param {any} node */
768
+ function exportedName(node) {
769
+ return node?.name ?? node?.value;
770
+ }
771
+
772
+ /**
773
+ * Check a `.js` or `.mjs` theme module that is not the built module itself.
774
+ * It is accepted when it only loads the theme's own styles and re-exports the
775
+ * built module: each statement imports the theme's exported stylesheet or font
776
+ * stylesheet for its side effects, except one, which re-exports from a `.js`
777
+ * file that matches the built module either everything or only its theme,
778
+ * under the theme's export name. Returns null for any other file and for a module that
779
+ * re-exports nothing, and otherwise whether it is accepted. A
780
+ * rejection gives the reason, or, when only the re-exported module is stale,
781
+ * that module's specifier with `rebuild` set.
782
+ *
783
+ * @param {string} moduleFile
784
+ * @param {string} expected the built module, normalized for comparison
785
+ * @param {{exportName: string, stylesheetFile: string, fontStylesheetFile?: string, pkg: {name?: string}, packageDir: string}} theme
786
+ * @returns {{ok: true} | {ok: false, reason: string, rebuild: boolean} | null}
787
+ */
788
+ function ownStylesEntry(moduleFile, expected, theme) {
789
+ if (!/\.m?js$/u.test(moduleFile)) return null;
790
+ let ast;
791
+ try {
792
+ ast = parseModule(fs.readFileSync(moduleFile, 'utf-8'), {
793
+ sourceType: 'module',
794
+ plugins: ['importAttributes'],
795
+ });
796
+ } catch {
797
+ return null;
798
+ }
799
+ const body = ast.program.body;
800
+ const reexports = body.filter(
801
+ (/** @type {any} */ node) =>
802
+ node.type === 'ExportAllDeclaration' ||
803
+ (node.type === 'ExportNamedDeclaration' && node.source != null),
804
+ );
805
+ if (reexports.length === 0) return null;
806
+ /** @param {string} reason @param {boolean} [rebuild] */
807
+ const reject = (reason, rebuild = false) => ({
808
+ ok: /** @type {const} */ (false),
809
+ reason,
810
+ rebuild,
811
+ });
812
+ if (reexports.length > 1) return reject('it has more than one re-export');
813
+ if ((ast.program.directives ?? []).length > 0) {
814
+ return reject('it has a directive');
815
+ }
816
+ const ownStylesheets = new Set(
817
+ [theme.stylesheetFile, theme.fontStylesheetFile]
818
+ .filter(file => file != null)
819
+ .map(file => realFile(/** @type {string} */ (file))),
820
+ );
821
+ const imported = new Set();
822
+ for (const node of body) {
823
+ if (node === reexports[0]) continue;
824
+ const line = node.loc?.start.line;
825
+ if (node.type !== 'ImportDeclaration') {
826
+ return reject(
827
+ `line ${line} is neither a stylesheet import nor the re-export`,
828
+ );
829
+ }
830
+ const specifier = node.source.value;
831
+ if (
832
+ node.importKind === 'type' ||
833
+ node.specifiers.length > 0 ||
834
+ (node.attributes ?? []).length > 0 ||
835
+ node.phase != null
836
+ ) {
837
+ return reject(`its import of ${specifier} is not a side-effect import`);
838
+ }
839
+ const file = resolveStylesheetImport(
840
+ specifier,
841
+ moduleFile,
842
+ theme.pkg,
843
+ theme.packageDir,
844
+ );
845
+ if (!file || !ownStylesheets.has(file)) {
846
+ return reject(
847
+ `${specifier} is not the theme's own stylesheet or font stylesheet`,
848
+ );
849
+ }
850
+ if (imported.has(file)) return reject(`it imports ${specifier} twice`);
851
+ imported.add(file);
852
+ }
853
+ const reexport = /** @type {any} */ (reexports[0]);
854
+ const from = reexport.source.value;
855
+ /** @type {string | null} */
856
+ let reexported = null;
857
+ if (
858
+ reexport.exportKind === 'type' ||
859
+ (reexport.attributes ?? []).length > 0
860
+ ) {
861
+ return reject(`its re-export from ${from} is not a plain re-export`);
862
+ }
863
+ if (reexport.type === 'ExportAllDeclaration') {
864
+ if (reexport.exported != null) {
865
+ return reject(`it re-exports ${from} as a namespace`);
866
+ }
867
+ } else {
868
+ const specifiers = reexport.specifiers ?? [];
869
+ if (
870
+ specifiers.length !== 1 ||
871
+ specifiers[0].type !== 'ExportSpecifier' ||
872
+ specifiers[0].exportKind === 'type' ||
873
+ exportedName(specifiers[0].exported) !== theme.exportName
874
+ ) {
875
+ return reject(`it re-exports names other than ${theme.exportName}`);
876
+ }
877
+ reexported = exportedName(specifiers[0].local);
878
+ }
879
+ const target = resolveRelativeJsModule(from, moduleFile);
880
+ if (!target) {
881
+ return reject(`its re-export from ${from} does not name a .js module`);
882
+ }
883
+ if (
884
+ normalizeThemeVerifyForCompare(fs.readFileSync(target, 'utf-8')) !==
885
+ expected
886
+ ) {
887
+ return reject(from, true);
888
+ }
889
+ // `theme build` names the theme's export after the theme's own name, which
890
+ // can differ from its slug, so a named re-export may give that export the
891
+ // slug's export name. It must be the theme, not another export of the built
892
+ // module, such as its icon registry.
893
+ if (reexported != null && reexported !== builtThemeBinding(target)) {
894
+ return reject(`it re-exports ${reexported}, which is not the built theme`);
895
+ }
896
+ return {ok: /** @type {const} */ (true)};
897
+ }
898
+
733
899
  /**
734
900
  * Prove local theme exports are fresh, prove the same exports resolve from the
735
901
  * tarball, and reject lifecycle changes to source or built output bytes.
@@ -796,7 +962,7 @@ async function validatePackedThemeExports(
796
962
  issues.push(
797
963
  warning(
798
964
  'theme_exports_missing',
799
- `Theme "${localTheme.slug}" has no declared built exports. The package remains source-compatible. To make it importable, run \`astryx theme build ${sourceRelative}\` and export "./themes/${localTheme.slug}" plus "./themes/${localTheme.slug}.css" from package.json; \`astryx integration add theme ${localTheme.slug}\` creates those exports for a new theme.`,
965
+ `Theme "${localTheme.slug}" has no declared built exports. The package remains source-compatible. To make it importable, run \`${formatCliCommand(`theme build ${sourceRelative}`, localIntegration.__packageDir)}\` and export "./themes/${localTheme.slug}" plus "./themes/${localTheme.slug}.css" from package.json; \`${formatCliCommand(`integration add theme ${localTheme.slug}`, localIntegration.__packageDir)}\` creates those exports for a new theme.`,
800
966
  ),
801
967
  );
802
968
  continue;
@@ -836,7 +1002,7 @@ async function validatePackedThemeExports(
836
1002
  err instanceof ThemeImportError
837
1003
  ? 'theme_import_unresolvable'
838
1004
  : 'theme_import_invalid',
839
- `${message} Run \`astryx theme build ${sourceRelative}\` before packing.`,
1005
+ `${message} Run \`${formatCliCommand(`theme build ${sourceRelative}`, localIntegration.__packageDir)}\` before packing.`,
840
1006
  ),
841
1007
  );
842
1008
  continue;
@@ -847,21 +1013,27 @@ async function validatePackedThemeExports(
847
1013
  );
848
1014
  const expectedCss = path.join(expectedDir, `${localTheme.slug}.css`);
849
1015
  try {
850
- const receipt = await themeBuild(
851
- source,
852
- {out: expectedCss},
853
- {cwd: localIntegration.__packageDir},
854
- );
1016
+ // The scratch directory is the write root; the package stays the cwd,
1017
+ // so the build selects the package's Core and its paths as before.
1018
+ const scratchBuild = {out: expectedCss, __writeRoot: expectedDir};
1019
+ const receipt = await themeBuild(source, scratchBuild, {
1020
+ cwd: localIntegration.__packageDir,
1021
+ });
855
1022
  if (receipt == null || receipt.type !== 'theme.build') {
856
1023
  issues.push(
857
1024
  error(
858
1025
  'theme_build_failed',
859
- `Theme "${localTheme.slug}" did not produce a complete build. Run \`astryx theme build ${sourceRelative}\` before packing.`,
1026
+ `Theme "${localTheme.slug}" did not produce a complete build. Run \`${formatCliCommand(`theme build ${sourceRelative}`, localIntegration.__packageDir)}\` before packing.`,
860
1027
  ),
861
1028
  );
862
1029
  continue;
863
1030
  }
864
- const expectedModule = path.join(expectedDir, `${localTheme.slug}.js`);
1031
+ const expectedModule = builtModuleFile(
1032
+ receipt,
1033
+ localIntegration.__packageDir,
1034
+ expectedDir,
1035
+ localTheme.slug,
1036
+ );
865
1037
  /** @type {Array<{kind: string, expected: string, local: string, packed: string, staleCode: string, packedCode: string}>} */
866
1038
  const buildOutputs = [
867
1039
  {
@@ -881,6 +1053,20 @@ async function validatePackedThemeExports(
881
1053
  packedCode: 'packed_theme_stylesheet_changed',
882
1054
  },
883
1055
  ];
1056
+ const localEntry = {
1057
+ exportName: localTheme.exportName,
1058
+ stylesheetFile: localImports.stylesheetFile,
1059
+ fontStylesheetFile: localImports.fontStylesheetFile,
1060
+ pkg: integrationPackageMetadata(localIntegration),
1061
+ packageDir: localIntegration.__packageDir,
1062
+ };
1063
+ const packedEntry = {
1064
+ exportName: packedTheme.exportName,
1065
+ stylesheetFile: packedImports.stylesheetFile,
1066
+ fontStylesheetFile: packedImports.fontStylesheetFile,
1067
+ pkg: integrationPackageMetadata(packedIntegration),
1068
+ packageDir: packedIntegration.__packageDir,
1069
+ };
884
1070
  for (const output of buildOutputs) {
885
1071
  const expected = normalizeThemeVerifyForCompare(
886
1072
  fs.readFileSync(output.expected, 'utf-8'),
@@ -891,15 +1077,31 @@ async function validatePackedThemeExports(
891
1077
  const packed = normalizeThemeVerifyForCompare(
892
1078
  fs.readFileSync(output.packed, 'utf-8'),
893
1079
  );
894
- if (expected !== local) {
1080
+ // A module that is not the built module may still be an entry that
1081
+ // loads the theme's own styles and re-exports the built module.
1082
+ const localWrapper =
1083
+ output.kind === 'module' && expected !== local
1084
+ ? ownStylesEntry(output.local, expected, localEntry)
1085
+ : null;
1086
+ const packedWrapper =
1087
+ output.kind === 'module' && expected !== packed
1088
+ ? ownStylesEntry(output.packed, expected, packedEntry)
1089
+ : null;
1090
+ const localMatches = expected === local || localWrapper?.ok === true;
1091
+ const packedMatches = expected === packed || packedWrapper?.ok === true;
1092
+ if (!localMatches) {
895
1093
  issues.push(
896
1094
  error(
897
1095
  output.staleCode,
898
- `Theme "${localTheme.slug}" ${output.kind} does not match ${sourceRelative}. Run \`astryx theme build ${sourceRelative}\` before packing.`,
1096
+ localWrapper && !localWrapper.ok && !localWrapper.rebuild
1097
+ ? `Theme "${localTheme.slug}" module is not the module \`${formatCliCommand(`theme build ${sourceRelative}`, localIntegration.__packageDir)}\` writes and does not only wrap it: ${localWrapper.reason}. A module that wraps the built module may only import the theme's own stylesheet and font stylesheet for their side effects and re-export from the built module either everything or only its theme, as ${localTheme.exportName}.`
1098
+ : localWrapper && !localWrapper.ok
1099
+ ? `Theme "${localTheme.slug}" module re-exports ${localWrapper.reason}, which does not match ${sourceRelative}. Run \`${formatCliCommand(`theme build ${sourceRelative}`, localIntegration.__packageDir)}\` before packing.`
1100
+ : `Theme "${localTheme.slug}" ${output.kind} does not match ${sourceRelative}. Run \`${formatCliCommand(`theme build ${sourceRelative}`, localIntegration.__packageDir)}\` before packing.`,
899
1101
  ),
900
1102
  );
901
1103
  }
902
- if (local !== packed) {
1104
+ if (local !== packed || (localMatches && !packedMatches)) {
903
1105
  issues.push(
904
1106
  error(
905
1107
  output.packedCode,
@@ -927,7 +1129,7 @@ async function validatePackedThemeExports(
927
1129
  issues.push(
928
1130
  error(
929
1131
  'theme_build_failed',
930
- `Theme "${localTheme.slug}" could not be rebuilt from ${sourceRelative}: ${err instanceof Error ? err.message : String(err)} Run \`astryx theme build ${sourceRelative}\` before packing.`,
1132
+ `Theme "${localTheme.slug}" could not be rebuilt from ${sourceRelative}: ${err instanceof Error ? err.message : String(err)} Run \`${formatCliCommand(`theme build ${sourceRelative}`, localIntegration.__packageDir)}\` before packing.`,
931
1133
  ),
932
1134
  );
933
1135
  } finally {
@@ -1085,6 +1287,24 @@ export async function integrationPackCheck(options = {}) {
1085
1287
  const fileInv = computeRequiredFiles(loaded);
1086
1288
  const {identities: localIdentities, errors: localDiscoveryErrors} =
1087
1289
  await collectIdentities(loaded);
1290
+ // A family lists its members from the module its import names, which the
1291
+ // pack lifecycle may build or rebuild, so they are compared as local
1292
+ // discovery lists them after the pack.
1293
+ const familiesBefore = familyImports(loaded).map(family => family.docPath);
1294
+ /**
1295
+ * @param {Array<{name: string, docPath: string|null}>} records
1296
+ * @param {Set<string>} families
1297
+ */
1298
+ const membersOf = (records, families) =>
1299
+ records
1300
+ .filter(
1301
+ r =>
1302
+ r.docPath != null &&
1303
+ families.has(r.docPath) &&
1304
+ isIntegrationMember(r),
1305
+ )
1306
+ .map(r => r.name);
1307
+ const recordsBefore = discoverIntegrationComponents(loaded);
1088
1308
  for (const discoveryError of localDiscoveryErrors) {
1089
1309
  issues.push(
1090
1310
  error(
@@ -1320,6 +1540,10 @@ export async function integrationPackCheck(options = {}) {
1320
1540
  code: `packed_${issue.code}`,
1321
1541
  severity: issue.severity,
1322
1542
  message: `Packed: ${issue.message}`,
1543
+ // The packed tree keeps the package's own paths.
1544
+ ...(issue.fix != null ? {fix: issue.fix} : {}),
1545
+ ...(issue.file != null ? {file: issue.file} : {}),
1546
+ ...(issue.line != null ? {line: issue.line} : {}),
1323
1547
  });
1324
1548
  }
1325
1549
 
@@ -1345,7 +1569,27 @@ export async function integrationPackCheck(options = {}) {
1345
1569
  );
1346
1570
  }
1347
1571
 
1348
- issues.push(...compareIdentities(localIdentities, packedIdentities));
1572
+ const families = new Set([
1573
+ ...familiesBefore,
1574
+ ...familyImports(loaded).map(family => family.docPath),
1575
+ ]);
1576
+ let localComponents = localIdentities.components;
1577
+ if (families.size > 0) {
1578
+ const stale = new Set(membersOf(recordsBefore, families));
1579
+ const built = new Set(
1580
+ membersOf(discoverIntegrationComponents(loaded), families),
1581
+ );
1582
+ const {identities: afterPack} = await collectIdentities(loaded);
1583
+ localComponents = [
1584
+ ...new Set([
1585
+ ...localComponents.filter(name => !stale.has(name)),
1586
+ ...afterPack.components.filter(name => built.has(name)),
1587
+ ]),
1588
+ ].sort();
1589
+ }
1590
+ // The receipt reports the local inventory that was compared.
1591
+ const comparedLocal = {...localIdentities, components: localComponents};
1592
+ issues.push(...compareIdentities(comparedLocal, packedIdentities));
1349
1593
  issues.push(
1350
1594
  ...(await validatePackedThemeExports(
1351
1595
  loaded,
@@ -1367,7 +1611,7 @@ export async function integrationPackCheck(options = {}) {
1367
1611
  pkgVersion,
1368
1612
  fileInv,
1369
1613
  packResult,
1370
- localIdentities,
1614
+ comparedLocal,
1371
1615
  packedIdentities,
1372
1616
  issues,
1373
1617
  );