@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db7b338

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 (276) hide show
  1. package/README.md +22 -1
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/build.doc.mjs +1 -0
  4. package/api/component/component.doc.mjs +1 -0
  5. package/api/discover/discover.doc.mjs +1 -0
  6. package/api/docs/_adapter.d.mts +3 -8
  7. package/api/docs/_adapter.mjs +13 -105
  8. package/api/docs/detail/section/section.mjs +9 -15
  9. package/api/docs/detail/section/section.test.mjs +15 -6
  10. package/api/docs/docs.doc.mjs +1 -0
  11. package/api/docs/list/list.mjs +3 -7
  12. package/api/doctor/doctor.d.mts +25 -3
  13. package/api/doctor/doctor.doc.mjs +1 -0
  14. package/api/doctor/doctor.mjs +186 -14
  15. package/api/doctor/doctor.test.mjs +332 -7
  16. package/api/gap-report/gap-report.doc.mjs +1 -0
  17. package/api/hook/_adapter.mjs +19 -5
  18. package/api/hook/hook.doc.mjs +1 -0
  19. package/api/hook/list/list.d.mts +1 -1
  20. package/api/hook/list/list.mjs +69 -17
  21. package/api/init/init.doc.mjs +1 -0
  22. package/api/integration/add-contribution.mjs +7 -7
  23. package/api/integration/add-contribution.test.mjs +37 -3
  24. package/api/integration/add-theme.mjs +34 -64
  25. package/api/integration/add-theme.test.mjs +105 -21
  26. package/api/integration/authoring-checks.test.mjs +8 -4
  27. package/api/integration/integration-block-exports.test.mjs +10 -6
  28. package/api/integration/integrationAdd.doc.mjs +1 -0
  29. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  30. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  31. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  32. package/api/integration/integrationAddDoc.doc.mjs +1 -0
  33. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  34. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  35. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  36. package/api/integration/integrationDocConflicts.doc.mjs +1 -0
  37. package/api/integration/integrationPackCheck.doc.mjs +1 -0
  38. package/api/integration/integrationTemplateConflicts.doc.mjs +1 -0
  39. package/api/integration/pack-check.test.mjs +17 -47
  40. package/api/integration/summarizeIssues.doc.mjs +1 -0
  41. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  42. package/api/integration/validate-integration.mjs +50 -102
  43. package/api/integration/validate-integration.test.mjs +85 -23
  44. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  45. package/api/integration/validateIntegration.doc.mjs +1 -0
  46. package/api/json/assertResponse.doc.mjs +1 -0
  47. package/api/json/isError.doc.mjs +1 -0
  48. package/api/json/parseResponse.doc.mjs +1 -0
  49. package/api/layout/layoutCheck.doc.mjs +1 -0
  50. package/api/layout/layoutExpand.doc.mjs +1 -0
  51. package/api/layout/layoutGrammar.doc.mjs +1 -0
  52. package/api/search/search.doc.mjs +1 -0
  53. package/api/search/search.mjs +16 -6
  54. package/api/swizzle/swizzle.doc.mjs +1 -0
  55. package/api/template/template-suffix.test.mjs +41 -21
  56. package/api/template/template.doc.mjs +1 -0
  57. package/api/theme/_adapter.d.mts +2 -3
  58. package/api/theme/_adapter.mjs +4 -5
  59. package/api/theme/add/add.binary.test.mjs +10 -17
  60. package/api/theme/add/add.test.mjs +14 -1
  61. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  62. package/api/theme/integration-themes.test.mjs +39 -28
  63. package/api/theme/list/list.test.mjs +19 -20
  64. package/api/theme/listThemes.doc.mjs +6 -5
  65. package/api/theme/themeAdd.doc.mjs +4 -3
  66. package/api/theme/themeBuild.doc.mjs +1 -0
  67. package/api/theme/themeList.doc.mjs +6 -3
  68. package/api/theme/themeListAvailable.doc.mjs +5 -3
  69. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  70. package/api/theme/themeTargets.doc.mjs +1 -0
  71. package/api/theme/themeTemplate.doc.mjs +1 -0
  72. package/api/upgrade/_adapter.d.mts +32 -5
  73. package/api/upgrade/_adapter.mjs +97 -69
  74. package/api/upgrade/provider-agreement.test.mjs +152 -0
  75. package/api/upgrade/run/run.mjs +356 -59
  76. package/api/upgrade/upgrade.doc.mjs +6 -1
  77. package/api/upgrade/upgrade.type.d.mts +34 -0
  78. package/api/upgrade/upgrade.type.mjs +15 -0
  79. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  80. package/assets/codemods/integration-discovery.mjs +8 -2
  81. package/assets/codemods/integration-discovery.test.mjs +15 -0
  82. package/assets/codemods/integration-runner.mjs +56 -4
  83. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  84. package/assets/codemods/run-codemod.mjs +177 -34
  85. package/assets/codemods/runner.mjs +350 -102
  86. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  87. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  88. package/assets/codemods/transforms/next/index.mjs +19 -1
  89. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  90. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  91. package/assets/docs/cli-integrations.doc.mjs +30 -30
  92. package/assets/docs/cli.doc.mjs +15 -0
  93. package/assets/docs/theme.doc.mjs +1 -1
  94. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  95. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  96. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  97. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  98. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  99. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  100. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  101. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  102. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  103. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  104. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  105. package/authoring/codemod/codemod.doc.mjs +1 -1
  106. package/authoring/codemod/type.ts +12 -0
  107. package/authoring/config/config.doc.mjs +1 -1
  108. package/authoring/debug/debug.doc.d.mts +11 -0
  109. package/authoring/debug/debug.doc.mjs +182 -0
  110. package/authoring/doctypes/_schema.d.mts +71 -141
  111. package/authoring/doctypes/_schema.mjs +29 -2
  112. package/authoring/doctypes/base/graph-fields.doc.mjs +1 -1
  113. package/authoring/doctypes/command/command.doc.mjs +1 -1
  114. package/authoring/doctypes/command/type.ts +1 -1
  115. package/authoring/doctypes/component/type.ts +2 -2
  116. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  117. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  118. package/authoring/doctypes/enum/type.ts +1 -1
  119. package/authoring/doctypes/function/function.doc.mjs +1 -1
  120. package/authoring/doctypes/function/type.ts +1 -1
  121. package/authoring/doctypes/hook/type.ts +2 -2
  122. package/authoring/doctypes/load-contract.test.mjs +3 -2
  123. package/authoring/doctypes/namespace/namespace.doc.mjs +2 -2
  124. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  125. package/authoring/doctypes/namespace/type.ts +5 -2
  126. package/authoring/doctypes/parse.d.mts +4 -2
  127. package/authoring/doctypes/parse.mjs +10 -5
  128. package/authoring/doctypes/reference/reference.doc.mjs +6 -4
  129. package/authoring/doctypes/reference/type.ts +9 -10
  130. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  131. package/authoring/doctypes/schema/type.ts +1 -2
  132. package/authoring/doctypes/template/template.doc.mjs +3 -3
  133. package/authoring/doctypes/theme/parse.d.mts +35 -0
  134. package/authoring/doctypes/theme/parse.mjs +76 -0
  135. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  136. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  137. package/authoring/doctypes/theme/type.ts +42 -0
  138. package/authoring/doctypes/types.ts +2 -1
  139. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  140. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  141. package/authoring/index.d.mts +1 -0
  142. package/authoring/index.d.ts +7 -4
  143. package/authoring/index.mjs +2 -1
  144. package/authoring/integration/integration.doc.mjs +2 -2
  145. package/authoring/integration/type.ts +3 -9
  146. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  147. package/clients/cli/commands/blog.doc.mjs +1 -1
  148. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  149. package/clients/cli/commands/build-theme.mjs +10 -44
  150. package/clients/cli/commands/build.doc.mjs +1 -1
  151. package/clients/cli/commands/component.doc.mjs +1 -1
  152. package/clients/cli/commands/discover.doc.mjs +1 -1
  153. package/clients/cli/commands/docs.doc.mjs +1 -1
  154. package/clients/cli/commands/docs.mjs +15 -58
  155. package/clients/cli/commands/docs.test.mjs +11 -14
  156. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  157. package/clients/cli/commands/doctor-integration-docs.doc.mjs +1 -1
  158. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -1
  159. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  160. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  161. package/clients/cli/commands/doctor-integration.test.mjs +15 -7
  162. package/clients/cli/commands/doctor.doc.mjs +1 -1
  163. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  164. package/clients/cli/commands/hook.doc.mjs +1 -1
  165. package/clients/cli/commands/init.doc.mjs +1 -1
  166. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  167. package/clients/cli/commands/integration-add.doc.mjs +1 -1
  168. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  169. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  170. package/clients/cli/commands/integration.doc.mjs +1 -1
  171. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  172. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  173. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  174. package/clients/cli/commands/layout.doc.mjs +1 -1
  175. package/clients/cli/commands/manifest.doc.mjs +1 -1
  176. package/clients/cli/commands/search.doc.mjs +1 -1
  177. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  178. package/clients/cli/commands/template.doc.mjs +1 -1
  179. package/clients/cli/commands/text-json-parity.test.mjs +718 -0
  180. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  181. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  182. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  183. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  184. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  185. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  186. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  187. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  188. package/clients/cli/commands/theme.doc.mjs +1 -1
  189. package/clients/cli/commands/upgrade.doc.mjs +3 -2
  190. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  191. package/clients/cli/commands/upgrade.mjs +29 -7
  192. package/clients/cli/formatters/index.mjs +2 -0
  193. package/clients/cli/formatters/index.test.mjs +6 -0
  194. package/clients/cli/lib/hook-format.mjs +14 -5
  195. package/foundation/config/project-themes.test.mjs +11 -19
  196. package/foundation/config/project.d.mts +8 -0
  197. package/foundation/config/project.mjs +50 -21
  198. package/foundation/config/project.test.mjs +3 -16
  199. package/foundation/discovery/authoring-self-docs.d.mts +6 -0
  200. package/foundation/discovery/authoring-self-docs.mjs +17 -7
  201. package/foundation/discovery/authoring-self-docs.test.mjs +3 -2
  202. package/foundation/discovery/authoring-surface.d.mts +74 -0
  203. package/foundation/discovery/authoring-surface.mjs +525 -0
  204. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  205. package/foundation/discovery/cli-self-docs.d.mts +113 -0
  206. package/foundation/discovery/cli-self-docs.mjs +514 -0
  207. package/foundation/discovery/cli-self-docs.test.mjs +437 -0
  208. package/foundation/discovery/component-loader.d.mts +35 -38
  209. package/foundation/discovery/component-loader.mjs +53 -222
  210. package/foundation/discovery/docs-discovery.d.mts +3 -2
  211. package/foundation/discovery/docs-discovery.mjs +14 -17
  212. package/foundation/discovery/docs-discovery.test.mjs +15 -15
  213. package/foundation/discovery/docs-section-key.d.mts +19 -8
  214. package/foundation/discovery/docs-section-key.mjs +119 -32
  215. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  216. package/foundation/discovery/template-adapter.d.mts +7 -0
  217. package/foundation/discovery/template-adapter.mjs +27 -40
  218. package/foundation/discovery/template-adapter.test.mjs +15 -0
  219. package/foundation/discovery/theme-discovery.d.mts +67 -7
  220. package/foundation/discovery/theme-discovery.mjs +916 -186
  221. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  222. package/foundation/doc-compiler/bundle.d.mts +47 -0
  223. package/foundation/doc-compiler/bundle.mjs +218 -0
  224. package/foundation/doc-compiler/bundle.test.mjs +255 -0
  225. package/foundation/doc-compiler/compile.d.mts +200 -0
  226. package/foundation/doc-compiler/compile.mjs +259 -9
  227. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  228. package/foundation/doc-compiler/diagnostics.mjs +277 -0
  229. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  230. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  231. package/foundation/doc-compiler/import.d.mts +24 -0
  232. package/foundation/doc-compiler/import.mjs +59 -0
  233. package/foundation/doc-compiler/inputs.d.mts +102 -0
  234. package/foundation/doc-compiler/inputs.mjs +286 -0
  235. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  236. package/foundation/doc-compiler/ir.d.mts +13 -0
  237. package/foundation/doc-compiler/ir.mjs +189 -12
  238. package/foundation/doc-compiler/lower-doc.test.mjs +492 -0
  239. package/foundation/doc-compiler/overlays.d.mts +37 -0
  240. package/foundation/doc-compiler/overlays.mjs +206 -0
  241. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  242. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  243. package/foundation/doc-compiler/read.d.mts +126 -0
  244. package/foundation/doc-compiler/read.mjs +320 -0
  245. package/foundation/doc-compiler/read.test.mjs +313 -0
  246. package/foundation/doc-compiler/source.d.mts +33 -0
  247. package/foundation/doc-compiler/source.mjs +128 -0
  248. package/foundation/fs/file-protection.d.mts +33 -0
  249. package/foundation/fs/file-protection.mjs +825 -0
  250. package/foundation/fs/file-protection.test.mjs +250 -0
  251. package/foundation/integrations/autolink.d.mts +58 -1
  252. package/foundation/integrations/autolink.mjs +143 -57
  253. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  254. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  255. package/foundation/integrations/contribution-inventory.d.mts +1 -1
  256. package/foundation/integrations/contribution-inventory.mjs +17 -20
  257. package/foundation/integrations/contribution-inventory.test.mjs +67 -27
  258. package/foundation/integrations/integrations.d.mts +3 -0
  259. package/foundation/integrations/integrations.mjs +11 -97
  260. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  261. package/foundation/integrations/provider-resolution.d.mts +152 -0
  262. package/foundation/integrations/provider-resolution.mjs +576 -0
  263. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  264. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  265. package/foundation/integrations/theme-descriptor.mjs +44 -0
  266. package/foundation/integrations/validate-contributions.mjs +104 -10
  267. package/foundation/response/error-codes.d.mts +3 -1
  268. package/foundation/response/error-codes.d.ts +2 -0
  269. package/foundation/response/error-codes.doc.mjs +12 -2
  270. package/foundation/response/error-codes.mjs +7 -1
  271. package/foundation/response/error-codes.test.mjs +55 -11
  272. package/foundation/response/response-types.doc.mjs +1 -1
  273. package/foundation/response/response.doc.mjs +1 -1
  274. package/foundation/text/string-utils.mjs +22 -10
  275. package/package.json +10 -9
  276. package/assets/templates/themes/manifest.json +0 -95
@@ -0,0 +1,206 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Translation overlays for component and hook docs.
5
+ *
6
+ * @input An authored component or hook doc and the translation its module
7
+ * exports for the reading language (`docsZh`, `docsDense`).
8
+ * @output The doc in that language: translated text laid over the authored
9
+ * doc, never dropping a prop, param, or field the translation has not
10
+ * caught up with.
11
+ * @position Used by ./compile.mjs when it lowers a component or hook doc, so
12
+ * every reader sees one overlay rule. Reference topics overlay by section in
13
+ * ./compile.mjs instead.
14
+ */
15
+
16
+ /**
17
+ * The translation a doc module exports for `lang`, or null.
18
+ * @param {Record<string, any>} mod
19
+ * @param {string | null} lang
20
+ * @returns {any}
21
+ */
22
+ export function translationFor(mod, lang) {
23
+ const key = lang === 'zh' ? 'docsZh' : lang === 'dense' ? 'docsDense' : null;
24
+ return key && mod?.[key] ? mod[key] : null;
25
+ }
26
+
27
+ /**
28
+ * Lay a translation over an authored component or hook doc. A translation
29
+ * that carries props is a full translated doc, overlaid prop by prop; any other
30
+ * is a set of translated strings merged onto the doc.
31
+ * @param {any} docs
32
+ * @param {any} translation
33
+ * @returns {any}
34
+ */
35
+ export function overlayAuthoredDoc(docs, translation) {
36
+ if (
37
+ translation.props ||
38
+ translation.components?.some((/** @type {any} */ c) => c.props)
39
+ ) {
40
+ return overlayComponentDoc(docs, translation);
41
+ }
42
+ return mergeTranslation(docs, translation);
43
+ }
44
+
45
+ /**
46
+ * @param {any} docs
47
+ * @param {any} translation
48
+ * @returns {any}
49
+ */
50
+ export function mergeTranslation(docs, translation) {
51
+ if (!translation) return docs;
52
+
53
+ /** @type {any} */
54
+ const merged = {...docs};
55
+
56
+ // Merge prose into usage
57
+ if (merged.usage) {
58
+ merged.usage = {...merged.usage};
59
+ if (translation.usage?.description)
60
+ merged.usage.description = translation.usage.description;
61
+ else if (translation.description)
62
+ merged.usage.description = translation.description;
63
+ if (translation.usage?.bestPractices)
64
+ merged.usage.bestPractices = translation.usage.bestPractices;
65
+ if (translation.usage?.accessibility)
66
+ merged.usage.accessibility = translation.usage.accessibility;
67
+ }
68
+
69
+ // Legacy top-level fields (for docsZh that are full ComponentDoc clones)
70
+ if (translation.description && !merged.usage)
71
+ merged.description = translation.description;
72
+
73
+ // Merge prop descriptions for single-component docs
74
+ if (translation.propDescriptions && merged.props) {
75
+ merged.props = merged.props.map((/** @type {any} */ prop) => {
76
+ const desc = translation.propDescriptions[prop.name];
77
+ return desc != null ? {...prop, description: desc} : prop;
78
+ });
79
+ }
80
+
81
+ // Merge hook param descriptions (HookTranslationDoc). Params are an array of
82
+ // {name, type, description, required}; override description by name where the
83
+ // translation has an entry. Names may include dots (e.g. 'options.isActive');
84
+ // the lookup is keyed by the exact param name.
85
+ if (translation.paramDescriptions && merged.params) {
86
+ merged.params = merged.params.map((/** @type {any} */ param) => {
87
+ const desc = translation.paramDescriptions[param.name];
88
+ return desc != null ? {...param, description: desc} : param;
89
+ });
90
+ }
91
+
92
+ // Merge hook return descriptions (HookTranslationDoc). Returns are an array
93
+ // of {name, type, description}; override description by name where present.
94
+ if (translation.returnDescriptions && merged.returns) {
95
+ merged.returns = merged.returns.map((/** @type {any} */ ret) => {
96
+ const desc = translation.returnDescriptions[ret.name];
97
+ return desc != null ? {...ret, description: desc} : ret;
98
+ });
99
+ }
100
+
101
+ // Merge sub-component translations
102
+ if (translation.components && merged.components) {
103
+ merged.components = merged.components.map(
104
+ (/** @type {any} */ comp, /** @type {any} */ i) => {
105
+ const trans =
106
+ translation.components.find(
107
+ (/** @type {any} */ t) => t.name === comp.name,
108
+ ) || translation.components[i];
109
+ if (!trans) return comp;
110
+
111
+ const mergedComp = {...comp};
112
+ if (trans.description) mergedComp.description = trans.description;
113
+ if (trans.propDescriptions && comp.props) {
114
+ mergedComp.props = comp.props.map((/** @type {any} */ prop) => {
115
+ const desc = trans.propDescriptions[prop.name];
116
+ return desc != null ? {...prop, description: desc} : prop;
117
+ });
118
+ }
119
+ return mergedComp;
120
+ },
121
+ );
122
+ }
123
+
124
+ return merged;
125
+ }
126
+
127
+ /**
128
+ * Overlay a full-ComponentDoc-shaped translation onto the English doc.
129
+ *
130
+ * Base order and completeness win; the translation supplies text for the
131
+ * entries it covers. Props are matched by name, never by position, so a
132
+ * translation that is missing entries (or lists them in another order) can no
133
+ * longer drop or misattribute one.
134
+ *
135
+ * @param {any} docs Base (English) component doc.
136
+ * @param {any} translation Translated doc, possibly covering only some props.
137
+ * @returns {any} Merged doc with every base prop present.
138
+ */
139
+ function overlayComponentDoc(docs, translation) {
140
+ /** Merge one prop list: keep base entries and order, translate what's covered.
141
+ * @param {any[] | undefined} baseProps
142
+ * @param {any[] | undefined} tProps
143
+ */
144
+ const overlayProps = (baseProps, tProps) => {
145
+ if (!baseProps) return baseProps;
146
+ const byName = new Map(
147
+ (tProps ?? []).map((/** @type {any} */ p) => [p.name, p]),
148
+ );
149
+ return baseProps.map((/** @type {any} */ prop) => {
150
+ const t = byName.get(prop.name);
151
+ // Take the translated text, but never let it drop the prop's contract
152
+ // (type/default/required stay authoritative from the English doc).
153
+ return t ? {...prop, ...t, name: prop.name, type: prop.type} : prop;
154
+ });
155
+ };
156
+
157
+ /** Preserve canonical structured guidance added after legacy full-doc translations,
158
+ * without changing established translated prose behavior.
159
+ * @param {any} baseUsage
160
+ * @param {any} translatedUsage
161
+ */
162
+ const mergeUsage = (baseUsage, translatedUsage) => {
163
+ if (!translatedUsage) return baseUsage;
164
+ return {
165
+ ...translatedUsage,
166
+ ...(translatedUsage.accessibility === undefined &&
167
+ baseUsage?.accessibility !== undefined
168
+ ? {accessibility: baseUsage.accessibility}
169
+ : null),
170
+ ...(translatedUsage.accessibilityThemeCoverage === undefined &&
171
+ baseUsage?.accessibilityThemeCoverage !== undefined
172
+ ? {accessibilityThemeCoverage: baseUsage.accessibilityThemeCoverage}
173
+ : null),
174
+ ...(translatedUsage.anatomy === undefined &&
175
+ baseUsage?.anatomy !== undefined
176
+ ? {anatomy: baseUsage.anatomy}
177
+ : null),
178
+ };
179
+ };
180
+
181
+ const merged = {
182
+ ...docs,
183
+ ...translation,
184
+ usage: mergeUsage(docs.usage, translation.usage),
185
+ };
186
+
187
+ merged.props = overlayProps(docs.props, translation.props);
188
+
189
+ if (docs.components) {
190
+ const tByName = new Map(
191
+ (translation.components ?? []).map((/** @type {any} */ c) => [c.name, c]),
192
+ );
193
+ merged.components = docs.components.map((/** @type {any} */ base) => {
194
+ const t = tByName.get(base.name);
195
+ if (!t) return base;
196
+ return {
197
+ ...base,
198
+ ...t,
199
+ usage: mergeUsage(base.usage, t.usage),
200
+ props: overlayProps(base.props, t.props),
201
+ };
202
+ });
203
+ }
204
+
205
+ return merged;
206
+ }
@@ -0,0 +1,9 @@
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
+ * @param {unknown} input
6
+ * @param {string} [label]
7
+ * @returns {any}
8
+ */
9
+ export function parseReadableDoc(input: unknown, label?: string): any;
@@ -0,0 +1,29 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file parseDoc as every reader outside the themes root has always seen it.
5
+ *
6
+ * @input Any authored doc value and a label for messages.
7
+ * @output The parsed doc, or the parser's error.
8
+ * @position The public `parseDoc` also accepts theme descriptors. Component,
9
+ * hook, self-doc, and topic readers never did: to them a theme descriptor is
10
+ * an unsupported type, with the message they have always printed.
11
+ */
12
+
13
+ import {parseDoc} from '../../authoring/doctypes/parse.mjs';
14
+
15
+ /**
16
+ * @param {unknown} input
17
+ * @param {string} [label]
18
+ * @returns {any}
19
+ */
20
+ export function parseReadableDoc(input, label = 'doc') {
21
+ const type =
22
+ input && typeof input === 'object' && 'type' in input
23
+ ? /** @type {{type?: unknown}} */ (input).type
24
+ : undefined;
25
+ if (type === 'theme') {
26
+ throw new Error(`${label} has unsupported type ${JSON.stringify(type)}.`);
27
+ }
28
+ return parseDoc(input, label);
29
+ }
@@ -0,0 +1,126 @@
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
+ * Lower one descriptor file. Memoized per file and options for the life of the
6
+ * process; a file that failed to load is tried again on the next read, as a
7
+ * fresh import would be.
8
+ * @param {string} file absolute path
9
+ * @param {ReadOptions} options
10
+ * @param {{node?: boolean}} [want] also build the sealed node
11
+ * @returns {Promise<import('./compile.mjs').LoweredDoc>}
12
+ */
13
+ export function compileDocFile(file: string, options: ReadOptions, { node }?: {
14
+ node?: boolean;
15
+ }): Promise<import("./compile.mjs").LoweredDoc>;
16
+ /**
17
+ * What a reader gets for one descriptor: the view it has always read.
18
+ *
19
+ * `strict` readers check the doc and throw what they always threw: the import
20
+ * error, the parser's error (for an empty export too), then a failed
21
+ * translation. Other readers skip the check, throw only what importing or
22
+ * translating threw, and read an empty export as the empty value itself.
23
+ * The view is shared, as the module's own export always was.
24
+ * @param {string} file absolute path
25
+ * @param {ReadOptions & {strict?: boolean}} options
26
+ * @returns {Promise<any>}
27
+ */
28
+ export function readDocView(file: string, options: ReadOptions & {
29
+ strict?: boolean;
30
+ }): Promise<any>;
31
+ /**
32
+ * Freeze a value and everything in it.
33
+ * @template T
34
+ * @param {T} value
35
+ * @returns {T}
36
+ */
37
+ export function deepFreeze<T>(value: T): T;
38
+ /**
39
+ * Where the `lang` overlay of a doc file lives: `{topic}.doc.{lang}.mjs`.
40
+ * @param {string} docPath
41
+ * @param {string} lang
42
+ * @returns {string}
43
+ */
44
+ export function overlayPath(docPath: string, lang: string): string;
45
+ /**
46
+ * The overlay languages a topic ships for its own file or any extension.
47
+ * @param {import('../discovery/docs-discovery.mjs').DocsTopicEntry} entry
48
+ * @returns {string[]}
49
+ */
50
+ export function overlayLanguages(entry: import("../discovery/docs-discovery.mjs").DocsTopicEntry): string[];
51
+ /**
52
+ * Load one topic file and the overlay for `lang`. A failure is recorded on the
53
+ * result, not thrown, so the compiler reports it in reading order.
54
+ * @param {string} docPath
55
+ * @param {string | null} lang
56
+ * @returns {Promise<import('./compile.mjs').AuthoredFile>}
57
+ */
58
+ export function loadTopicFile(docPath: string, lang: string | null): Promise<import("./compile.mjs").AuthoredFile>;
59
+ /**
60
+ * Everything the compiler needs for one topic, read from disk.
61
+ * @param {import('../discovery/docs-discovery.mjs').DocsTopicEntry} entry
62
+ * @param {string | null} lang
63
+ * @returns {Promise<import('./compile.mjs').ReferenceTopicInput>}
64
+ */
65
+ export function loadTopicInput(entry: import("../discovery/docs-discovery.mjs").DocsTopicEntry, lang: string | null): Promise<import("./compile.mjs").ReferenceTopicInput>;
66
+ /**
67
+ * How each root's module names its doc, in precedence order: the `??` chain
68
+ * each reader has always used, so a file that exports two docs keeps serving
69
+ * the one it served before.
70
+ */
71
+ export const DOC_EXPORTS: Readonly<{
72
+ components: string[];
73
+ hooks: string[];
74
+ templates: string[];
75
+ 'self-docs': string[];
76
+ }>;
77
+ /** The localized overlays a docs read can apply. */
78
+ export const OVERLAY_LANGUAGES: string[];
79
+ export type ReadOptions = {
80
+ root: "components" | "hooks" | "templates" | "themes" | "self-docs";
81
+ /**
82
+ * overlay language; null reads the authored
83
+ * text
84
+ */
85
+ lang?: string | null | undefined;
86
+ /**
87
+ * how a parse error names the file (default: its
88
+ * file name)
89
+ */
90
+ label?: string | undefined;
91
+ /**
92
+ * the owning package, when the caller knows it
93
+ */
94
+ provider?: string | undefined;
95
+ /**
96
+ * the node's id (default: provider, root, and file
97
+ * name)
98
+ */
99
+ id?: string | undefined;
100
+ /**
101
+ * how to import the module (default
102
+ * `user`)
103
+ */
104
+ loader?: "template" | "user" | "native" | undefined;
105
+ /**
106
+ * which exports name the doc, in
107
+ * precedence order, for a reader that has always read a narrower set than its
108
+ * root's default ({@link DOC_EXPORTS})
109
+ */
110
+ exports?: readonly string[] | undefined;
111
+ /**
112
+ * for themes: the descriptor value, read
113
+ * without executing the file
114
+ */
115
+ readStatic?: (() => unknown) | undefined;
116
+ /**
117
+ * which value the view carries: the
118
+ * authored export (default) or its parser's result, for a reader that has
119
+ * always read the checked value
120
+ */
121
+ value?: "authored" | "parsed" | undefined;
122
+ /**
123
+ * hold the doc to its kind's parser
124
+ */
125
+ check?: boolean | undefined;
126
+ };
@@ -0,0 +1,320 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Doc reader — authored files in, lowered docs out, for every doc kind.
5
+ *
6
+ * @input A descriptor file, the root that reads it (components, hooks,
7
+ * templates, themes, self-docs, or doc topics), the reading language, and
8
+ * how strictly the reader checks.
9
+ * @output The lowered doc for that file. Readers get the view they have always
10
+ * read, lowered afresh on every read; a whole-project compile also gets the
11
+ * sealed node, memoized per file for the life of the process. For doc
12
+ * topics: the compiler input for a topic, with its extensions and overlays
13
+ * loaded.
14
+ * @position Loads authored doc files for the readers in api/ and clients/ and
15
+ * hands each to ./compile.mjs. Each reader keeps its own loader, export
16
+ * order, and strictness, so what it prints is what it printed before.
17
+ * ./doc-loads.test.mjs lists, site by site, every other place the CLI runs
18
+ * anything but its static imports of other CLI code, the doc reads that skip
19
+ * this module among them: discovery's catalog fields, each command's
20
+ * self-docs for its help text, and the build-time README. A new site fails
21
+ * it. It catches every ordinary way of running code, whatever the local
22
+ * names; deliberate obfuscation is out of scope. Internal to the CLI:
23
+ * nothing here is public API.
24
+ */
25
+
26
+ import * as fs from 'node:fs';
27
+ import * as path from 'node:path';
28
+ import {
29
+ importDocModule,
30
+ importNativeModule,
31
+ importTemplateModule,
32
+ } from './import.mjs';
33
+ import {lowerDoc, parserFor} from './compile.mjs';
34
+ import {translationFor} from './overlays.mjs';
35
+ import {parseReadableDoc} from './parse-readable.mjs';
36
+ import {packageOf, packageSource} from './source.mjs';
37
+
38
+ /**
39
+ * How each root's module names its doc, in precedence order: the `??` chain
40
+ * each reader has always used, so a file that exports two docs keeps serving
41
+ * the one it served before.
42
+ */
43
+ export const DOC_EXPORTS = Object.freeze({
44
+ components: ['default', 'docs'],
45
+ hooks: ['default', 'docs'],
46
+ templates: ['default', 'doc'],
47
+ 'self-docs': ['doc', 'docs', 'default'],
48
+ });
49
+
50
+ /** How a reader imports a doc module. */
51
+ const LOADERS = Object.freeze({
52
+ // jiti for `.ts`, native otherwise: the checked loaders' import.
53
+ user: importDocModule,
54
+ // A plain `import()`: what the unchecked readers have always used.
55
+ native: importNativeModule,
56
+ // jiti with JSX for `.ts`: template discovery's import.
57
+ template: importTemplateModule,
58
+ });
59
+
60
+ /**
61
+ * @typedef {object} ReadOptions
62
+ * @property {'components' | 'hooks' | 'templates' | 'themes' | 'self-docs'} root
63
+ * @property {string | null} [lang] overlay language; null reads the authored
64
+ * text
65
+ * @property {string} [label] how a parse error names the file (default: its
66
+ * file name)
67
+ * @property {string} [provider] the owning package, when the caller knows it
68
+ * @property {string} [id] the node's id (default: provider, root, and file
69
+ * name)
70
+ * @property {keyof typeof LOADERS} [loader] how to import the module (default
71
+ * `user`)
72
+ * @property {readonly string[]} [exports] which exports name the doc, in
73
+ * precedence order, for a reader that has always read a narrower set than its
74
+ * root's default ({@link DOC_EXPORTS})
75
+ * @property {() => unknown} [readStatic] for themes: the descriptor value, read
76
+ * without executing the file
77
+ * @property {'authored' | 'parsed'} [value] which value the view carries: the
78
+ * authored export (default) or its parser's result, for a reader that has
79
+ * always read the checked value
80
+ * @property {boolean} [check] hold the doc to its kind's parser
81
+ */
82
+
83
+ /** @type {Map<string, Promise<import('./compile.mjs').LoweredDoc>>} */
84
+ const lowered = new Map();
85
+
86
+ /**
87
+ * Lower one descriptor file. Memoized per file and options for the life of the
88
+ * process; a file that failed to load is tried again on the next read, as a
89
+ * fresh import would be.
90
+ * @param {string} file absolute path
91
+ * @param {ReadOptions} options
92
+ * @param {{node?: boolean}} [want] also build the sealed node
93
+ * @returns {Promise<import('./compile.mjs').LoweredDoc>}
94
+ */
95
+ export function compileDocFile(file, options, {node = false} = {}) {
96
+ const lang = options.lang ?? null;
97
+ const check = options.check === true;
98
+ const key = JSON.stringify([
99
+ path.resolve(file),
100
+ options.root,
101
+ lang,
102
+ options.label ?? null,
103
+ options.provider ?? null,
104
+ options.id ?? null,
105
+ options.loader ?? 'user',
106
+ options.exports ?? null,
107
+ options.value ?? 'authored',
108
+ check,
109
+ node,
110
+ ]);
111
+ let result = lowered.get(key);
112
+ if (!result) {
113
+ result = loadAuthored(file, options, lang).then(authored => {
114
+ const provider = options.provider ?? packageOf(file);
115
+ const out = lowerDoc(
116
+ {
117
+ id:
118
+ options.id ?? `${provider}:${options.root}:${path.basename(file)}`,
119
+ root: options.root,
120
+ provider,
121
+ source: packageSource(file),
122
+ lang,
123
+ file: authored,
124
+ ...(options.label ? {label: options.label} : {}),
125
+ ...(options.value === 'parsed' ? {useParsed: true} : {}),
126
+ },
127
+ {check, node},
128
+ );
129
+ if (out.node) deepFreeze(out.node);
130
+ if (out.loadFailed || 'overlayError' in authored) {
131
+ lowered.delete(key);
132
+ }
133
+ return out;
134
+ });
135
+ lowered.set(key, result);
136
+ }
137
+ return result;
138
+ }
139
+
140
+ /**
141
+ * What a reader gets for one descriptor: the view it has always read.
142
+ *
143
+ * `strict` readers check the doc and throw what they always threw: the import
144
+ * error, the parser's error (for an empty export too), then a failed
145
+ * translation. Other readers skip the check, throw only what importing or
146
+ * translating threw, and read an empty export as the empty value itself.
147
+ * The view is shared, as the module's own export always was.
148
+ * @param {string} file absolute path
149
+ * @param {ReadOptions & {strict?: boolean}} options
150
+ * @returns {Promise<any>}
151
+ */
152
+ export async function readDocView(file, options) {
153
+ const strict = options.strict === true;
154
+ const lang = options.lang ?? null;
155
+ // Lowered on every read, as the readers always loaded: Node caches the
156
+ // module, and the translation and the check run fresh each time, so a
157
+ // reader never shares a translated or parsed result with the next one.
158
+ const provider = options.provider ?? packageOf(file);
159
+ const result = lowerDoc(
160
+ {
161
+ id: options.id ?? `${provider}:${options.root}:${path.basename(file)}`,
162
+ root: options.root,
163
+ provider,
164
+ source: packageSource(file),
165
+ lang,
166
+ file: await loadAuthored(file, options, lang),
167
+ ...(options.label ? {label: options.label} : {}),
168
+ ...(options.value === 'parsed' ? {useParsed: true} : {}),
169
+ },
170
+ {check: strict || options.check === true, node: false},
171
+ );
172
+ if (result.loadFailed) throw result.loadFailure;
173
+ if (result.missing) {
174
+ if (strict)
175
+ parserFor(options.root)(result.missingValue, options.label ?? file);
176
+ return result.missingValue;
177
+ }
178
+ if (strict && result.failed) throw result.failure;
179
+ if (result.overlayFailed) throw result.overlayFailure;
180
+ return result.view;
181
+ }
182
+
183
+ /**
184
+ * Load one file as its root reads it: the doc its module exports and, for a
185
+ * component or hook, the translation it exports for `lang`.
186
+ * @param {string} file
187
+ * @param {ReadOptions} options
188
+ * @param {string | null} lang
189
+ * @returns {Promise<import('./compile.mjs').AuthoredFile>}
190
+ */
191
+ async function loadAuthored(file, options, lang) {
192
+ const name = path.basename(file);
193
+ if (options.root === 'themes') {
194
+ if (!options.readStatic) {
195
+ throw new Error(
196
+ 'A theme descriptor is read statically; pass readStatic.',
197
+ );
198
+ }
199
+ try {
200
+ return {file: name, doc: options.readStatic()};
201
+ } catch (error) {
202
+ return {file: name, error};
203
+ }
204
+ }
205
+ let mod;
206
+ try {
207
+ mod = await LOADERS[options.loader ?? 'user'](file);
208
+ } catch (error) {
209
+ return {file: name, error};
210
+ }
211
+ // `a ?? b ?? c`, exactly: the first value that is not null or undefined,
212
+ // else the last one.
213
+ const [first, ...rest] = options.exports ?? DOC_EXPORTS[options.root];
214
+ let doc = mod?.[first];
215
+ for (const key of rest) doc = doc ?? mod?.[key];
216
+ const overlay =
217
+ lang && (options.root === 'components' || options.root === 'hooks')
218
+ ? translationFor(mod, lang)
219
+ : null;
220
+ return overlay ? {file: name, doc, overlay} : {file: name, doc};
221
+ }
222
+
223
+ /**
224
+ * Freeze a value and everything in it.
225
+ * @template T
226
+ * @param {T} value
227
+ * @returns {T}
228
+ */
229
+ export function deepFreeze(value) {
230
+ if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
231
+ Object.freeze(value);
232
+ for (const child of Object.values(value)) deepFreeze(child);
233
+ }
234
+ return value;
235
+ }
236
+
237
+ // ── Doc topics ─────────────────────────────────────────────────────────────
238
+
239
+ /** The localized overlays a docs read can apply. */
240
+ export const OVERLAY_LANGUAGES = ['zh', 'dense'];
241
+
242
+ /**
243
+ * Where the `lang` overlay of a doc file lives: `{topic}.doc.{lang}.mjs`.
244
+ * @param {string} docPath
245
+ * @param {string} lang
246
+ * @returns {string}
247
+ */
248
+ export function overlayPath(docPath, lang) {
249
+ return path.join(
250
+ path.dirname(docPath),
251
+ `${path.basename(docPath, '.doc.mjs')}.doc.${lang}.mjs`,
252
+ );
253
+ }
254
+
255
+ /**
256
+ * The overlay languages a topic ships for its own file or any extension.
257
+ * @param {import('../discovery/docs-discovery.mjs').DocsTopicEntry} entry
258
+ * @returns {string[]}
259
+ */
260
+ export function overlayLanguages(entry) {
261
+ const files = [entry.path, ...entry.extensions.map(ext => ext.path)];
262
+ return OVERLAY_LANGUAGES.filter(lang =>
263
+ files.some(file => fs.existsSync(overlayPath(file, lang))),
264
+ );
265
+ }
266
+
267
+ /**
268
+ * Load one topic file and the overlay for `lang`. A failure is recorded on the
269
+ * result, not thrown, so the compiler reports it in reading order.
270
+ * @param {string} docPath
271
+ * @param {string | null} lang
272
+ * @returns {Promise<import('./compile.mjs').AuthoredFile>}
273
+ */
274
+ export async function loadTopicFile(docPath, lang) {
275
+ const file = path.basename(docPath);
276
+ let doc;
277
+ try {
278
+ const mod = await importNativeModule(docPath);
279
+ doc = parseReadableDoc(mod.docs ?? mod.default, file);
280
+ } catch (error) {
281
+ return {file, error};
282
+ }
283
+ if (!lang) return {file, doc};
284
+ const translationPath = overlayPath(docPath, lang);
285
+ if (!fs.existsSync(translationPath)) return {file, doc};
286
+ try {
287
+ const translationMod = await importNativeModule(translationPath);
288
+ return {
289
+ file,
290
+ doc,
291
+ overlay: translationMod.docsZh || translationMod.docsDense || null,
292
+ };
293
+ } catch (overlayError) {
294
+ return {file, doc, overlayError};
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Everything the compiler needs for one topic, read from disk.
300
+ * @param {import('../discovery/docs-discovery.mjs').DocsTopicEntry} entry
301
+ * @param {string | null} lang
302
+ * @returns {Promise<import('./compile.mjs').ReferenceTopicInput>}
303
+ */
304
+ export async function loadTopicInput(entry, lang) {
305
+ const extensions = [];
306
+ for (const extension of entry.extensions) {
307
+ extensions.push({
308
+ ...(await loadTopicFile(extension.path, lang)),
309
+ provider: extension.package,
310
+ });
311
+ }
312
+ return {
313
+ id: entry.name,
314
+ provider: entry.package,
315
+ replaces: entry.replaces ?? null,
316
+ lang,
317
+ base: await loadTopicFile(entry.path, lang),
318
+ extensions,
319
+ };
320
+ }