@astryxdesign/cli 0.7.2-canary.fdd384f → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +169 -149
  3. package/api/build/_adapter.d.mts +8 -0
  4. package/api/build/_adapter.mjs +12 -1
  5. package/api/build/build.doc.mjs +4 -0
  6. package/api/build/kit/kit.mjs +3 -11
  7. package/api/component/_adapter.d.mts +31 -0
  8. package/api/component/_adapter.mjs +119 -11
  9. package/api/component/component.doc.mjs +6 -1
  10. package/api/component/component.mjs +60 -10
  11. package/api/component/doc-links.test.mjs +386 -0
  12. package/api/discover/_adapter.d.mts +132 -12
  13. package/api/discover/_adapter.mjs +488 -27
  14. package/api/discover/_adapter.test.mjs +599 -17
  15. package/api/discover/_catalog-view.d.mts +16 -5
  16. package/api/discover/_catalog-view.mjs +32 -8
  17. package/api/discover/detail/doc/doc.mjs +9 -1
  18. package/api/discover/discover.d.mts +11 -0
  19. package/api/discover/discover.doc.mjs +9 -7
  20. package/api/discover/discover.mjs +121 -19
  21. package/api/discover/discover.type.d.mts +20 -7
  22. package/api/discover/discover.type.mjs +17 -7
  23. package/api/discover/list/list.d.mts +4 -3
  24. package/api/discover/list/list.mjs +17 -10
  25. package/api/docs/_adapter.d.mts +63 -0
  26. package/api/docs/_adapter.mjs +312 -18
  27. package/api/docs/docs.doc.mjs +2 -1
  28. package/api/docs/docs.mjs +2 -1
  29. package/api/docs/docs.test.mjs +5 -2
  30. package/api/docs/integration-tree.test.mjs +22 -0
  31. package/api/docs/list/list.mjs +12 -2
  32. package/api/docs/reference-blocks.test.mjs +1 -3
  33. package/api/doctor/_adapter.d.mts +108 -0
  34. package/api/doctor/_adapter.mjs +223 -4
  35. package/api/doctor/doctor.d.mts +199 -0
  36. package/api/doctor/doctor.doc.mjs +3 -2
  37. package/api/doctor/doctor.mjs +530 -34
  38. package/api/doctor/doctor.test.mjs +180 -14
  39. package/api/doctor/release-checks.test.mjs +156 -0
  40. package/api/doctor/theme-checks.mjs +16 -3
  41. package/api/doctor/theme-checks.test.mjs +223 -7
  42. package/api/error.d.mts +10 -0
  43. package/api/error.mjs +25 -0
  44. package/api/gap-report/gap-report.d.mts +3 -0
  45. package/api/gap-report/gap-report.doc.mjs +11 -3
  46. package/api/gap-report/gap-report.mjs +152 -48
  47. package/api/gap-report/gap-report.test.mjs +233 -0
  48. package/api/gap-report/gap-report.type.d.mts +41 -0
  49. package/api/gap-report/gap-report.type.mjs +18 -0
  50. package/api/hook/_adapter.mjs +3 -11
  51. package/api/hook/hook.doc.mjs +4 -0
  52. package/api/hook/list/list.d.mts +1 -1
  53. package/api/init/_adapter.d.mts +22 -0
  54. package/api/init/_adapter.mjs +59 -0
  55. package/api/init/init.d.mts +7 -5
  56. package/api/init/init.doc.mjs +28 -7
  57. package/api/init/init.mjs +28 -4
  58. package/api/init/init.type.d.mts +48 -3
  59. package/api/init/init.type.mjs +37 -3
  60. package/api/init/run/run.d.mts +1 -1
  61. package/api/init/run/run.mjs +15 -4
  62. package/api/init/user/user.d.mts +14 -0
  63. package/api/init/user/user.mjs +87 -0
  64. package/api/init/user/user.test.mjs +232 -0
  65. package/api/integration/_adapter.d.mts +33 -0
  66. package/api/integration/_adapter.mjs +58 -0
  67. package/api/integration/authoring-checks.mjs +100 -5
  68. package/api/integration/authoring-checks.test.mjs +769 -0
  69. package/api/integration/authoring-checks.type.d.mts +134 -0
  70. package/api/integration/authoring-checks.type.mjs +60 -1
  71. package/api/integration/integrationCheck.doc.mjs +1 -1
  72. package/api/integration/integrationComponentConflicts.doc.mjs +11 -3
  73. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  74. package/api/integration/pack-check.mjs +303 -13
  75. package/api/integration/pack-check.test.mjs +371 -10
  76. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  77. package/api/integration/validate-integration.d.mts +8 -0
  78. package/api/integration/validate-integration.mjs +55 -9
  79. package/api/integration/validate-integration.test.mjs +77 -8
  80. package/api/integration/validate-integration.type.d.mts +8 -0
  81. package/api/integration/validate-integration.type.mjs +3 -1
  82. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  83. package/api/integration/validateIntegration.doc.mjs +4 -2
  84. package/api/search/_adapter.d.mts +11 -0
  85. package/api/search/_adapter.mjs +25 -0
  86. package/api/search/search.d.mts +73 -13
  87. package/api/search/search.doc.mjs +8 -3
  88. package/api/search/search.domain-priority.test.mjs +108 -18
  89. package/api/search/search.mjs +541 -52
  90. package/api/search/search.test.mjs +143 -1
  91. package/api/search/search.type.d.mts +14 -1
  92. package/api/search/search.type.mjs +6 -1
  93. package/api/swizzle/_adapter.mjs +2 -2
  94. package/api/swizzle/copy/copy.mjs +1 -1
  95. package/api/template/template-integration.test.mjs +1 -1
  96. package/api/theme/_adapter.d.mts +16 -0
  97. package/api/theme/_adapter.mjs +32 -5
  98. package/api/theme/build/build.d.mts +2 -1
  99. package/api/theme/build/build.mjs +70 -14
  100. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  101. package/api/theme/build/font-warning.mjs +5 -3
  102. package/api/theme/build/font-warning.test.mjs +2 -1
  103. package/api/theme/remove/remove.test.mjs +2 -2
  104. package/api/theme/targets/targets.mjs +3 -10
  105. package/api/theme/themeTargets.doc.mjs +4 -0
  106. package/api/theme/use/use.test.mjs +1 -1
  107. package/api/upgrade/_adapter.d.mts +40 -2
  108. package/api/upgrade/_adapter.mjs +118 -5
  109. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  110. package/api/upgrade/run/run.mjs +35 -2
  111. package/api/upgrade/status/status.d.mts +21 -5
  112. package/api/upgrade/status/status.mjs +45 -8
  113. package/api/upgrade/upgrade.doc.mjs +6 -3
  114. package/api/upgrade/upgrade.mjs +5 -1
  115. package/api/upgrade/upgrade.type.d.mts +23 -0
  116. package/api/upgrade/upgrade.type.mjs +16 -1
  117. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  118. package/assets/codemods/registry.mjs +1 -0
  119. package/assets/codemods/runner.mjs +7 -1
  120. package/assets/codemods/transforms/next/index.mjs +1 -11
  121. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  122. package/assets/discover/official-packages.json +367 -0
  123. package/assets/docs/author-a-theme.doc.mjs +2 -2
  124. package/assets/docs/browser-support.doc.mjs +1 -1
  125. package/assets/docs/getting-started.doc.mjs +23 -3
  126. package/assets/docs/styling-overview.doc.mjs +1 -1
  127. package/assets/docs/theme.doc.mjs +1 -1
  128. package/assets/docs/tree/add-a-component.doc.mjs +12 -2
  129. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  130. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  131. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  132. package/assets/docs/tree/block-template.doc.mjs +6 -18
  133. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  134. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  135. package/assets/docs/tree/check-the-props.doc.mjs +8 -3
  136. package/assets/docs/tree/check-your-docs.doc.mjs +30 -13
  137. package/assets/docs/tree/checks.doc.mjs +17 -17
  138. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  139. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  140. package/assets/docs/tree/cli.doc.mjs +6 -0
  141. package/assets/docs/tree/codemods.doc.mjs +17 -7
  142. package/assets/docs/tree/commands.doc.mjs +7 -1
  143. package/assets/docs/tree/component-family.doc.mjs +3 -5
  144. package/assets/docs/tree/component-imports.doc.mjs +13 -3
  145. package/assets/docs/tree/components.doc.mjs +7 -1
  146. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  147. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  148. package/assets/docs/tree/configuration.doc.mjs +7 -3
  149. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  150. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  151. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  152. package/assets/docs/tree/docs.doc.mjs +9 -3
  153. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  154. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  155. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  156. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  157. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  158. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  159. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  160. package/assets/docs/tree/global.doc.mjs +131 -0
  161. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  162. package/assets/docs/tree/install-once.doc.mjs +105 -0
  163. package/assets/docs/tree/integrations.doc.mjs +13 -1
  164. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  165. package/assets/docs/tree/layout.doc.mjs +6 -6
  166. package/assets/docs/tree/links.doc.mjs +6 -5
  167. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  168. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  169. package/assets/docs/tree/migration.doc.mjs +0 -1
  170. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  171. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  172. package/assets/docs/tree/page-template.doc.mjs +3 -7
  173. package/assets/docs/tree/problems.doc.mjs +205 -0
  174. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  175. package/assets/docs/tree/publishing.doc.mjs +6 -6
  176. package/assets/docs/tree/quick-start.doc.mjs +52 -26
  177. package/assets/docs/tree/reference.doc.mjs +192 -0
  178. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  179. package/assets/docs/tree/replace-a-core-template.doc.mjs +9 -11
  180. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  181. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  182. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  183. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  184. package/assets/docs/tree/single-component.doc.mjs +14 -6
  185. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  186. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  187. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  188. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  189. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  190. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  191. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  192. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  193. package/assets/docs/tree/templates.doc.mjs +7 -1
  194. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  195. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  196. package/assets/docs/tree/themes.doc.mjs +4 -0
  197. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  198. package/assets/docs/tree/troubleshooting.doc.mjs +22 -14
  199. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  200. package/assets/docs/tree/updates.doc.mjs +92 -0
  201. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  202. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  203. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  204. package/assets/docs/tree/versioning.doc.mjs +9 -9
  205. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  206. package/assets/docs/tree/write-the-template-file.doc.mjs +5 -5
  207. package/assets/docs/working-with-ai.doc.mjs +23 -9
  208. package/authoring/codemod/codemod.doc.mjs +7 -3
  209. package/authoring/config/config.doc.mjs +9 -6
  210. package/authoring/config/type.ts +4 -2
  211. package/authoring/debug/parse.d.mts +5 -5
  212. package/authoring/discover/discover.doc.mjs +7 -1
  213. package/authoring/doctypes/_schema.d.mts +8 -3
  214. package/authoring/doctypes/_schema.mjs +1 -0
  215. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  216. package/authoring/doctypes/command/command.doc.mjs +20 -12
  217. package/authoring/doctypes/command/type.ts +14 -0
  218. package/authoring/doctypes/component/component.doc.mjs +29 -26
  219. package/authoring/doctypes/component/type.ts +15 -5
  220. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  221. package/authoring/doctypes/function/function.doc.mjs +9 -8
  222. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  223. package/authoring/doctypes/hook/type.ts +4 -0
  224. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  225. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  226. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  227. package/authoring/doctypes/template/template.doc.mjs +2 -2
  228. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  229. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  230. package/authoring/identity/identity.doc.mjs +3 -3
  231. package/authoring/index.d.ts +1 -0
  232. package/authoring/integration/integration.doc.mjs +22 -5
  233. package/authoring/integration/parse.test.mjs +11 -0
  234. package/authoring/integration/schema.d.mts +14 -3
  235. package/authoring/integration/schema.mjs +33 -2
  236. package/authoring/integration/type.ts +8 -0
  237. package/clients/cli/bin/astryx.mjs +20 -3
  238. package/clients/cli/cli-command.test.mjs +166 -0
  239. package/clients/cli/commands/blog.doc.mjs +1 -0
  240. package/clients/cli/commands/build-theme.batch-report.test.mjs +14 -2
  241. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  242. package/clients/cli/commands/build-theme.mjs +25 -12
  243. package/clients/cli/commands/build.doc.mjs +1 -0
  244. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  245. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  246. package/clients/cli/commands/component.doc.mjs +1 -0
  247. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  248. package/clients/cli/commands/discover.doc.mjs +9 -1
  249. package/clients/cli/commands/discover.mjs +144 -93
  250. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  251. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  252. package/clients/cli/commands/docs.doc.mjs +1 -0
  253. package/clients/cli/commands/docs.mjs +49 -19
  254. package/clients/cli/commands/docs.test.mjs +2 -2
  255. package/clients/cli/commands/doctor-integration-components.doc.mjs +7 -3
  256. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  257. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  258. package/clients/cli/commands/doctor-integration-validate.doc.mjs +13 -2
  259. package/clients/cli/commands/doctor-integration.test.mjs +52 -4
  260. package/clients/cli/commands/doctor.doc.mjs +12 -2
  261. package/clients/cli/commands/doctor.mjs +102 -7
  262. package/clients/cli/commands/doctor.test.mjs +10 -4
  263. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  264. package/clients/cli/commands/gap-report.mjs +24 -2
  265. package/clients/cli/commands/gap-report.test.mjs +79 -0
  266. package/clients/cli/commands/hook.doc.mjs +1 -0
  267. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  268. package/clients/cli/commands/init.doc.mjs +29 -3
  269. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  270. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  271. package/clients/cli/commands/integration-check.doc.mjs +10 -2
  272. package/clients/cli/commands/integration-check.test.mjs +175 -1
  273. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  274. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  275. package/clients/cli/commands/integration.mjs +15 -9
  276. package/clients/cli/commands/json-contract.test.mjs +11 -2
  277. package/clients/cli/commands/manifest.doc.mjs +1 -0
  278. package/clients/cli/commands/outside-project.test.mjs +774 -0
  279. package/clients/cli/commands/search.doc.mjs +1 -0
  280. package/clients/cli/commands/search.mjs +16 -11
  281. package/clients/cli/commands/search.test.mjs +429 -296
  282. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  283. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  284. package/clients/cli/commands/swizzle.mjs +5 -3
  285. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  286. package/clients/cli/commands/template.doc.mjs +1 -0
  287. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  288. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  289. package/clients/cli/commands/theme-build.doc.mjs +1 -0
  290. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  291. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  292. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  293. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  294. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  295. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  296. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  297. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  298. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  299. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  300. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  301. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  302. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  303. package/clients/cli/index.mjs +111 -16
  304. package/clients/cli/lib/cli-error.mjs +4 -1
  305. package/clients/cli/lib/component-format.test.mjs +1 -1
  306. package/clients/cli/lib/define-command.mjs +2 -2
  307. package/clients/cli/lib/json-shim.mjs +3 -1
  308. package/clients/cli/lib/manifest.d.ts +9 -0
  309. package/clients/cli/lib/manifest.mjs +17 -1
  310. package/clients/cli/lib/manifest.test.mjs +25 -0
  311. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  312. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  313. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  314. package/foundation/agent-docs/agent-docs.mjs +11 -9
  315. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  316. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  317. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  318. package/foundation/config/project.d.mts +35 -3
  319. package/foundation/config/project.mjs +145 -8
  320. package/foundation/config/project.test.mjs +66 -0
  321. package/foundation/config/theme-state.mjs +5 -4
  322. package/foundation/discovery/authoring-self-docs.mjs +1 -1
  323. package/foundation/discovery/cli-self-docs.mjs +20 -3
  324. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  325. package/foundation/discovery/component-replacement.mjs +2 -1
  326. package/foundation/discovery/copied-passages.d.mts +216 -0
  327. package/foundation/discovery/copied-passages.mjs +720 -0
  328. package/foundation/discovery/copied-passages.test.mjs +124 -0
  329. package/foundation/discovery/doc-consistency.d.mts +34 -0
  330. package/foundation/discovery/doc-consistency.mjs +513 -0
  331. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  332. package/foundation/discovery/doc-fluff.d.mts +112 -0
  333. package/foundation/discovery/doc-fluff.mjs +328 -0
  334. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  335. package/foundation/discovery/doc-types.d.mts +1 -1
  336. package/foundation/discovery/doc-types.mjs +15 -8
  337. package/foundation/discovery/doc-types.test.mjs +94 -2
  338. package/foundation/discovery/docs-discovery.d.mts +7 -0
  339. package/foundation/discovery/docs-discovery.mjs +26 -2
  340. package/foundation/discovery/project-typescript.d.mts +11 -2
  341. package/foundation/discovery/project-typescript.mjs +32 -4
  342. package/foundation/discovery/prop-drift.d.mts +26 -7
  343. package/foundation/discovery/prop-drift.mjs +173 -8
  344. package/foundation/discovery/prop-drift.test.mjs +120 -2
  345. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  346. package/foundation/discovery/runner-allowlist.mjs +53 -0
  347. package/foundation/discovery/stray-pointers.d.mts +52 -0
  348. package/foundation/discovery/stray-pointers.mjs +293 -0
  349. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  350. package/foundation/discovery/template-needs.mjs +5 -2
  351. package/foundation/discovery/theme-imports.mjs +8 -4
  352. package/foundation/discovery/theme-imports.test.mjs +1 -1
  353. package/foundation/discovery/type-text.mjs +1 -1
  354. package/foundation/discovery/type-text.test.mjs +1 -1
  355. package/foundation/doc-compiler/doc-loads.test.mjs +42 -2
  356. package/foundation/doc-compiler/inputs.mjs +4 -1
  357. package/foundation/doc-compiler/links.d.mts +32 -0
  358. package/foundation/doc-compiler/links.mjs +163 -26
  359. package/foundation/doc-compiler/links.test.mjs +97 -4
  360. package/foundation/doc-compiler/read.d.mts +1 -1
  361. package/foundation/doc-compiler/tree.mjs +30 -1
  362. package/foundation/doc-compiler/tree.test.mjs +8 -1
  363. package/foundation/env/cli-command.d.mts +83 -0
  364. package/foundation/env/cli-command.mjs +152 -0
  365. package/foundation/env/cli-command.test.mjs +129 -0
  366. package/foundation/env/node-version.test.mjs +15 -3
  367. package/foundation/env/package-manager.d.mts +93 -9
  368. package/foundation/env/package-manager.global.test.mjs +66 -0
  369. package/foundation/env/package-manager.mjs +184 -13
  370. package/foundation/env/package-manager.test.mjs +341 -15
  371. package/foundation/env/project-scope.d.mts +9 -0
  372. package/foundation/env/project-scope.mjs +33 -0
  373. package/foundation/env/search-path.d.mts +42 -0
  374. package/foundation/env/search-path.mjs +205 -0
  375. package/foundation/env/search-path.test.mjs +122 -0
  376. package/foundation/env/user-dirs.d.mts +39 -0
  377. package/foundation/env/user-dirs.mjs +93 -0
  378. package/foundation/env/user-dirs.test.mjs +72 -0
  379. package/foundation/env/user-settings.d.mts +46 -0
  380. package/foundation/env/user-settings.mjs +106 -0
  381. package/foundation/env/user-settings.test.mjs +64 -0
  382. package/foundation/fs/file-protection.mjs +15 -1
  383. package/foundation/fs/module-loader.d.mts +2 -1
  384. package/foundation/fs/module-loader.mjs +60 -4
  385. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  386. package/foundation/fs/module-loader.test.mjs +194 -0
  387. package/foundation/fs/path-compare.d.mts +33 -0
  388. package/foundation/fs/path-compare.mjs +73 -0
  389. package/foundation/fs/paths.d.mts +57 -1
  390. package/foundation/fs/paths.mjs +131 -3
  391. package/foundation/fs/paths.test.mjs +10 -1
  392. package/foundation/integrations/computer-integrations.d.mts +78 -0
  393. package/foundation/integrations/computer-integrations.mjs +316 -0
  394. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  395. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  396. package/foundation/integrations/contribution-inventory.mjs +50 -2
  397. package/foundation/integrations/integration-warnings.mjs +3 -2
  398. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  399. package/foundation/integrations/integrations.d.mts +25 -1
  400. package/foundation/integrations/integrations.mjs +46 -3
  401. package/foundation/integrations/issue.ts +6 -0
  402. package/foundation/integrations/validate-contributions.d.mts +2 -0
  403. package/foundation/integrations/validate-contributions.mjs +41 -1
  404. package/foundation/release/agent-text-worker.d.mts +4 -0
  405. package/foundation/release/agent-text-worker.mjs +52 -0
  406. package/foundation/release/cache.d.mts +310 -0
  407. package/foundation/release/cache.mjs +1216 -0
  408. package/foundation/release/cache.test.mjs +920 -0
  409. package/foundation/release/choose.d.mts +207 -0
  410. package/foundation/release/choose.mjs +677 -0
  411. package/foundation/release/choose.test.mjs +427 -0
  412. package/foundation/release/core-worker.d.mts +4 -0
  413. package/foundation/release/core-worker.mjs +38 -0
  414. package/foundation/release/global-state.d.mts +259 -0
  415. package/foundation/release/global-state.mjs +472 -0
  416. package/foundation/release/global-state.test.mjs +381 -0
  417. package/foundation/release/handoff.d.mts +60 -0
  418. package/foundation/release/handoff.e2e.test.mjs +278 -0
  419. package/foundation/release/handoff.mjs +357 -0
  420. package/foundation/release/lock-lease.d.mts +23 -0
  421. package/foundation/release/lock-lease.mjs +80 -0
  422. package/foundation/release/notices.d.mts +253 -0
  423. package/foundation/release/notices.mjs +596 -0
  424. package/foundation/release/notices.test.mjs +565 -0
  425. package/foundation/release/npm-guard.d.mts +4 -0
  426. package/foundation/release/npm-guard.mjs +126 -0
  427. package/foundation/release/project.d.mts +141 -0
  428. package/foundation/release/project.mjs +466 -0
  429. package/foundation/release/project.test.mjs +288 -0
  430. package/foundation/release/refresh-worker.d.mts +4 -0
  431. package/foundation/release/refresh-worker.mjs +27 -0
  432. package/foundation/release/run-context.d.mts +47 -0
  433. package/foundation/release/run-context.mjs +149 -0
  434. package/foundation/release/update-notice-env.d.mts +28 -0
  435. package/foundation/release/update-notice-env.mjs +39 -0
  436. package/foundation/response/base.d.ts +26 -3
  437. package/foundation/response/error-codes.d.mts +3 -1
  438. package/foundation/response/error-codes.doc.mjs +12 -0
  439. package/foundation/response/error-codes.mjs +17 -0
  440. package/foundation/response/error-codes.test.mjs +2 -0
  441. package/foundation/response/json-contract.test.mjs +7 -2
  442. package/foundation/response/json.mjs +4 -1
  443. package/foundation/response/response-types.doc.mjs +17 -11
  444. package/foundation/response/response-types.doc.test.mjs +46 -0
  445. package/foundation/response/response.doc.mjs +3 -3
  446. package/foundation/xle/browser-imports.test.mjs +74 -0
  447. package/package.json +11 -11
  448. package/scripts/postinstall.mjs +24 -5
  449. /package/assets/codemods/transforms/{next → v0.7.2}/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +0 -0
  450. /package/assets/codemods/transforms/{next → v0.7.2}/migrate-list-edge-compensation-to-edge.mjs +0 -0
@@ -0,0 +1,106 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The CLI's per-user settings file (spec AST-081 FR8):
5
+ * `astryx/settings.json` in the platform's per-user configuration folder.
6
+ *
7
+ * It holds the person's choices about the computer-level agent text:
8
+ *
9
+ * ```json
10
+ * {"computerAgentText": {"install": true, "links": ["/home/me/.claude/skills/astryx"]}}
11
+ * ```
12
+ *
13
+ * - `computerAgentText.install`: `false` after `astryx init --user
14
+ * --remove-agents`, `true` after `astryx init --user`. A missing file or
15
+ * value means install.
16
+ * - `computerAgentText.links`: the links to the skill folder Astryx made, so
17
+ * removal removes exactly those.
18
+ *
19
+ * Only `astryx init --user` and the global copy's refresh change it. No environment variable or option
20
+ * does. A file that cannot be read or parsed counts as missing, and one that
21
+ * cannot be written never fails a run.
22
+ *
23
+ * @input the per-user configuration folder
24
+ * @output the settings, and writes to them
25
+ * @position packages/cli/foundation/env
26
+ */
27
+
28
+ import * as fs from 'node:fs';
29
+ import * as path from 'node:path';
30
+ import {userConfigDir} from './user-dirs.mjs';
31
+
32
+ /**
33
+ * @typedef {object} ComputerAgentTextSettings
34
+ * @property {boolean} install Whether the computer-level text is wanted.
35
+ * @property {string[]} links The links to its skill folder Astryx made, so
36
+ * removal removes exactly those.
37
+ */
38
+
39
+ /**
40
+ * The settings file's path.
41
+ * @param {string} [configDir]
42
+ * @returns {string}
43
+ */
44
+ export function settingsFile(configDir = userConfigDir()) {
45
+ return path.join(configDir, 'settings.json');
46
+ }
47
+
48
+ /**
49
+ * The whole settings object; `{}` when the file is missing or unreadable.
50
+ * @param {string} [configDir]
51
+ * @returns {Record<string, unknown>}
52
+ */
53
+ export function readSettings(configDir = userConfigDir()) {
54
+ try {
55
+ const value = JSON.parse(fs.readFileSync(settingsFile(configDir), 'utf8'));
56
+ return value && typeof value === 'object' && !Array.isArray(value)
57
+ ? value
58
+ : {};
59
+ } catch {
60
+ return {};
61
+ }
62
+ }
63
+
64
+ /**
65
+ * The person's choices about the computer-level agent text.
66
+ * @param {string} [configDir]
67
+ * @returns {ComputerAgentTextSettings}
68
+ */
69
+ export function computerAgentTextSettings(configDir = userConfigDir()) {
70
+ const raw = readSettings(configDir).computerAgentText;
71
+ const value =
72
+ raw && typeof raw === 'object' && !Array.isArray(raw)
73
+ ? /** @type {Record<string, unknown>} */ (raw)
74
+ : {};
75
+ const links = Array.isArray(value.links)
76
+ ? value.links.filter(link => typeof link === 'string')
77
+ : [];
78
+ return {install: value.install !== false, links};
79
+ }
80
+
81
+ /**
82
+ * Record the person's choices about the computer-level agent text, keeping
83
+ * every other setting. Returns whether the file was written.
84
+ * @param {ComputerAgentTextSettings} choice
85
+ * @param {string} [configDir]
86
+ * @returns {boolean}
87
+ */
88
+ export function recordComputerAgentTextSettings(
89
+ choice,
90
+ configDir = userConfigDir(),
91
+ ) {
92
+ const next = {
93
+ ...readSettings(configDir),
94
+ computerAgentText: {install: choice.install, links: choice.links},
95
+ };
96
+ const file = settingsFile(configDir);
97
+ try {
98
+ fs.mkdirSync(path.dirname(file), {recursive: true});
99
+ const tmp = `${file}.${process.pid}.tmp`;
100
+ fs.writeFileSync(tmp, `${JSON.stringify(next, null, 2)}\n`);
101
+ fs.renameSync(tmp, file);
102
+ return true;
103
+ } catch {
104
+ return false;
105
+ }
106
+ }
@@ -0,0 +1,64 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import * as fs from 'node:fs';
4
+ import * as os from 'node:os';
5
+ import * as path from 'node:path';
6
+ import {afterEach, describe, expect, it} from 'vitest';
7
+ import {
8
+ computerAgentTextSettings,
9
+ readSettings,
10
+ recordComputerAgentTextSettings,
11
+ settingsFile,
12
+ } from './user-settings.mjs';
13
+
14
+ /** @type {string[]} */
15
+ const dirs = [];
16
+ const tmp = () => {
17
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-settings-'));
18
+ dirs.push(dir);
19
+ return dir;
20
+ };
21
+ afterEach(() => {
22
+ for (const dir of dirs.splice(0)) {
23
+ fs.rmSync(dir, {recursive: true, force: true});
24
+ }
25
+ });
26
+
27
+ describe('the settings file (spec AST-081 FR8)', () => {
28
+ it('means install, with no links, when missing or unreadable', () => {
29
+ const dir = tmp();
30
+ expect(computerAgentTextSettings(dir)).toEqual({install: true, links: []});
31
+ fs.writeFileSync(settingsFile(dir), 'not json');
32
+ expect(readSettings(dir)).toEqual({});
33
+ expect(computerAgentTextSettings(dir)).toEqual({install: true, links: []});
34
+ });
35
+
36
+ it('records and reads the choice and the links Astryx made, keeping other settings', () => {
37
+ const dir = tmp();
38
+ fs.writeFileSync(settingsFile(dir), '{"other":true}');
39
+ expect(
40
+ recordComputerAgentTextSettings(
41
+ {install: true, links: ['/h/.claude/skills/astryx']},
42
+ dir,
43
+ ),
44
+ ).toBe(true);
45
+ expect(JSON.parse(fs.readFileSync(settingsFile(dir), 'utf8'))).toEqual({
46
+ other: true,
47
+ computerAgentText: {install: true, links: ['/h/.claude/skills/astryx']},
48
+ });
49
+ expect(computerAgentTextSettings(dir)).toEqual({
50
+ install: true,
51
+ links: ['/h/.claude/skills/astryx'],
52
+ });
53
+ recordComputerAgentTextSettings({install: false, links: []}, dir);
54
+ expect(computerAgentTextSettings(dir)).toEqual({install: false, links: []});
55
+ });
56
+
57
+ it('never fails when the folder cannot be written', () => {
58
+ const file = path.join(tmp(), 'file');
59
+ fs.writeFileSync(file, '');
60
+ expect(
61
+ recordComputerAgentTextSettings({install: false, links: []}, file),
62
+ ).toBe(false);
63
+ });
64
+ });
@@ -9,8 +9,22 @@
9
9
  */
10
10
 
11
11
  import * as fs from 'node:fs';
12
+ import {createRequire} from 'node:module';
12
13
  import * as path from 'node:path';
13
- import ignore from 'ignore';
14
+
15
+ /**
16
+ * The `ignore` matcher factory, loaded on first use: this module is reached
17
+ * from every CLI run through the agent-docs helpers, and only a run that
18
+ * classifies a file needs the matcher.
19
+ * @type {typeof import('ignore') | undefined}
20
+ */
21
+ let ignoreFactory;
22
+
23
+ /** @param {import('ignore').Options} [options] */
24
+ function ignore(options) {
25
+ ignoreFactory ??= createRequire(import.meta.url)('ignore');
26
+ return /** @type {typeof import('ignore')} */ (ignoreFactory)(options);
27
+ }
14
28
 
15
29
  const DEPENDENCY_DIRS = new Set([
16
30
  'node_modules',
@@ -21,7 +21,8 @@ export function importUserModule(file: string, { fresh }?: {
21
21
  /**
22
22
  * Load a generated theme artifact and its extensionless JS/JSX/TS/TSX graph.
23
23
  * Package specifiers resolve from the consumer project, while relative paths
24
- * resolve from that same project root.
24
+ * resolve from that same project root. An import of an existing `.css` file
25
+ * loads as an empty module.
25
26
  * @param {string} specifier
26
27
  * @param {string} cwd
27
28
  * @returns {Promise<Record<string, unknown>>}
@@ -7,9 +7,11 @@
7
7
  * user-authored module (`.ts` via jiti, `.mjs`/`.js` via native dynamic
8
8
  * import) and (b) find conventional files by basename in a fixed
9
9
  * load-precedence order. Generated theme artifacts also use a dedicated jiti
10
- * path that resolves extensionless JSX/TSX registry imports. These helpers
11
- * centralize that behavior so callers stay in lockstep. A module's stdout writes go to stderr while it loads, so
12
- * project code that prints cannot corrupt a `--json` envelope.
10
+ * path that resolves extensionless JSX/TSX registry imports and loads the
11
+ * stylesheets they import as empty modules. These helpers centralize that
12
+ * behavior so callers stay in lockstep. A module's stdout writes go to stderr
13
+ * while it loads, so project code that prints cannot corrupt a `--json`
14
+ * envelope.
13
15
  *
14
16
  * `loadModuleWithParser` builds on these primitives to provide the single
15
17
  * load/validation boundary shared by config, integration, codemod, and
@@ -125,6 +127,56 @@ const THEME_ARTIFACT_EXTENSIONS = [
125
127
  '.cts',
126
128
  ];
127
129
 
130
+ /**
131
+ * @param {string | symbol} specifier
132
+ * @returns {specifier is string}
133
+ */
134
+ function isStylesheetSpecifier(specifier) {
135
+ return typeof specifier === 'string' && specifier.endsWith('.css');
136
+ }
137
+
138
+ /**
139
+ * Whether `specifier`, resolved from the module at `from`, names an existing
140
+ * `.css` file.
141
+ * @param {string} specifier
142
+ * @param {string} from
143
+ */
144
+ function resolvesToStylesheet(specifier, from) {
145
+ try {
146
+ return createRequire(from).resolve(specifier).endsWith('.css');
147
+ } catch {
148
+ return false;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * A built theme module may import a stylesheet so that the app's bundler emits
154
+ * it. Node cannot load a stylesheet, so an import that ends in `.css` and
155
+ * resolves from the theme module to an existing `.css` file loads as an empty
156
+ * module. Any other import loads as before: a missing stylesheet fails like
157
+ * any missing import, and `./tokens.css` loads `tokens.css.js` when that is
158
+ * the file it names. jiti shows only the import text, so the check resolves it
159
+ * from the theme module's folder, not from the importing module's. A module in
160
+ * another folder fails on a stylesheet it reaches by a different relative
161
+ * path, and passes on a missing stylesheet whose path exists from the theme
162
+ * module's folder.
163
+ * @param {() => string} themeModule returns the theme module's path
164
+ * @returns {Record<string, unknown>}
165
+ */
166
+ function themeArtifactStylesheets(themeModule) {
167
+ return new Proxy(
168
+ {},
169
+ {
170
+ has: (_target, specifier) =>
171
+ specifier !== themeModule() &&
172
+ isStylesheetSpecifier(specifier) &&
173
+ resolvesToStylesheet(specifier, themeModule()),
174
+ get: (_target, specifier) =>
175
+ isStylesheetSpecifier(specifier) ? {} : undefined,
176
+ },
177
+ );
178
+ }
179
+
128
180
  class ThemeArtifactNotFoundError extends Error {
129
181
  code = 'ERR_THEME_ARTIFACT_NOT_FOUND';
130
182
 
@@ -153,17 +205,20 @@ function isMissingPackageExport(error) {
153
205
  /**
154
206
  * Load a generated theme artifact and its extensionless JS/JSX/TS/TSX graph.
155
207
  * Package specifiers resolve from the consumer project, while relative paths
156
- * resolve from that same project root.
208
+ * resolve from that same project root. An import of an existing `.css` file
209
+ * loads as an empty module.
157
210
  * @param {string} specifier
158
211
  * @param {string} cwd
159
212
  * @returns {Promise<Record<string, unknown>>}
160
213
  */
161
214
  export async function importThemeArtifact(specifier, cwd) {
215
+ let themeModule = path.join(cwd, 'package.json');
162
216
  const loader = createJiti(path.join(cwd, 'package.json'), {
163
217
  moduleCache: false,
164
218
  jsx: true,
165
219
  interopDefault: true,
166
220
  extensions: THEME_ARTIFACT_EXTENSIONS,
221
+ virtualModules: themeArtifactStylesheets(() => themeModule),
167
222
  });
168
223
  let target;
169
224
  if (specifier.startsWith('.') || path.isAbsolute(specifier)) {
@@ -179,6 +234,7 @@ export async function importThemeArtifact(specifier, cwd) {
179
234
  if (!fs.existsSync(target)) {
180
235
  throw new ThemeArtifactNotFoundError(specifier);
181
236
  }
237
+ themeModule = target;
182
238
  return await withStdoutOnStderr(() => loader.import(target));
183
239
  }
184
240
 
@@ -14,6 +14,7 @@ import * as fs from 'node:fs';
14
14
  import * as os from 'node:os';
15
15
  import * as path from 'node:path';
16
16
  import {fileURLToPath, pathToFileURL} from 'node:url';
17
+ import {CLI_VERSION} from '../../test-utils/release-fixtures.mjs';
17
18
 
18
19
  const HERE = path.dirname(fileURLToPath(import.meta.url));
19
20
  const LOADER_URL = pathToFileURL(path.join(HERE, 'module-loader.mjs')).href;
@@ -268,7 +269,7 @@ describe('--json stdout stays one envelope when project modules print', () => {
268
269
  write('package.json', JSON.stringify({name: 'consumer', private: true}));
269
270
  write(
270
271
  'node_modules/@astryxdesign/core/package.json',
271
- JSON.stringify({name: '@astryxdesign/core', version: '0.6.3'}),
272
+ JSON.stringify({name: '@astryxdesign/core', version: CLI_VERSION}),
272
273
  );
273
274
  write(
274
275
  'astryx.config.js',
@@ -289,7 +290,7 @@ describe('--json stdout stays one envelope when project modules print', () => {
289
290
  );
290
291
  write(
291
292
  'node_modules/@astryxdesign/core/package.json',
292
- JSON.stringify({name: '@astryxdesign/core', version: '0.6.3'}),
293
+ JSON.stringify({name: '@astryxdesign/core', version: CLI_VERSION}),
293
294
  );
294
295
  write(
295
296
  'astryx.config.mjs',
@@ -9,6 +9,7 @@ import {pathToFileURL} from 'node:url';
9
9
  import {z} from 'zod';
10
10
  import {
11
11
  findPresentFiles,
12
+ importThemeArtifact,
12
13
  importUserModule,
13
14
  loadModuleWithParser,
14
15
  } from './module-loader.mjs';
@@ -155,6 +156,199 @@ process.stdout.write(JSON.stringify({first: first.default?.answer, fresh: fresh.
155
156
  });
156
157
  });
157
158
 
159
+ describe('importThemeArtifact', () => {
160
+ const STYLESHEET_RAN = '__astryxThemeArtifactStylesheetRan';
161
+
162
+ /** @param {string} file @param {string} content */
163
+ function write(file, content) {
164
+ const target = path.join(tmpDir, file);
165
+ fs.mkdirSync(path.dirname(target), {recursive: true});
166
+ fs.writeFileSync(target, content);
167
+ }
168
+
169
+ beforeEach(() => {
170
+ write('package.json', JSON.stringify({type: 'module'}));
171
+ write(
172
+ 'ocean.css',
173
+ '[data-astryx-theme="ocean"] { --color-text: black; }\n',
174
+ );
175
+ });
176
+
177
+ afterEach(() => {
178
+ Reflect.deleteProperty(globalThis, STYLESHEET_RAN);
179
+ });
180
+
181
+ it('loads a built module that imports its stylesheet, without running the stylesheet', async () => {
182
+ // Valid JavaScript, so running this stylesheet would leave a trace.
183
+ write('ran.css', `globalThis.${STYLESHEET_RAN} = true;\n`);
184
+ write(
185
+ 'ocean.js',
186
+ "import './ocean.css';\nimport './ran.css';\nexport const oceanTheme = {name: 'ocean'};\n",
187
+ );
188
+
189
+ const mod = await importThemeArtifact('./ocean.js', tmpDir);
190
+
191
+ expect(mod.oceanTheme).toEqual({name: 'ocean'});
192
+ expect(Reflect.get(globalThis, STYLESHEET_RAN)).toBeUndefined();
193
+ });
194
+
195
+ it('loads a CommonJS built module that requires its stylesheet', async () => {
196
+ write('commonjs/package.json', JSON.stringify({type: 'commonjs'}));
197
+ write(
198
+ 'commonjs/ocean.css',
199
+ '[data-astryx-theme="ocean"] { --color-text: black; }\n',
200
+ );
201
+ write(
202
+ 'commonjs/ocean.js',
203
+ "require('./ocean.css');\nexports.oceanTheme = {name: 'ocean'};\n",
204
+ );
205
+
206
+ const mod = await importThemeArtifact('./commonjs/ocean.js', tmpDir);
207
+
208
+ expect(mod.oceanTheme).toEqual({name: 'ocean'});
209
+ });
210
+
211
+ it('loads stylesheets imported by a nested module, through a package export, and dynamically', async () => {
212
+ const packageDir = 'node_modules/@acme/themes';
213
+ write(
214
+ `${packageDir}/package.json`,
215
+ JSON.stringify({
216
+ name: '@acme/themes',
217
+ type: 'module',
218
+ exports: {
219
+ './themes/ocean': './dist/ocean.js',
220
+ './themes/ocean.css': './dist/ocean.css',
221
+ },
222
+ }),
223
+ );
224
+ write(
225
+ `${packageDir}/dist/ocean.css`,
226
+ '[data-astryx-theme="ocean"] { --color-text: black; }\n',
227
+ );
228
+ write(
229
+ `${packageDir}/dist/tokens.js`,
230
+ "import '@acme/themes/themes/ocean.css';\nexport const tokens = {};\n",
231
+ );
232
+ write(
233
+ `${packageDir}/dist/ocean.js`,
234
+ "import {tokens} from './tokens.js';\nawait import('./ocean.css');\nexport const oceanTheme = {name: 'ocean', tokens};\n",
235
+ );
236
+
237
+ const mod = await importThemeArtifact('@acme/themes/themes/ocean', tmpDir);
238
+
239
+ expect(mod.oceanTheme).toEqual({name: 'ocean', tokens: {}});
240
+ });
241
+
242
+ it('still resolves an extensionless import to its directory index beside a same-named stylesheet', async () => {
243
+ write('ocean/index.js', "export const oceanTheme = {name: 'ocean'};\n");
244
+ write(
245
+ 'entry.js',
246
+ "import './ocean.css';\nexport {oceanTheme} from './ocean';\n",
247
+ );
248
+
249
+ const mod = await importThemeArtifact('./entry.js', tmpDir);
250
+
251
+ expect(mod.oceanTheme).toEqual({name: 'ocean'});
252
+ });
253
+
254
+ it('loads a module whose import ends in .css but names a JavaScript file', async () => {
255
+ write('tokens.css.js', "export const vars = {accent: 'blue'};\n");
256
+ write(
257
+ 'ocean.js',
258
+ "import './ocean.css';\nimport {vars} from './tokens.css';\nexport const oceanTheme = {name: 'ocean', vars};\n",
259
+ );
260
+
261
+ const mod = await importThemeArtifact('./ocean.js', tmpDir);
262
+
263
+ expect(mod.oceanTheme).toEqual({name: 'ocean', vars: {accent: 'blue'}});
264
+ });
265
+
266
+ it.each([
267
+ ['a missing file', "import './fonts.css';\n", /fonts\.css/],
268
+ [
269
+ 'a package that is not installed',
270
+ "import '@acme/missing-fonts/inter.css';\n",
271
+ /@acme\/missing-fonts\/inter\.css/,
272
+ ],
273
+ ])(
274
+ 'still rejects a stylesheet import of %s',
275
+ async (_label, stylesheetImport, expected) => {
276
+ write(
277
+ 'ocean.js',
278
+ `import './ocean.css';\n${stylesheetImport}export const oceanTheme = {name: 'ocean'};\n`,
279
+ );
280
+
281
+ await expect(importThemeArtifact('./ocean.js', tmpDir)).rejects.toThrow(
282
+ expected,
283
+ );
284
+ },
285
+ );
286
+
287
+ it('still rejects a theme module path that names a stylesheet', async () => {
288
+ await expect(importThemeArtifact('./ocean.css', tmpDir)).rejects.toThrow(
289
+ /Unknown file extension "\.css"/,
290
+ );
291
+ });
292
+
293
+ it('still rejects a stylesheet import that its package does not export', async () => {
294
+ const packageDir = 'node_modules/@acme/themes';
295
+ write(
296
+ `${packageDir}/package.json`,
297
+ JSON.stringify({
298
+ name: '@acme/themes',
299
+ type: 'module',
300
+ exports: {'./themes/ocean': './dist/ocean.js'},
301
+ }),
302
+ );
303
+ write(
304
+ `${packageDir}/dist/ocean.css`,
305
+ '[data-astryx-theme="ocean"] { --color-text: black; }\n',
306
+ );
307
+ write(
308
+ `${packageDir}/dist/ocean.js`,
309
+ "import '@acme/themes/themes/ocean.css';\nexport const oceanTheme = {name: 'ocean'};\n",
310
+ );
311
+
312
+ await expect(
313
+ importThemeArtifact('@acme/themes/themes/ocean', tmpDir),
314
+ ).rejects.toThrow(/not defined by "exports"/);
315
+ });
316
+
317
+ it.each([
318
+ [
319
+ 'a missing dependency',
320
+ "import './missing.js';\nexport const oceanTheme = {name: 'ocean'};\n",
321
+ /missing\.js/,
322
+ ],
323
+ [
324
+ 'a syntax error in a module it imports',
325
+ "import './broken.js';\nexport const oceanTheme = {name: 'ocean'};\n",
326
+ /ParseError/,
327
+ ],
328
+ [
329
+ 'a throwing module',
330
+ "throw new Error('broken theme module');\n",
331
+ /broken theme module/,
332
+ ],
333
+ [
334
+ 'an asset that is not a stylesheet',
335
+ "import './logo.svg';\nexport const oceanTheme = {name: 'ocean'};\n",
336
+ /Unknown file extension "\.svg"/,
337
+ ],
338
+ ])(
339
+ 'still rejects a built module that imports its stylesheet and has %s',
340
+ async (_label, rest, expected) => {
341
+ write('logo.svg', '<svg xmlns="http://www.w3.org/2000/svg"/>\n');
342
+ write('broken.js', 'export const broken = ;\n');
343
+ write('ocean.js', `import './ocean.css';\n${rest}`);
344
+
345
+ await expect(importThemeArtifact('./ocean.js', tmpDir)).rejects.toThrow(
346
+ expected,
347
+ );
348
+ },
349
+ );
350
+ });
351
+
158
352
  describe('loadModuleWithParser', () => {
159
353
  // Temp module files live under a repo-local dir (not /tmp): Vite's dynamic
160
354
  // import blocks /tmp, so we mirror the existing repo-local temp pattern.
@@ -0,0 +1,33 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * Whether the platform's file systems ignore letter case by default.
6
+ * @param {NodeJS.Platform} [platform]
7
+ * @returns {boolean}
8
+ */
9
+ export function ignoresCase(platform?: NodeJS.Platform): boolean;
10
+ /**
11
+ * A path in the form two spellings of one folder share.
12
+ * @param {string} p
13
+ * @param {NodeJS.Platform} [platform]
14
+ * @returns {string}
15
+ */
16
+ export function pathKey(p: string, platform?: NodeJS.Platform): string;
17
+ /**
18
+ * Whether two paths name the same folder or file.
19
+ * @param {string} a
20
+ * @param {string} b
21
+ * @param {NodeJS.Platform} [platform]
22
+ * @returns {boolean}
23
+ */
24
+ export function samePath(a: string, b: string, platform?: NodeJS.Platform): boolean;
25
+ /**
26
+ * `child` relative to `parent`, compared the way the file system compares
27
+ * names; `null` when `child` is not inside `parent` (or is `parent`).
28
+ * @param {string} parent
29
+ * @param {string} child
30
+ * @param {NodeJS.Platform} [platform]
31
+ * @returns {string | null}
32
+ */
33
+ export function relativeWithin(parent: string, child: string, platform?: NodeJS.Platform): string | null;
@@ -0,0 +1,73 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Path comparison that matches the file system (spec AST-081 FR11):
5
+ * Windows and macOS file systems ignore letter case by default, so two
6
+ * spellings that differ only in case name the same folder there, and the
7
+ * release choice must treat them as one.
8
+ *
9
+ * @input paths and the platform
10
+ * @output whether two paths are the same, and a path relative to a folder
11
+ * @position packages/cli/foundation/fs — used by the release choice and the
12
+ * release cache.
13
+ */
14
+
15
+ import * as path from 'node:path';
16
+
17
+ /**
18
+ * Whether the platform's file systems ignore letter case by default.
19
+ * @param {NodeJS.Platform} [platform]
20
+ * @returns {boolean}
21
+ */
22
+ export function ignoresCase(platform = process.platform) {
23
+ return platform === 'win32' || platform === 'darwin';
24
+ }
25
+
26
+ /**
27
+ * The path module of a platform.
28
+ * @param {NodeJS.Platform} platform
29
+ * @returns {typeof path.posix}
30
+ */
31
+ function pathsOf(platform) {
32
+ return platform === 'win32' ? path.win32 : path.posix;
33
+ }
34
+
35
+ /**
36
+ * A path in the form two spellings of one folder share.
37
+ * @param {string} p
38
+ * @param {NodeJS.Platform} [platform]
39
+ * @returns {string}
40
+ */
41
+ export function pathKey(p, platform = process.platform) {
42
+ const normal = pathsOf(platform).normalize(p);
43
+ return ignoresCase(platform) ? normal.toLowerCase() : normal;
44
+ }
45
+
46
+ /**
47
+ * Whether two paths name the same folder or file.
48
+ * @param {string} a
49
+ * @param {string} b
50
+ * @param {NodeJS.Platform} [platform]
51
+ * @returns {boolean}
52
+ */
53
+ export function samePath(a, b, platform = process.platform) {
54
+ return pathKey(a, platform) === pathKey(b, platform);
55
+ }
56
+
57
+ /**
58
+ * `child` relative to `parent`, compared the way the file system compares
59
+ * names; `null` when `child` is not inside `parent` (or is `parent`).
60
+ * @param {string} parent
61
+ * @param {string} child
62
+ * @param {NodeJS.Platform} [platform]
63
+ * @returns {string | null}
64
+ */
65
+ export function relativeWithin(parent, child, platform = process.platform) {
66
+ const paths = pathsOf(platform);
67
+ const rel = paths.relative(
68
+ pathKey(parent, platform),
69
+ pathKey(child, platform),
70
+ );
71
+ if (rel === '' || rel.startsWith('..') || paths.isAbsolute(rel)) return null;
72
+ return rel;
73
+ }