@astryxdesign/cli 0.6.3 → 0.6.4-canary.06c8fa3

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 (670) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +121 -85
  3. package/api/blog/blog.doc.mjs +1 -0
  4. package/api/build/_adapter.d.mts +50 -0
  5. package/api/build/_adapter.mjs +60 -0
  6. package/api/build/build.doc.mjs +16 -9
  7. package/api/build/build.test.mjs +197 -8
  8. package/api/build/build.type.d.mts +91 -2
  9. package/api/build/build.type.mjs +52 -8
  10. package/api/build/help/help.d.mts +12 -5
  11. package/api/build/help/help.mjs +69 -6
  12. package/api/build/kit/kit.d.mts +4 -1
  13. package/api/build/kit/kit.mjs +165 -49
  14. package/api/build/kit/rank.d.mts +44 -0
  15. package/api/build/kit/rank.mjs +432 -0
  16. package/api/build/kit/rank.test.mjs +196 -0
  17. package/api/component/_adapter.d.mts +31 -12
  18. package/api/component/_adapter.mjs +79 -15
  19. package/api/component/component.d.mts +6 -3
  20. package/api/component/component.doc.mjs +36 -13
  21. package/api/component/component.mjs +339 -22
  22. package/api/component/component.test.mjs +38 -0
  23. package/api/component/component.type.d.mts +47 -11
  24. package/api/component/component.type.mjs +76 -24
  25. package/api/component/detail/blocks/blocks.d.mts +2 -1
  26. package/api/component/detail/blocks/blocks.mjs +4 -3
  27. package/api/component/list/list.d.mts +0 -5
  28. package/api/component/list/list.mjs +40 -11
  29. package/api/discover/_adapter.d.mts +114 -6
  30. package/api/discover/_adapter.mjs +372 -17
  31. package/api/discover/_adapter.test.mjs +215 -0
  32. package/api/discover/_catalog-view.d.mts +115 -0
  33. package/api/discover/_catalog-view.mjs +203 -0
  34. package/api/discover/_catalog-view.test.mjs +128 -0
  35. package/api/discover/detail/detail.d.mts +18 -6
  36. package/api/discover/detail/detail.mjs +67 -13
  37. package/api/discover/detail/detail.test.mjs +85 -0
  38. package/api/discover/detail/item/item.d.mts +26 -0
  39. package/api/discover/detail/item/item.mjs +78 -0
  40. package/api/discover/detail/item/item.test.mjs +73 -0
  41. package/api/discover/discover.d.mts +3 -9
  42. package/api/discover/discover.doc.mjs +62 -18
  43. package/api/discover/discover.mjs +220 -36
  44. package/api/discover/discover.test.mjs +11 -2
  45. package/api/discover/discover.type.d.mts +150 -11
  46. package/api/discover/discover.type.mjs +107 -17
  47. package/api/discover/list/list.d.mts +20 -6
  48. package/api/discover/list/list.mjs +45 -12
  49. package/api/discover/list/list.test.mjs +46 -0
  50. package/api/discover/search/search.d.mts +18 -16
  51. package/api/discover/search/search.mjs +102 -56
  52. package/api/discover/search/search.test.mjs +144 -10
  53. package/api/docs/_adapter.d.mts +272 -41
  54. package/api/docs/_adapter.mjs +985 -108
  55. package/api/docs/compiled-topics.test.mjs +78 -0
  56. package/api/docs/detail/detail.mjs +22 -63
  57. package/api/docs/detail/section/section.d.mts +1 -1
  58. package/api/docs/detail/section/section.mjs +54 -19
  59. package/api/docs/detail/section/section.test.mjs +50 -0
  60. package/api/docs/docs.d.mts +10 -3
  61. package/api/docs/docs.doc.mjs +55 -16
  62. package/api/docs/docs.mjs +53 -10
  63. package/api/docs/docs.test.mjs +166 -4
  64. package/api/docs/docs.type.d.mts +221 -5
  65. package/api/docs/docs.type.mjs +153 -11
  66. package/api/docs/index/index.d.mts +18 -0
  67. package/api/docs/index/index.mjs +40 -0
  68. package/api/docs/index/index.test.mjs +62 -0
  69. package/api/docs/integration-tree.test.mjs +555 -0
  70. package/api/docs/integrationDocs.test.mjs +114 -8
  71. package/api/docs/list/list.mjs +28 -12
  72. package/api/docs/node/node.d.mts +43 -0
  73. package/api/docs/node/node.mjs +192 -0
  74. package/api/docs/reference-blocks.test.mjs +406 -0
  75. package/api/doctor/doctor.d.mts +104 -1
  76. package/api/doctor/doctor.doc.mjs +1 -0
  77. package/api/doctor/doctor.mjs +635 -7
  78. package/api/doctor/doctor.test.mjs +732 -11
  79. package/api/gap-report/gap-report.doc.mjs +8 -4
  80. package/api/hook/_adapter.mjs +19 -5
  81. package/api/hook/hook.doc.mjs +1 -0
  82. package/api/hook/hook.type.d.mts +3 -3
  83. package/api/hook/hook.type.mjs +11 -11
  84. package/api/hook/list/list.d.mts +2 -2
  85. package/api/hook/list/list.mjs +69 -17
  86. package/api/index.d.mts +2 -3
  87. package/api/index.mjs +5 -4
  88. package/api/init/init.doc.mjs +6 -1
  89. package/api/init/init.test.mjs +41 -1
  90. package/api/init/remove/remove.mjs +1 -1
  91. package/api/init/run/run.mjs +20 -10
  92. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  93. package/api/integration/add-contribution.d.mts +2 -1
  94. package/api/integration/add-contribution.mjs +130 -15
  95. package/api/integration/add-contribution.test.mjs +258 -7
  96. package/api/integration/add-helpers.d.mts +5 -2
  97. package/api/integration/add-helpers.mjs +36 -9
  98. package/api/integration/add-theme.mjs +34 -64
  99. package/api/integration/add-theme.test.mjs +105 -21
  100. package/api/integration/authoring-checks.mjs +138 -28
  101. package/api/integration/authoring-checks.test.mjs +179 -7
  102. package/api/integration/authoring-checks.type.mjs +6 -1
  103. package/api/integration/integration-authoring.type.d.mts +3 -1
  104. package/api/integration/integration-authoring.type.mjs +2 -0
  105. package/api/integration/integration-block-exports.test.mjs +10 -6
  106. package/api/integration/integrationAdd.doc.mjs +14 -4
  107. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  108. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  109. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  110. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  111. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  112. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  113. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  114. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  115. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  116. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  117. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  118. package/api/integration/pack-check.lifecycle-output.test.mjs +105 -0
  119. package/api/integration/pack-check.mjs +111 -10
  120. package/api/integration/pack-check.test.mjs +387 -47
  121. package/api/integration/pack-check.type.d.mts +26 -2
  122. package/api/integration/pack-check.type.mjs +14 -1
  123. package/api/integration/summarizeIssues.doc.mjs +1 -0
  124. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  126. package/api/integration/validate-integration.mjs +52 -102
  127. package/api/integration/validate-integration.test.mjs +179 -26
  128. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  129. package/api/integration/validateIntegration.doc.mjs +3 -2
  130. package/api/json/assertResponse.doc.mjs +1 -0
  131. package/api/json/envelope-types.test.mjs +76 -0
  132. package/api/json/index.ts +2 -1
  133. package/api/json/isError.doc.mjs +1 -0
  134. package/api/json/parseResponse.doc.mjs +3 -2
  135. package/api/search/search-return-type.test.mjs +54 -0
  136. package/api/search/search.d.mts +62 -11
  137. package/api/search/search.doc.mjs +8 -2
  138. package/api/search/search.mjs +471 -83
  139. package/api/search/search.test.mjs +142 -1
  140. package/api/search/search.type.d.mts +15 -3
  141. package/api/search/search.type.mjs +5 -2
  142. package/api/swizzle/copy/copy.mjs +28 -11
  143. package/api/swizzle/swizzle.doc.mjs +2 -1
  144. package/api/swizzle/swizzle.type.d.mts +2 -2
  145. package/api/swizzle/swizzle.type.mjs +2 -2
  146. package/api/template/copy/copy.mjs +17 -23
  147. package/api/template/copy/copy.test.mjs +17 -0
  148. package/api/template/list/list.mjs +1 -0
  149. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  150. package/api/template/template-integration.test.mjs +1008 -3
  151. package/api/template/template-suffix.test.mjs +41 -21
  152. package/api/template/template.d.mts +1 -1
  153. package/api/template/template.doc.mjs +30 -8
  154. package/api/template/template.mjs +46 -9
  155. package/api/template/template.type.d.mts +12 -14
  156. package/api/template/template.type.mjs +15 -14
  157. package/api/theme/_adapter.d.mts +2 -3
  158. package/api/theme/_adapter.mjs +4 -5
  159. package/api/theme/add/add.binary.test.mjs +84 -0
  160. package/api/theme/add/add.mjs +31 -22
  161. package/api/theme/add/add.rollback.test.mjs +158 -0
  162. package/api/theme/add/add.staging.test.mjs +83 -0
  163. package/api/theme/add/add.test.mjs +14 -1
  164. package/api/theme/build/build.family.test.mjs +7 -12
  165. package/api/theme/build/build.mjs +140 -59
  166. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  167. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  168. package/api/theme/build/build.rollback.test.mjs +148 -0
  169. package/api/theme/build/build.test.mjs +127 -0
  170. package/api/theme/build/font-warning.mjs +3 -3
  171. package/api/theme/build/font-warning.test.mjs +5 -2
  172. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  173. package/api/theme/integration-themes.test.mjs +39 -28
  174. package/api/theme/list/list.test.mjs +19 -20
  175. package/api/theme/listThemes.doc.mjs +6 -5
  176. package/api/theme/palette/generate/generate.mjs +8 -3
  177. package/api/theme/palette/generate/generate.test.mjs +96 -0
  178. package/api/theme/palette/generate/generator.d.mts +10 -13
  179. package/api/theme/palette/generate/generator.mjs +15 -4
  180. package/api/theme/palette/generate/generator.test.mjs +10 -0
  181. package/api/theme/template/template.mjs +11 -2
  182. package/api/theme/template/template.test.mjs +20 -0
  183. package/api/theme/theme.type.d.mts +170 -11
  184. package/api/theme/theme.type.mjs +94 -27
  185. package/api/theme/themeAdd.doc.mjs +4 -3
  186. package/api/theme/themeBuild.doc.mjs +8 -4
  187. package/api/theme/themeList.doc.mjs +6 -3
  188. package/api/theme/themeListAvailable.doc.mjs +5 -3
  189. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  190. package/api/theme/themeTargets.doc.mjs +1 -0
  191. package/api/theme/themeTemplate.doc.mjs +6 -2
  192. package/api/upgrade/_adapter.d.mts +32 -5
  193. package/api/upgrade/_adapter.mjs +139 -22
  194. package/api/upgrade/list/list.mjs +2 -1
  195. package/api/upgrade/list/list.test.mjs +73 -0
  196. package/api/upgrade/project-context.test.mjs +272 -0
  197. package/api/upgrade/provider-agreement.test.mjs +152 -0
  198. package/api/upgrade/run/files-changed.test.mjs +111 -0
  199. package/api/upgrade/run/run.mjs +359 -60
  200. package/api/upgrade/status/status.mjs +2 -2
  201. package/api/upgrade/upgrade.doc.mjs +12 -5
  202. package/api/upgrade/upgrade.type.d.mts +43 -5
  203. package/api/upgrade/upgrade.type.mjs +29 -13
  204. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  205. package/assets/codemods/__tests__/runner.test.mjs +332 -8
  206. package/assets/codemods/file-count.test.mjs +163 -0
  207. package/assets/codemods/integration-discovery.mjs +48 -4
  208. package/assets/codemods/integration-discovery.test.mjs +73 -0
  209. package/assets/codemods/integration-runner.mjs +59 -7
  210. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  211. package/assets/codemods/registry.mjs +1 -0
  212. package/assets/codemods/run-codemod.mjs +177 -34
  213. package/assets/codemods/runner.mjs +353 -104
  214. package/assets/codemods/term-log.mjs +32 -8
  215. package/assets/codemods/term-log.test.mjs +19 -1
  216. package/assets/codemods/transform-prop.mjs +109 -0
  217. package/assets/codemods/transform-prop.test.mjs +95 -0
  218. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  219. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  220. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  221. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  222. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  223. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  224. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  225. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  226. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  227. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  228. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  229. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  230. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  231. package/assets/docs/README.md +9 -0
  232. package/assets/docs/authoring.doc.mjs +14 -0
  233. package/assets/docs/getting-started.doc.mjs +2 -2
  234. package/assets/docs/internationalization.doc.mjs +7 -5
  235. package/assets/docs/layout.doc.dense.mjs +2 -2
  236. package/assets/docs/layout.doc.mjs +1 -1
  237. package/assets/docs/principles.doc.mjs +6 -6
  238. package/assets/docs/styling-libraries.doc.mjs +4 -4
  239. package/assets/docs/styling.doc.mjs +4 -4
  240. package/assets/docs/theme.doc.mjs +5 -5
  241. package/assets/docs/tokens.doc.mjs +1 -1
  242. package/assets/docs/tree/api.doc.mjs +30 -0
  243. package/assets/docs/tree/cli.doc.mjs +23 -0
  244. package/assets/docs/tree/commands.doc.mjs +25 -0
  245. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  246. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
  247. package/assets/docs/tree/integrations.test.mjs +62 -0
  248. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  249. package/assets/docs/working-with-ai.doc.mjs +4 -4
  250. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  251. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  252. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  253. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  254. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  255. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  256. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  262. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  263. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  264. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  265. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  266. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  267. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  268. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  269. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  270. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  271. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  272. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  273. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  274. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  275. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  276. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  277. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  278. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  279. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  280. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  281. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  282. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  283. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  284. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  285. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  286. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  287. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  288. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  289. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  290. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  291. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  292. package/authoring/_shared/contract.ts +22 -0
  293. package/authoring/codemod/codemod.doc.mjs +7 -2
  294. package/authoring/codemod/parse.d.mts +8 -8
  295. package/authoring/codemod/parse.mjs +8 -6
  296. package/authoring/codemod/type.ts +12 -0
  297. package/authoring/config/config.doc.mjs +11 -3
  298. package/authoring/config/debug-composition.test.mjs +92 -0
  299. package/authoring/config/parse.d.mts +15 -13
  300. package/authoring/config/parse.mjs +27 -8
  301. package/authoring/config/parse.test.mjs +8 -0
  302. package/authoring/config/type.ts +31 -8
  303. package/authoring/debug/debug.doc.d.mts +11 -0
  304. package/authoring/debug/debug.doc.mjs +182 -0
  305. package/authoring/debug/parse.d.mts +8 -8
  306. package/authoring/debug/parse.mjs +3 -3
  307. package/authoring/discover/discover.doc.d.mts +13 -0
  308. package/authoring/discover/discover.doc.mjs +138 -0
  309. package/authoring/discover/parse.d.mts +24 -0
  310. package/authoring/discover/parse.mjs +128 -0
  311. package/authoring/discover/parse.test.mjs +124 -0
  312. package/authoring/discover/type.ts +87 -0
  313. package/authoring/doctypes/_schema.d.mts +790 -23
  314. package/authoring/doctypes/_schema.mjs +543 -39
  315. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  316. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  317. package/authoring/doctypes/base/type.ts +41 -0
  318. package/authoring/doctypes/command/command.doc.mjs +5 -4
  319. package/authoring/doctypes/command/parse.d.mts +2 -2
  320. package/authoring/doctypes/command/parse.mjs +1 -1
  321. package/authoring/doctypes/command/type.ts +6 -5
  322. package/authoring/doctypes/component/component.doc.mjs +6 -3
  323. package/authoring/doctypes/component/parse.d.mts +2 -2
  324. package/authoring/doctypes/component/parse.mjs +1 -1
  325. package/authoring/doctypes/component/type.ts +6 -5
  326. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  327. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  328. package/authoring/doctypes/enum/parse.d.mts +2 -2
  329. package/authoring/doctypes/enum/parse.mjs +1 -1
  330. package/authoring/doctypes/enum/type.ts +4 -2
  331. package/authoring/doctypes/function/function.doc.mjs +7 -2
  332. package/authoring/doctypes/function/parse.d.mts +2 -2
  333. package/authoring/doctypes/function/parse.mjs +1 -1
  334. package/authoring/doctypes/function/type.ts +9 -4
  335. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  336. package/authoring/doctypes/hook/parse.d.mts +2 -2
  337. package/authoring/doctypes/hook/parse.mjs +1 -1
  338. package/authoring/doctypes/hook/type.ts +5 -4
  339. package/authoring/doctypes/legacy.d.mts +8 -6
  340. package/authoring/doctypes/legacy.mjs +5 -4
  341. package/authoring/doctypes/load-contract.test.mjs +233 -0
  342. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  343. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  344. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  345. package/authoring/doctypes/namespace/parse.mjs +25 -0
  346. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  347. package/authoring/doctypes/namespace/type.ts +74 -0
  348. package/authoring/doctypes/parse.d.mts +22 -18
  349. package/authoring/doctypes/parse.mjs +22 -11
  350. package/authoring/doctypes/parse.test.mjs +77 -3
  351. package/authoring/doctypes/reference/parse.d.mts +2 -2
  352. package/authoring/doctypes/reference/parse.mjs +8 -5
  353. package/authoring/doctypes/reference/reference.doc.mjs +48 -6
  354. package/authoring/doctypes/reference/type.ts +70 -7
  355. package/authoring/doctypes/schema/parse.d.mts +2 -2
  356. package/authoring/doctypes/schema/parse.mjs +1 -1
  357. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  358. package/authoring/doctypes/schema/type.ts +4 -4
  359. package/authoring/doctypes/template/parse.d.mts +94 -1
  360. package/authoring/doctypes/template/parse.mjs +40 -2
  361. package/authoring/doctypes/template/parse.test.mjs +26 -2
  362. package/authoring/doctypes/template/template.doc.mjs +13 -3
  363. package/authoring/doctypes/template/type.ts +13 -2
  364. package/authoring/doctypes/theme/parse.d.mts +35 -0
  365. package/authoring/doctypes/theme/parse.mjs +76 -0
  366. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  367. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  368. package/authoring/doctypes/theme/type.ts +42 -0
  369. package/authoring/doctypes/types.ts +12 -10
  370. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  371. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  372. package/authoring/gap-report/parse.d.mts +10 -10
  373. package/authoring/gap-report/parse.mjs +6 -6
  374. package/authoring/gap-report/type.ts +1 -1
  375. package/authoring/identity/identity.doc.d.mts +9 -0
  376. package/authoring/identity/identity.doc.mjs +61 -0
  377. package/authoring/identity/type.ts +132 -0
  378. package/authoring/index.d.mts +3 -0
  379. package/authoring/index.d.ts +62 -17
  380. package/authoring/index.mjs +4 -1
  381. package/authoring/integration/integration.doc.mjs +15 -8
  382. package/authoring/integration/parse.d.mts +2 -2
  383. package/authoring/integration/parse.mjs +1 -1
  384. package/authoring/integration/parse.test.mjs +10 -1
  385. package/authoring/integration/schema.d.mts +6 -4
  386. package/authoring/integration/schema.mjs +9 -3
  387. package/authoring/integration/type.ts +19 -8
  388. package/authoring/shadcn/receipt.d.mts +6 -6
  389. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  390. package/clients/cli/command-load-failure.test.mjs +83 -0
  391. package/clients/cli/command-result-coverage.test.mjs +7 -7
  392. package/clients/cli/commands/blog.doc.mjs +1 -1
  393. package/clients/cli/commands/blog.mjs +23 -8
  394. package/clients/cli/commands/blog.test.mjs +42 -1
  395. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  396. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  397. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  398. package/clients/cli/commands/build-theme.mjs +16 -50
  399. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  400. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  401. package/clients/cli/commands/build.doc.mjs +16 -8
  402. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  403. package/clients/cli/commands/build.mjs +137 -114
  404. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  405. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  406. package/clients/cli/commands/component/index.mjs +153 -61
  407. package/clients/cli/commands/component-batch.test.mjs +341 -0
  408. package/clients/cli/commands/component-ownership.test.mjs +92 -3
  409. package/clients/cli/commands/component-package.test.mjs +46 -0
  410. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  411. package/clients/cli/commands/component.doc.mjs +24 -7
  412. package/clients/cli/commands/component.test.mjs +19 -0
  413. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  414. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  415. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  416. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  417. package/clients/cli/commands/discover.doc.mjs +55 -9
  418. package/clients/cli/commands/discover.mjs +393 -118
  419. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  420. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  421. package/clients/cli/commands/docs.doc.mjs +28 -6
  422. package/clients/cli/commands/docs.mjs +240 -26
  423. package/clients/cli/commands/docs.test.mjs +222 -1
  424. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  425. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  426. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  427. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  428. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  429. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  430. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  431. package/clients/cli/commands/doctor.doc.mjs +1 -1
  432. package/clients/cli/commands/doctor.mjs +59 -32
  433. package/clients/cli/commands/doctor.test.mjs +42 -0
  434. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  435. package/clients/cli/commands/gap-report.test.mjs +72 -0
  436. package/clients/cli/commands/hook/index.mjs +7 -17
  437. package/clients/cli/commands/hook.doc.mjs +1 -1
  438. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  439. package/clients/cli/commands/init.doc.mjs +20 -9
  440. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  441. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  442. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  443. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  444. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  445. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  446. package/clients/cli/commands/integration.doc.mjs +1 -1
  447. package/clients/cli/commands/integration.mjs +1 -0
  448. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  449. package/clients/cli/commands/json-contract.test.mjs +33 -0
  450. package/clients/cli/commands/manifest.doc.mjs +1 -1
  451. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  452. package/clients/cli/commands/search.doc.mjs +7 -4
  453. package/clients/cli/commands/search.mjs +28 -9
  454. package/clients/cli/commands/search.test.mjs +75 -0
  455. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  456. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  457. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  458. package/clients/cli/commands/template.doc.mjs +52 -13
  459. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  460. package/clients/cli/commands/template.mjs +4 -91
  461. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  462. package/clients/cli/commands/text-json-parity.test.mjs +702 -0
  463. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  464. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  465. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  466. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  467. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  468. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  469. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  470. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  471. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  472. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  473. package/clients/cli/commands/theme.doc.mjs +1 -1
  474. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  475. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  476. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  477. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  478. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  479. package/clients/cli/commands/upgrade.mjs +29 -7
  480. package/clients/cli/formatters/index.mjs +164 -1
  481. package/clients/cli/formatters/index.test.mjs +97 -0
  482. package/clients/cli/index.mjs +21 -34
  483. package/clients/cli/latest-version-env.test.mjs +50 -0
  484. package/clients/cli/lib/cli-error.test.mjs +7 -0
  485. package/clients/cli/lib/component-format.mjs +9 -9
  486. package/clients/cli/lib/component-format.test.mjs +1 -1
  487. package/clients/cli/lib/define-command.mjs +32 -6
  488. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  489. package/clients/cli/lib/exit-codes.test.mjs +90 -0
  490. package/clients/cli/lib/hook-format.mjs +19 -10
  491. package/clients/cli/lib/json-shim.mjs +62 -16
  492. package/clients/cli/lib/json-shim.test.mjs +69 -0
  493. package/clients/cli/lib/manifest.d.ts +2 -0
  494. package/clients/cli/lib/manifest.mjs +39 -10
  495. package/clients/cli/lib/manifest.test.mjs +22 -2
  496. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  497. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  498. package/foundation/agent-docs/agent-docs.mjs +82 -12
  499. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  500. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  501. package/foundation/config/integration-debug.test.mjs +28 -3
  502. package/foundation/config/project-themes.test.mjs +11 -19
  503. package/foundation/config/project.d.mts +20 -11
  504. package/foundation/config/project.mjs +263 -91
  505. package/foundation/config/project.test.mjs +270 -21
  506. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  507. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  508. package/foundation/discovery/authoring-self-docs.test.mjs +170 -0
  509. package/foundation/discovery/authoring-surface.d.mts +74 -0
  510. package/foundation/discovery/authoring-surface.mjs +525 -0
  511. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  512. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  513. package/foundation/discovery/cli-self-docs.mjs +490 -0
  514. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  515. package/foundation/discovery/component-discovery.d.mts +39 -1
  516. package/foundation/discovery/component-discovery.mjs +50 -1
  517. package/foundation/discovery/component-loader.d.mts +35 -38
  518. package/foundation/discovery/component-loader.mjs +53 -222
  519. package/foundation/discovery/docs-discovery.d.mts +119 -11
  520. package/foundation/discovery/docs-discovery.mjs +423 -108
  521. package/foundation/discovery/docs-discovery.test.mjs +365 -20
  522. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  523. package/foundation/discovery/docs-output-budget.mjs +50 -0
  524. package/foundation/discovery/docs-section-key.d.mts +116 -0
  525. package/foundation/discovery/docs-section-key.mjs +322 -0
  526. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  527. package/foundation/discovery/template-adapter.d.mts +113 -11
  528. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  529. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  530. package/foundation/discovery/template-adapter.mjs +775 -84
  531. package/foundation/discovery/template-adapter.test.mjs +57 -0
  532. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  533. package/foundation/discovery/template-conflict-release.mjs +40 -0
  534. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  535. package/foundation/discovery/theme-discovery.d.mts +67 -7
  536. package/foundation/discovery/theme-discovery.mjs +916 -186
  537. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  538. package/foundation/discovery/theming-targets.test.mjs +4 -0
  539. package/foundation/doc-compiler/bundle.d.mts +47 -0
  540. package/foundation/doc-compiler/bundle.mjs +278 -0
  541. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  542. package/foundation/doc-compiler/compile.d.mts +343 -0
  543. package/foundation/doc-compiler/compile.mjs +558 -0
  544. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  545. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  546. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  547. package/foundation/doc-compiler/doc-loads.test.mjs +1630 -0
  548. package/foundation/doc-compiler/import.d.mts +24 -0
  549. package/foundation/doc-compiler/import.mjs +59 -0
  550. package/foundation/doc-compiler/inputs.d.mts +102 -0
  551. package/foundation/doc-compiler/inputs.mjs +291 -0
  552. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  553. package/foundation/doc-compiler/ir.d.mts +22 -0
  554. package/foundation/doc-compiler/ir.mjs +471 -0
  555. package/foundation/doc-compiler/lenses.d.mts +36 -0
  556. package/foundation/doc-compiler/lenses.mjs +173 -0
  557. package/foundation/doc-compiler/links.d.mts +162 -0
  558. package/foundation/doc-compiler/links.mjs +294 -0
  559. package/foundation/doc-compiler/links.test.mjs +192 -0
  560. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  561. package/foundation/doc-compiler/overlays.d.mts +37 -0
  562. package/foundation/doc-compiler/overlays.mjs +206 -0
  563. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  564. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  565. package/foundation/doc-compiler/read.d.mts +127 -0
  566. package/foundation/doc-compiler/read.mjs +325 -0
  567. package/foundation/doc-compiler/read.test.mjs +313 -0
  568. package/foundation/doc-compiler/source.d.mts +33 -0
  569. package/foundation/doc-compiler/source.mjs +128 -0
  570. package/foundation/doc-compiler/tree.d.mts +288 -0
  571. package/foundation/doc-compiler/tree.mjs +876 -0
  572. package/foundation/doc-compiler/tree.test.mjs +606 -0
  573. package/foundation/fs/file-protection.d.mts +33 -0
  574. package/foundation/fs/file-protection.mjs +825 -0
  575. package/foundation/fs/file-protection.test.mjs +250 -0
  576. package/foundation/fs/module-loader.d.mts +1 -0
  577. package/foundation/fs/module-loader.mjs +50 -1
  578. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  579. package/foundation/fs/path-safety.d.mts +3 -2
  580. package/foundation/fs/path-safety.mjs +49 -19
  581. package/foundation/fs/path-safety.test.mjs +50 -0
  582. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  583. package/foundation/identity/provider-identity.d.mts +90 -0
  584. package/foundation/identity/provider-identity.mjs +320 -0
  585. package/foundation/identity/provider-identity.test.mjs +254 -0
  586. package/foundation/identity/providers.d.mts +7 -0
  587. package/foundation/identity/providers.mjs +16 -0
  588. package/foundation/integrations/autolink.d.mts +58 -1
  589. package/foundation/integrations/autolink.mjs +143 -45
  590. package/foundation/integrations/autolink.test.mjs +1 -1
  591. package/foundation/integrations/cli-requirement.d.mts +45 -0
  592. package/foundation/integrations/cli-requirement.mjs +154 -0
  593. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  594. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  595. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  596. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  597. package/foundation/integrations/contribution-inventory.mjs +27 -24
  598. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  599. package/foundation/integrations/integration-warnings.d.mts +9 -2
  600. package/foundation/integrations/integration-warnings.mjs +52 -21
  601. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  602. package/foundation/integrations/integrations.d.mts +63 -3
  603. package/foundation/integrations/integrations.mjs +122 -9
  604. package/foundation/integrations/integrations.test.mjs +415 -1
  605. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  606. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  607. package/foundation/integrations/provider-resolution.d.mts +152 -0
  608. package/foundation/integrations/provider-resolution.mjs +576 -0
  609. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  610. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  611. package/foundation/integrations/theme-descriptor.mjs +44 -0
  612. package/foundation/integrations/validate-contributions.d.mts +2 -0
  613. package/foundation/integrations/validate-contributions.mjs +131 -29
  614. package/foundation/response/base.d.ts +8 -4
  615. package/foundation/response/batch.type.d.mts +33 -0
  616. package/foundation/response/batch.type.mjs +34 -0
  617. package/foundation/response/error-codes.d.mts +3 -1
  618. package/foundation/response/error-codes.d.ts +2 -0
  619. package/foundation/response/error-codes.doc.mjs +13 -4
  620. package/foundation/response/error-codes.mjs +8 -2
  621. package/foundation/response/error-codes.test.mjs +137 -10
  622. package/foundation/response/json-contract.test.mjs +57 -17
  623. package/foundation/response/json.d.mts +4 -2
  624. package/foundation/response/json.mjs +8 -10
  625. package/foundation/response/response-types.doc.d.mts +5 -1
  626. package/foundation/response/response-types.doc.mjs +46 -38
  627. package/foundation/response/response-types.doc.test.mjs +181 -0
  628. package/foundation/response/response.doc.mjs +1 -1
  629. package/foundation/text/string-utils.d.mts +8 -0
  630. package/foundation/text/string-utils.mjs +40 -10
  631. package/foundation/xle/browser.d.mts +3 -3
  632. package/foundation/xle/browser.mjs +3 -3
  633. package/foundation/xle/expand.d.mts +2 -0
  634. package/foundation/xle/expand.mjs +6 -5
  635. package/foundation/xle/expand.test.mjs +54 -0
  636. package/foundation/xle/parse.mjs +1 -1
  637. package/foundation/xle/print.mjs +2 -2
  638. package/foundation/xle/splice.mjs +1 -1
  639. package/foundation/xle/xle.test.mjs +13 -0
  640. package/package.json +10 -11
  641. package/api/layout/_adapter.d.mts +0 -34
  642. package/api/layout/_adapter.mjs +0 -133
  643. package/api/layout/check/check.d.mts +0 -16
  644. package/api/layout/check/check.mjs +0 -40
  645. package/api/layout/expand/expand.d.mts +0 -22
  646. package/api/layout/expand/expand.mjs +0 -153
  647. package/api/layout/grammar/grammar.d.mts +0 -13
  648. package/api/layout/grammar/grammar.mjs +0 -86
  649. package/api/layout/layout.d.mts +0 -6
  650. package/api/layout/layout.mjs +0 -17
  651. package/api/layout/layout.test.mjs +0 -297
  652. package/api/layout/layout.type.d.mts +0 -89
  653. package/api/layout/layout.type.mjs +0 -103
  654. package/api/layout/layoutCheck.doc.d.mts +0 -11
  655. package/api/layout/layoutCheck.doc.mjs +0 -84
  656. package/api/layout/layoutExpand.doc.d.mts +0 -11
  657. package/api/layout/layoutExpand.doc.mjs +0 -106
  658. package/api/layout/layoutGrammar.doc.d.mts +0 -11
  659. package/api/layout/layoutGrammar.doc.mjs +0 -56
  660. package/assets/templates/themes/manifest.json +0 -95
  661. package/clients/cli/commands/layout-check.doc.mjs +0 -54
  662. package/clients/cli/commands/layout-expand.doc.mjs +0 -66
  663. package/clients/cli/commands/layout-grammar.doc.mjs +0 -30
  664. package/clients/cli/commands/layout.doc.mjs +0 -34
  665. package/clients/cli/commands/layout.error-codes.test.mjs +0 -66
  666. package/clients/cli/commands/layout.exit-parity.test.mjs +0 -41
  667. package/clients/cli/commands/layout.mjs +0 -263
  668. package/clients/cli/lib/update-check.mjs +0 -83
  669. package/clients/cli/lib/update-check.test.mjs +0 -137
  670. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -12,9 +12,10 @@
12
12
  * - A .d.ts (plus an optional .variants.d.ts for custom prop values)
13
13
  *
14
14
  * It performs the writes and returns a `theme.build` receipt — its `warnings`
15
- * carry override problems and any fonts the theme names but does not load
16
- * (font-warning.mjs) — or `null` when the theme produced no CSS (nothing to
17
- * build). Errors throw AstryxError (with
15
+ * carry override problems, every declaration core's generator dropped because
16
+ * its value could not stay one CSS declaration, and any fonts the theme names
17
+ * but does not load (font-warning.mjs) — or `null` when the theme produced no
18
+ * CSS (nothing to build). Errors throw AstryxError (with
18
19
  * a stable code). Human progress is emitted through the shared `logger`
19
20
  * (silent by default), so the CLI keeps its exact output while a programmatic
20
21
  * caller stays quiet.
@@ -52,6 +53,7 @@ import {
52
53
  } from '../../../foundation/fs/path-safety.mjs';
53
54
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
54
55
  import {AstryxError} from '../../error.mjs';
56
+ import {applyWrites} from '../../integration/add-helpers.mjs';
55
57
  import {logger} from '../../logger.mjs';
56
58
  import {loadComponentDoc} from '../../../foundation/discovery/component-loader.mjs';
57
59
  import {
@@ -223,26 +225,15 @@ function staleBuildOutputs(writes, cwd) {
223
225
  /** @param {Array<{dest: string, content: string}>} writes */
224
226
  function writeBuildOutputs(writes) {
225
227
  if (writes.length === 0) return;
226
- fs.mkdirSync(path.dirname(writes[0].dest), {recursive: true});
227
- /** @type {Array<{tmp: string, dest: string}>} */
228
- const staged = [];
229
228
  try {
230
- for (const write of writes) {
231
- const tmp = `${write.dest}.${process.pid}.tmp`;
232
- fs.writeFileSync(tmp, write.content);
233
- staged.push({tmp, dest: write.dest});
234
- }
235
- for (const stagedWrite of staged) {
236
- fs.renameSync(stagedWrite.tmp, stagedWrite.dest);
237
- }
229
+ applyWrites(
230
+ writes.map(write => ({
231
+ path: write.dest,
232
+ contents: write.content,
233
+ createOnly: false,
234
+ })),
235
+ );
238
236
  } catch (error) {
239
- for (const stagedWrite of staged) {
240
- try {
241
- fs.rmSync(stagedWrite.tmp, {force: true});
242
- } catch {
243
- // Best effort: the command still fails and never reports success.
244
- }
245
- }
246
237
  const message = `Failed to write theme outputs: ${/** @type {Error} */ (error).message}`;
247
238
  throw new AstryxError(message, undefined, ERROR_CODES.ERR_WRITE_FAILED);
248
239
  }
@@ -1870,7 +1861,7 @@ function validatePrivateVars(themeDef) {
1870
1861
  if (prop.startsWith('--_')) {
1871
1862
  errors.push(
1872
1863
  `Component "${component}" (${[key, ...path].join(' ')}) sets private var "${prop}". ` +
1873
- `Private vars (--_*) are internal — use standard CSS properties ` +
1864
+ `Private vars (--_*) are internal; use standard CSS properties ` +
1874
1865
  `(e.g. borderRadius, padding) instead. The pipeline expands them automatically.`,
1875
1866
  );
1876
1867
  }
@@ -1930,20 +1921,7 @@ function getThemeComponentLayers(themeDef) {
1930
1921
  */
1931
1922
  function validateCustomHeadingTypes(themeDef) {
1932
1923
  const layers = getThemeComponentLayers(themeDef);
1933
- const customTypes = new Set();
1934
- for (const {components} of layers) {
1935
- const headingRules = components.heading;
1936
- if (!headingRules || typeof headingRules !== 'object') continue;
1937
-
1938
- for (const key of Object.keys(headingRules)) {
1939
- for (const pair of key.split('+')) {
1940
- const colon = pair.indexOf(':');
1941
- if (colon === -1 || pair.slice(0, colon) !== 'type') continue;
1942
- const value = pair.slice(colon + 1);
1943
- if (value && !BUILTIN_HEADING_TYPES.has(value)) customTypes.add(value);
1944
- }
1945
- }
1946
- }
1924
+ const customTypes = customHeadingTypes(themeDef);
1947
1925
 
1948
1926
  const errors = [];
1949
1927
  for (const type of customTypes) {
@@ -1970,10 +1948,65 @@ function validateCustomHeadingTypes(themeDef) {
1970
1948
  return errors;
1971
1949
  }
1972
1950
 
1951
+ /**
1952
+ * Custom Heading type names declared on the root or media-surface component
1953
+ * maps: the names the generated `HeadingTypeMap` augmentation enrolls.
1954
+ *
1955
+ * @param {{components?: Record<string, Record<string, unknown>>}} themeDef
1956
+ * @returns {Set<string>}
1957
+ */
1958
+ function customHeadingTypes(themeDef) {
1959
+ /** @type {Set<string>} */
1960
+ const customTypes = new Set();
1961
+ for (const {components} of getThemeComponentLayers(themeDef)) {
1962
+ const headingRules = components.heading;
1963
+ if (!headingRules || typeof headingRules !== 'object') continue;
1964
+
1965
+ for (const key of Object.keys(headingRules)) {
1966
+ for (const pair of key.split('+')) {
1967
+ const colon = pair.indexOf(':');
1968
+ if (colon === -1 || pair.slice(0, colon) !== 'type') continue;
1969
+ const value = pair.slice(colon + 1);
1970
+ if (value && !BUILTIN_HEADING_TYPES.has(value)) customTypes.add(value);
1971
+ }
1972
+ }
1973
+ }
1974
+ return customTypes;
1975
+ }
1976
+
1977
+ /**
1978
+ * Custom Heading types with no standalone rule in the generated CSS. A type
1979
+ * whose every declaration the compiler dropped still passes the source check,
1980
+ * and its augmentation would then name a type that has no styles.
1981
+ *
1982
+ * @param {Set<string>} types
1983
+ * @param {string} css - Generated root component and media-surface rules.
1984
+ * @returns {string[]}
1985
+ */
1986
+ function headingTypesWithoutEmittedRule(types, css) {
1987
+ /** @type {string[]} */
1988
+ const missing = [];
1989
+ for (const type of types) {
1990
+ const selector = `.astryx-heading[data-type="${type}"]`;
1991
+ let emitted = false;
1992
+ for (
1993
+ let at = css.indexOf(selector);
1994
+ at !== -1 && !emitted;
1995
+ at = css.indexOf(selector, at + 1)
1996
+ ) {
1997
+ // A following attribute is a combined selector, not the standalone rule.
1998
+ emitted = css[at + selector.length] !== '[';
1999
+ }
2000
+ if (!emitted) missing.push(type);
2001
+ }
2002
+ return missing;
2003
+ }
2004
+
1973
2005
  /**
1974
2006
  * Return true when a style object contains a declaration that the theme
1975
- * generator can emit. Empty pseudo blocks do not count: they produce no CSS
1976
- * and would leave the generated type augmentation without a usable rule.
2007
+ * generator can emit. Empty pseudo blocks and blank values do not count: they
2008
+ * produce no usable CSS and would leave the generated type augmentation
2009
+ * without a usable rule.
1977
2010
  *
1978
2011
  * @param {unknown} styles
1979
2012
  * @returns {boolean}
@@ -1987,7 +2020,12 @@ function hasUsableStyleDeclaration(styles) {
1987
2020
  if (hasUsableStyleDeclaration(value)) return true;
1988
2021
  continue;
1989
2022
  }
1990
- if (value !== undefined && value !== null && typeof value !== 'object') {
2023
+ if (
2024
+ value !== undefined &&
2025
+ value !== null &&
2026
+ typeof value !== 'object' &&
2027
+ String(value).trim() !== ''
2028
+ ) {
1991
2029
  return true;
1992
2030
  }
1993
2031
  }
@@ -2162,14 +2200,14 @@ async function themeBuildInternal(
2162
2200
  const noticeMessages = [];
2163
2201
  for (const w of warnings) {
2164
2202
  warningMessages.push(w);
2165
- logger.warn(` ⚠ ${w}`);
2203
+ logger.warn(` [warn] ${w}`);
2166
2204
  }
2167
2205
 
2168
2206
  // Validate no private vars are set directly
2169
2207
  const privateVarErrors = validatePrivateVars(themeDef);
2170
2208
  for (const e of privateVarErrors) {
2171
2209
  warningMessages.push(e);
2172
- logger.error(` ✗ ${e}`);
2210
+ logger.error(` [error] ${e}`);
2173
2211
  }
2174
2212
  if (privateVarErrors.length > 0) {
2175
2213
  logger.error(
@@ -2195,7 +2233,7 @@ async function themeBuildInternal(
2195
2233
  // capability exports are checked against what the theme actually asks for.
2196
2234
  if (!_defineTheme || !_generateThemeRulesSplit) {
2197
2235
  throw new AstryxError(
2198
- 'Could not load @astryxdesign/core/theme — `astryx theme build` requires a ' +
2236
+ 'Could not load @astryxdesign/core/theme: `astryx theme build` requires a ' +
2199
2237
  'built, resolvable @astryxdesign/core so it emits the same CSS as the ' +
2200
2238
  'runtime <Theme>. Build @astryxdesign/core first (e.g. `pnpm -F @astryxdesign/core ' +
2201
2239
  'build`)' +
@@ -2303,7 +2341,15 @@ async function themeBuildInternal(
2303
2341
  const scopeSelector = themeScopeStart(themeDef.name);
2304
2342
  const scopeTo = THEME_SCOPE_TO;
2305
2343
 
2306
- const {component, prose} = _generateThemeRulesSplit(resolvedTheme);
2344
+ // Older cores ignore this optional collector. Current core writes the same
2345
+ // warning text here that runtime callers receive on the console.
2346
+ /** @type {string[]} */
2347
+ const droppedDeclarations = [];
2348
+
2349
+ const {component, prose} = _generateThemeRulesSplit(
2350
+ resolvedTheme,
2351
+ droppedDeclarations,
2352
+ );
2307
2353
  const cssParts = [];
2308
2354
  // Prose element defaults always ship — the `<Theme>` runtime
2309
2355
  // (generateThemeCSS) always emits them, so the build must too, or the
@@ -2322,7 +2368,7 @@ async function themeBuildInternal(
2322
2368
  let adaptationCss;
2323
2369
  try {
2324
2370
  adaptationCss = _generateAdaptationCSS
2325
- ? _generateAdaptationCSS(resolvedTheme)
2371
+ ? _generateAdaptationCSS(resolvedTheme, droppedDeclarations)
2326
2372
  : {component: '', prose: ''};
2327
2373
  } catch (error) {
2328
2374
  const message =
@@ -2368,13 +2414,37 @@ async function themeBuildInternal(
2368
2414
  // adaptations on the same resolved leaf.
2369
2415
  let onMediaCss = '';
2370
2416
  if (_generateOnMediaCSS) {
2371
- onMediaCss = _generateOnMediaCSS(resolvedTheme);
2417
+ onMediaCss = _generateOnMediaCSS(resolvedTheme, droppedDeclarations);
2372
2418
  if (onMediaCss) {
2373
2419
  cssParts.push(`@layer astryx-theme {\n${onMediaCss}\n}`);
2374
2420
  }
2375
2421
  }
2422
+ for (const message of droppedDeclarations) {
2423
+ const w = `Declaration ${message}. The generated CSS omits it; fix the value in the theme source.`;
2424
+ warningMessages.push(w);
2425
+ logger.warn(` [warn] ${w}`);
2426
+ }
2427
+ const unemittedHeadingTypes = headingTypesWithoutEmittedRule(
2428
+ customHeadingTypes(themeDef),
2429
+ `${component.join('\n')}\n${onMediaCss}`,
2430
+ );
2431
+ if (unemittedHeadingTypes.length > 0) {
2432
+ throw new AstryxError(
2433
+ unemittedHeadingTypes
2434
+ .map(
2435
+ type =>
2436
+ `Custom Heading type "${type}" has no declaration the theme ` +
2437
+ `compiler could emit: every declaration in its standalone ` +
2438
+ `"type:${type}" rule was dropped. Give that rule at least one ` +
2439
+ 'valid declaration.',
2440
+ )
2441
+ .join('\n'),
2442
+ undefined,
2443
+ ERROR_CODES.ERR_THEME_INVALID,
2444
+ );
2445
+ }
2376
2446
  if (cssParts.length === 0) {
2377
- logger.log('No overrides found — nothing to build.');
2447
+ logger.log('No overrides found; nothing to build.');
2378
2448
  return null;
2379
2449
  }
2380
2450
  // The data-token defaults are theme-independent and go in @layer
@@ -2425,7 +2495,18 @@ async function themeBuildInternal(
2425
2495
  // Guard: relative paths must not escape cwd via `../`. Absolute paths are
2426
2496
  // trusted (the user explicitly controls where output goes, like gcc -o).
2427
2497
  if (!path.isAbsolute(options.out)) {
2428
- assertWithin(options.out, cwd, {label: 'output path'});
2498
+ try {
2499
+ assertWithin(options.out, cwd, {label: 'output path'});
2500
+ } catch (err) {
2501
+ if (err instanceof PathSafetyError) {
2502
+ throw new AstryxError(
2503
+ err.message,
2504
+ undefined,
2505
+ ERROR_CODES.ERR_PATH_TRAVERSAL,
2506
+ );
2507
+ }
2508
+ throw err;
2509
+ }
2429
2510
  }
2430
2511
  } else {
2431
2512
  outPath = path.join(path.dirname(filePath), `${baseName}.css`);
@@ -2534,10 +2615,10 @@ async function themeBuildInternal(
2534
2615
  const stale = staleBuildOutputs(writes, cwd);
2535
2616
  const upToDate = stale.length === 0;
2536
2617
  if (upToDate) {
2537
- logger.log(`\n✓ Theme outputs are up to date with ${sourceRelative}.`);
2618
+ logger.log(`\n[ok] Theme outputs are up to date with ${sourceRelative}.`);
2538
2619
  } else {
2539
2620
  logger.error(
2540
- `\n✗ ${stale.length} theme output(s) are out of date with ${sourceRelative}:`,
2621
+ `\n[fail] ${stale.length} theme output(s) are out of date with ${sourceRelative}:`,
2541
2622
  );
2542
2623
  for (const s of stale) {
2543
2624
  logger.error(
@@ -2559,17 +2640,17 @@ async function themeBuildInternal(
2559
2640
 
2560
2641
  writeBuildOutputs(writes);
2561
2642
 
2562
- logger.log(`\n✓ ${path.relative(cwd, outPath)}`);
2643
+ logger.log(`\n[ok] ${path.relative(cwd, outPath)}`);
2563
2644
  logger.log(
2564
2645
  ` ${tokenCount} token overrides, ${componentCount} component overrides`,
2565
2646
  );
2566
2647
  logger.log(` ${size} KB`);
2567
- logger.log(`✓ ${path.relative(cwd, jsPath)}`);
2568
- logger.log(`✓ ${path.relative(cwd, dtsPath)}`);
2648
+ logger.log(`[ok] ${path.relative(cwd, jsPath)}`);
2649
+ logger.log(`[ok] ${path.relative(cwd, dtsPath)}`);
2569
2650
  if (variantDtsPath && variantDecl) {
2570
2651
  const augCount = (variantDecl.match(/': true;/g) || []).length;
2571
2652
  logger.log(
2572
- `✓ ${path.relative(cwd, variantDtsPath)} (${augCount} type augmentations)`,
2653
+ `[ok] ${path.relative(cwd, variantDtsPath)} (${augCount} type augmentations)`,
2573
2654
  );
2574
2655
  }
2575
2656
 
@@ -2579,7 +2660,7 @@ async function themeBuildInternal(
2579
2660
  const cssImport = importSpecifier(relOutDir, cssBase) + '.css';
2580
2661
  const exportName = `${toIdentifier(baseName)}Theme`;
2581
2662
  logger.log(`
2582
- Install in your app (paths are relative to a file in src/ — adjust if yours lives elsewhere):
2663
+ Install in your app (paths are relative to a file in src/; adjust if yours lives elsewhere):
2583
2664
 
2584
2665
  import { ${exportName} } from '${jsImport}';
2585
2666
  import '${cssImport}';
@@ -2611,7 +2692,7 @@ Or with a <link> tag:
2611
2692
  // Adaptation rules are resolved theme writes in their own right, so a family
2612
2693
  // named only inside one needs the same notice as one named at the root.
2613
2694
  for (const family of unloadedFonts) {
2614
- const msg = `Font "${family}" is named by this theme but not loaded — add a <link> or @font-face in your app (recipe: astryx docs typography)`;
2695
+ const msg = `Font "${family}" is named by this theme but not loaded; add a <link> or @font-face in your app (recipe: astryx docs typography)`;
2615
2696
  noticeMessages.push(msg);
2616
2697
  logger.log(` note: ${msg}`);
2617
2698
  }
@@ -2905,11 +2986,11 @@ export async function themeBuildFamily(
2905
2986
  const upToDate = stale.length === 0;
2906
2987
  if (upToDate) {
2907
2988
  logger.log(
2908
- `\n✓ Theme family outputs are up to date with ${sourceRelative}.`,
2989
+ `\n[ok] Theme family outputs are up to date with ${sourceRelative}.`,
2909
2990
  );
2910
2991
  } else {
2911
2992
  logger.error(
2912
- `\n✗ ${stale.length} theme family output(s) are out of date:`,
2993
+ `\n[fail] ${stale.length} theme family output(s) are out of date:`,
2913
2994
  );
2914
2995
  for (const entry of stale) {
2915
2996
  logger.error(
@@ -2929,7 +3010,7 @@ export async function themeBuildFamily(
2929
3010
  };
2930
3011
  }
2931
3012
  writeBuildOutputs(writes);
2932
- for (const output of Object.values(outputs)) logger.log(`✓ ${output}`);
3013
+ for (const output of Object.values(outputs)) logger.log(`[ok] ${output}`);
2933
3014
  const themeBindings = members.map(member => bindings.get(member.theme.name));
2934
3015
  const relOutDir = path.relative(cwd, outDir) || '.';
2935
3016
  const jsImport = importSpecifier(relOutDir, baseName);
@@ -2947,7 +3028,7 @@ Or load the same stylesheet natively:
2947
3028
  ];
2948
3029
  const notices = unloadedFonts.map(
2949
3030
  family =>
2950
- `Font "${family}" is named by this theme family but not loaded — add a <link> or @font-face in your app (recipe: astryx docs typography)`,
3031
+ `Font "${family}" is named by this theme family but not loaded; add a <link> or @font-face in your app (recipe: astryx docs typography)`,
2951
3032
  );
2952
3033
  for (const notice of notices) logger.log(` note: ${notice}`);
2953
3034
  return {
@@ -19,7 +19,7 @@
19
19
  * next one is covered without touching this file.
20
20
  *
21
21
  * Asserted on the receipt's `warnings` rather than on a rejection: a private
22
- * var is reported (logged `✗`, collected into the receipt) and the build then
22
+ * var is reported (logged `[error]`, collected into the receipt) and the build then
23
23
  * emits its CSS and resolves anyway. Asserting a throw would pass for the
24
24
  * wrong reason — it never throws, which is why a throwaway build read as a
25
25
  * pass on the first version of #5214.
@@ -0,0 +1,111 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * Every field `themeBuild()` returns is named in its FunctionDoc and in the
5
+ * response-types doc.
6
+ *
7
+ * The receipt is a stable machine-readable response, so each field needs
8
+ * consumer documentation, not only a typedef. The fields are read off a real
9
+ * build, so a field added to the receipt without documentation fails here.
10
+ */
11
+
12
+ import {describe, it, expect, beforeAll, afterEach} from 'vitest';
13
+ import * as fs from 'node:fs';
14
+ import * as os from 'node:os';
15
+ import * as path from 'node:path';
16
+ import {themeBuild} from './build.mjs';
17
+ import {doc} from '../themeBuild.doc.mjs';
18
+ import {doc as responseTypes} from '../../../foundation/response/response-types.doc.mjs';
19
+ import {ensureCoreBuilt} from '../../../clients/cli/commands/ensure-core-built.mjs';
20
+
21
+ /** @type {string[]} */
22
+ const dirs = [];
23
+
24
+ beforeAll(() => {
25
+ ensureCoreBuilt();
26
+ }, 200_000);
27
+
28
+ afterEach(() => {
29
+ for (const dir of dirs.splice(0)) {
30
+ fs.rmSync(dir, {recursive: true, force: true});
31
+ }
32
+ });
33
+
34
+ /** @returns {string} a temp dir holding a theme that exercises every field */
35
+ function themeDir() {
36
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-receipt-doc-'));
37
+ dirs.push(dir);
38
+ fs.writeFileSync(
39
+ path.join(dir, 'ocean.mjs'),
40
+ `export default {
41
+ name: 'ocean',
42
+ tokens: {'--color-background-body': '#ffffff', '--font-family-body': '"Inter", sans-serif'},
43
+ components: {progressbar: {base: {color: 'red'}}},
44
+ };
45
+ `,
46
+ );
47
+ return dir;
48
+ }
49
+
50
+ /** @param {string} type */
51
+ function returnsDescription(type) {
52
+ const entry = doc.returns?.find(r => r.type === type);
53
+ expect(entry, `themeBuild doc has no ${type} return`).toBeDefined();
54
+ return /** @type {{description: string}} */ (entry).description;
55
+ }
56
+
57
+ /** @param {string} description @param {string[]} fields */
58
+ function undocumented(description, fields) {
59
+ return fields.filter(
60
+ field => !new RegExp(String.raw`\b${field}\b`).test(description),
61
+ );
62
+ }
63
+
64
+ describe('themeBuild() consumer docs name every receipt field', () => {
65
+ it('theme.build', async () => {
66
+ const dir = themeDir();
67
+ const result = await themeBuild('ocean.mjs', {}, {cwd: dir});
68
+
69
+ expect(result?.type).toBe('theme.build');
70
+ const data = /** @type {Record<string, any>} */ (result?.data);
71
+ // The theme is chosen so the optional arrays are non-empty.
72
+ expect(data.warnings.length).toBeGreaterThan(0);
73
+ expect(data.notices.length).toBeGreaterThan(0);
74
+
75
+ const fields = [
76
+ ...Object.keys(data),
77
+ ...Object.keys(data.outputs),
78
+ 'variantsDts',
79
+ ];
80
+ expect(undocumented(returnsDescription('theme.build'), fields)).toEqual([]);
81
+ });
82
+
83
+ it('theme.build in the response-types doc', async () => {
84
+ const dir = themeDir();
85
+ const result = await themeBuild('ocean.mjs', {}, {cwd: dir});
86
+ const data = /** @type {Record<string, any>} */ (result?.data);
87
+ const member = responseTypes.members.find(m => m.value === 'theme.build');
88
+ expect(member, 'response-types has no theme.build member').toBeDefined();
89
+
90
+ const fields = [
91
+ ...Object.keys(data),
92
+ ...Object.keys(data.outputs),
93
+ 'variantsDts',
94
+ ];
95
+ expect(
96
+ undocumented(/** @type {{description: string}} */ (member).description, fields),
97
+ ).toEqual([]);
98
+ });
99
+
100
+ it('theme.build.check', async () => {
101
+ const dir = themeDir();
102
+ const result = await themeBuild('ocean.mjs', {check: true}, {cwd: dir});
103
+
104
+ expect(result?.type).toBe('theme.build.check');
105
+ const data = /** @type {Record<string, any>} */ (result?.data);
106
+ const fields = [...Object.keys(data), ...Object.keys(data.stale[0])];
107
+ expect(
108
+ undocumented(returnsDescription('theme.build.check'), fields),
109
+ ).toEqual([]);
110
+ });
111
+ });
@@ -0,0 +1,148 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * `theme build` writes its CSS, JS, and declarations as one transaction: when
5
+ * a later output fails to publish, every output it already replaced gets its
6
+ * previous bytes back and every output it created is removed. Publishing is
7
+ * forced to fail by wrapping the two calls that publish a staged file:
8
+ * `linkSync` for a new file and `renameSync` for a replacement.
9
+ *
10
+ * Separate file because vi.mock is hoisted and affects the whole module.
11
+ * `themeBuild` needs a built core; the `node` project's globalSetup builds it.
12
+ */
13
+
14
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
15
+ import * as os from 'node:os';
16
+ import * as path from 'node:path';
17
+
18
+ const failures = vi.hoisted(() => ({
19
+ /** Fail the Nth staged-file publish (1-based); 0 disables. */
20
+ publish: 0,
21
+ count: 0,
22
+ }));
23
+
24
+ vi.mock('node:fs', async importOriginal => {
25
+ const actual = /** @type {typeof import('node:fs')} */ (
26
+ await importOriginal()
27
+ );
28
+ /** @param {string} source @param {string} op */
29
+ const maybeFail = (source, op) => {
30
+ if (!path.basename(String(source)).includes('.tmp')) return;
31
+ if (failures.publish === 0) return;
32
+ failures.count++;
33
+ if (failures.count === failures.publish) {
34
+ throw Object.assign(new Error(`EIO: forced ${op} failure`), {
35
+ code: 'EIO',
36
+ });
37
+ }
38
+ };
39
+ return {
40
+ ...actual,
41
+ linkSync: vi.fn((source, destination) => {
42
+ maybeFail(source, 'link');
43
+ return actual.linkSync(source, destination);
44
+ }),
45
+ renameSync: vi.fn((source, destination) => {
46
+ maybeFail(source, 'rename');
47
+ return actual.renameSync(source, destination);
48
+ }),
49
+ };
50
+ });
51
+
52
+ const fs = await import('node:fs');
53
+ const {themeBuild} = await import('./build.mjs');
54
+
55
+ vi.setConfig({testTimeout: 30000});
56
+
57
+ let tmpDir;
58
+
59
+ beforeEach(() => {
60
+ tmpDir = fs.mkdtempSync(
61
+ path.join(os.tmpdir(), 'astryx-theme-build-rollback-'),
62
+ );
63
+ failures.publish = 0;
64
+ failures.count = 0;
65
+ });
66
+
67
+ afterEach(() => {
68
+ failures.publish = 0;
69
+ fs.rmSync(tmpDir, {recursive: true, force: true});
70
+ });
71
+
72
+ /**
73
+ * Write a source for the theme named `rollbacktheme`. Each call uses a new
74
+ * file so the loader cannot serve an earlier version from its cache.
75
+ * @param {string} file @param {string} background
76
+ */
77
+ function writeTheme(file, background) {
78
+ fs.writeFileSync(
79
+ path.join(tmpDir, file),
80
+ `export default { name: 'rollbacktheme', tokens: { '--color-bg': '${background}' } };\n`,
81
+ );
82
+ return file;
83
+ }
84
+
85
+ const OUTPUTS = ['rollbacktheme.css', 'rollbacktheme.js', 'rollbacktheme.d.ts'];
86
+
87
+ /** @returns {Map<string, Buffer | null>} */
88
+ function readOutputs() {
89
+ return new Map(
90
+ OUTPUTS.map(name => {
91
+ const file = path.join(tmpDir, name);
92
+ return [name, fs.existsSync(file) ? fs.readFileSync(file) : null];
93
+ }),
94
+ );
95
+ }
96
+
97
+ function strays() {
98
+ return fs
99
+ .readdirSync(tmpDir)
100
+ .filter(name => name.includes('.tmp') || name.includes('.restore-'));
101
+ }
102
+
103
+ describe('themeBuild rolls back a partial write', () => {
104
+ it('removes every created output when the second publish fails', async () => {
105
+ const source = writeTheme('first.mjs', '#0a0a0a');
106
+ failures.publish = 2;
107
+
108
+ await expect(themeBuild(source, {}, {cwd: tmpDir})).rejects.toMatchObject({
109
+ code: 'ERR_WRITE_FAILED',
110
+ });
111
+
112
+ for (const bytes of readOutputs().values()) expect(bytes).toBeNull();
113
+ expect(strays()).toEqual([]);
114
+ });
115
+
116
+ it('restores every replaced output when the second publish fails', async () => {
117
+ const first = await themeBuild(
118
+ writeTheme('first.mjs', '#0a0a0a'),
119
+ {},
120
+ {cwd: tmpDir},
121
+ );
122
+ expect(first?.type).toBe('theme.build');
123
+ const before = readOutputs();
124
+ for (const bytes of before.values()) expect(bytes).not.toBeNull();
125
+
126
+ const next = writeTheme('next.mjs', '#fafafa');
127
+ failures.publish = 2;
128
+ await expect(themeBuild(next, {}, {cwd: tmpDir})).rejects.toMatchObject({
129
+ code: 'ERR_WRITE_FAILED',
130
+ });
131
+
132
+ const after = readOutputs();
133
+ for (const name of OUTPUTS) {
134
+ expect(
135
+ after.get(name)?.equals(/** @type {Buffer} */ (before.get(name))),
136
+ ).toBe(true);
137
+ }
138
+
139
+ // The failed build really had different bytes to write.
140
+ failures.publish = 0;
141
+ await themeBuild(next, {}, {cwd: tmpDir});
142
+ const css = readOutputs().get('rollbacktheme.css');
143
+ expect(
144
+ css?.equals(/** @type {Buffer} */ (before.get('rollbacktheme.css'))),
145
+ ).toBe(false);
146
+ expect(strays()).toEqual([]);
147
+ });
148
+ });