@astryxdesign/cli 0.6.4-canary.06c8fa3 → 0.6.4-canary.0e1fbdb

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 (254) hide show
  1. package/README.md +49 -40
  2. package/api/build/build.doc.mjs +6 -1
  3. package/api/build/build.test.mjs +22 -0
  4. package/api/build/kit/kit.mjs +44 -5
  5. package/api/component/component.doc.mjs +14 -7
  6. package/api/docs/_adapter.d.mts +8 -3
  7. package/api/docs/_adapter.mjs +14 -6
  8. package/api/docs/docOverlays.test.mjs +27 -1
  9. package/api/docs/docs.doc.mjs +2 -2
  10. package/api/doctor/doctor.doc.mjs +17 -8
  11. package/api/doctor/doctor.type.d.mts +1 -1
  12. package/api/doctor/doctor.type.mjs +1 -1
  13. package/api/gap-report/gap-report.doc.mjs +19 -10
  14. package/api/hook/hook.doc.mjs +6 -3
  15. package/api/index.d.mts +2 -0
  16. package/api/index.mjs +3 -1
  17. package/api/init/init.doc.mjs +17 -12
  18. package/api/integration/add-theme.mjs +22 -1
  19. package/api/integration/add-theme.test.mjs +34 -0
  20. package/api/integration/authoring-checks.mjs +2 -2
  21. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  22. package/api/integration/pack-check.lifecycle-output.test.mjs +2 -0
  23. package/api/integration/pack-check.mjs +54 -6
  24. package/api/integration/pack-check.test.mjs +90 -0
  25. package/api/integration/pack-check.type.mjs +1 -1
  26. package/api/json/assertResponse.doc.mjs +1 -1
  27. package/api/json/index.ts +1 -0
  28. package/api/json/isError.doc.mjs +1 -1
  29. package/api/layout/_adapter.d.mts +34 -0
  30. package/api/layout/_adapter.mjs +148 -0
  31. package/api/layout/check/check.d.mts +16 -0
  32. package/api/layout/check/check.mjs +40 -0
  33. package/api/layout/expand/expand.d.mts +22 -0
  34. package/api/layout/expand/expand.mjs +155 -0
  35. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  36. package/api/layout/grammar/grammar.d.mts +13 -0
  37. package/api/layout/grammar/grammar.mjs +87 -0
  38. package/api/layout/layout.d.mts +6 -0
  39. package/api/layout/layout.mjs +17 -0
  40. package/api/layout/layout.test.mjs +297 -0
  41. package/api/layout/layout.type.d.mts +89 -0
  42. package/api/layout/layout.type.mjs +103 -0
  43. package/api/layout/layoutCheck.doc.d.mts +11 -0
  44. package/api/layout/layoutCheck.doc.mjs +85 -0
  45. package/api/layout/layoutExpand.doc.d.mts +11 -0
  46. package/api/layout/layoutExpand.doc.mjs +107 -0
  47. package/api/layout/layoutGrammar.doc.d.mts +11 -0
  48. package/api/layout/layoutGrammar.doc.mjs +57 -0
  49. package/api/search/search.d.mts +27 -1
  50. package/api/search/search.doc.mjs +2 -2
  51. package/api/search/search.mjs +228 -16
  52. package/api/swizzle/swizzle.doc.mjs +7 -5
  53. package/api/template/copy/copy.mjs +1 -1
  54. package/api/template/copy/copy.test.mjs +9 -0
  55. package/api/template/template-integration.test.mjs +65 -1
  56. package/api/template/template.doc.mjs +2 -1
  57. package/api/template/template.mjs +1 -1
  58. package/api/theme/generateTonalPalette.doc.mjs +1 -2
  59. package/api/theme/listThemes.doc.mjs +1 -1
  60. package/api/theme/themeAdd.doc.mjs +9 -10
  61. package/api/theme/themeBuild.doc.mjs +13 -13
  62. package/api/theme/themeList.doc.mjs +1 -1
  63. package/api/theme/themeListAvailable.doc.mjs +2 -1
  64. package/api/theme/themePaletteGenerate.doc.mjs +15 -8
  65. package/api/theme/themeTargets.doc.mjs +3 -2
  66. package/api/theme/themeTemplate.doc.mjs +2 -1
  67. package/api/upgrade/run/run.mjs +1 -1
  68. package/api/upgrade/upgrade.doc.mjs +24 -22
  69. package/assets/docs/README.md +4 -2
  70. package/assets/docs/browser-support.doc.mjs +11 -11
  71. package/assets/docs/color.doc.mjs +8 -2
  72. package/assets/docs/elevation.doc.mjs +6 -4
  73. package/assets/docs/getting-started.doc.mjs +5 -16
  74. package/assets/docs/icons.doc.mjs +2 -21
  75. package/assets/docs/illustrations.doc.mjs +7 -15
  76. package/assets/docs/layout.doc.dense.mjs +130 -82
  77. package/assets/docs/layout.doc.mjs +133 -77
  78. package/assets/docs/migration.doc.mjs +19 -21
  79. package/assets/docs/motion.doc.mjs +16 -3
  80. package/assets/docs/principles.doc.dense.mjs +5 -5
  81. package/assets/docs/principles.doc.mjs +8 -0
  82. package/assets/docs/principles.doc.zh.mjs +6 -6
  83. package/assets/docs/shape.doc.mjs +8 -3
  84. package/assets/docs/spacing.doc.mjs +7 -2
  85. package/assets/docs/styling-libraries.doc.mjs +6 -2
  86. package/assets/docs/styling.doc.mjs +19 -23
  87. package/assets/docs/theme.doc.dense.mjs +58 -18
  88. package/assets/docs/theme.doc.mjs +56 -46
  89. package/assets/docs/theme.doc.zh.mjs +9 -8
  90. package/assets/docs/tokens.doc.dense.mjs +2 -2
  91. package/assets/docs/tokens.doc.mjs +389 -8
  92. package/assets/docs/tokens.doc.zh.mjs +2 -2
  93. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  94. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  95. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  96. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  97. package/assets/docs/tree/block-template.doc.mjs +130 -0
  98. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  99. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  100. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  101. package/assets/docs/tree/checks.doc.mjs +119 -0
  102. package/assets/docs/tree/codemods.doc.mjs +147 -0
  103. package/assets/docs/tree/component-family.doc.mjs +113 -0
  104. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  105. package/assets/docs/tree/components.doc.mjs +23 -0
  106. package/assets/docs/tree/configuration.doc.mjs +23 -0
  107. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  108. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  109. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  110. package/assets/docs/tree/docs.doc.mjs +21 -0
  111. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  112. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  113. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  114. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  115. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  116. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  117. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  118. package/assets/docs/tree/help.doc.mjs +16 -0
  119. package/assets/docs/tree/integrations.doc.mjs +25 -470
  120. package/assets/docs/tree/links.doc.mjs +98 -0
  121. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  122. package/assets/docs/tree/page-template.doc.mjs +71 -0
  123. package/assets/docs/tree/publishing.doc.mjs +111 -0
  124. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  125. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  126. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  127. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  128. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  129. package/assets/docs/tree/ship.doc.mjs +16 -0
  130. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  131. package/assets/docs/tree/single-component.doc.mjs +165 -0
  132. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  133. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  134. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  135. package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
  136. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  137. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  138. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  139. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  140. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  141. package/assets/docs/tree/templates.doc.mjs +34 -0
  142. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  143. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  144. package/assets/docs/tree/themes.doc.mjs +39 -0
  145. package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
  146. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  147. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  148. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  149. package/assets/docs/tree/versioning.doc.mjs +161 -0
  150. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  151. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  152. package/assets/docs/typography.doc.mjs +24 -4
  153. package/assets/docs/working-with-ai.doc.mjs +30 -22
  154. package/authoring/config/config.doc.mjs +2 -2
  155. package/authoring/config/type.ts +2 -2
  156. package/authoring/doctypes/_schema.d.mts +3 -2
  157. package/authoring/doctypes/_schema.mjs +6 -0
  158. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  159. package/authoring/doctypes/base/type.ts +4 -2
  160. package/authoring/doctypes/command/command.doc.mjs +1 -1
  161. package/authoring/doctypes/command/type.ts +1 -1
  162. package/authoring/doctypes/component/component.doc.mjs +6 -0
  163. package/authoring/doctypes/component/type.ts +8 -0
  164. package/authoring/doctypes/reference/reference.doc.mjs +7 -0
  165. package/authoring/doctypes/reference/type.ts +5 -0
  166. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  167. package/authoring/doctypes/template/template.doc.mjs +1 -1
  168. package/authoring/doctypes/template/type.ts +2 -2
  169. package/authoring/integration/integration.doc.mjs +12 -10
  170. package/clients/cli/command-result-coverage.test.mjs +7 -7
  171. package/clients/cli/commands/component.doc.mjs +4 -3
  172. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  173. package/clients/cli/commands/docs.doc.mjs +1 -1
  174. package/clients/cli/commands/docs.mjs +60 -17
  175. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
  176. package/clients/cli/commands/doctor-integration.test.mjs +53 -0
  177. package/clients/cli/commands/doctor.doc.mjs +3 -1
  178. package/clients/cli/commands/doctor.mjs +49 -5
  179. package/clients/cli/commands/gap-report.doc.mjs +10 -9
  180. package/clients/cli/commands/init.doc.mjs +9 -6
  181. package/clients/cli/commands/integration-add.doc.mjs +9 -9
  182. package/clients/cli/commands/integration-authoring.test.mjs +61 -10
  183. package/clients/cli/commands/integration-pack.doc.mjs +5 -9
  184. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  185. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  186. package/clients/cli/commands/integration.doc.mjs +4 -4
  187. package/clients/cli/commands/integration.mjs +74 -43
  188. package/clients/cli/commands/layout-check.doc.mjs +65 -0
  189. package/clients/cli/commands/layout-expand.doc.mjs +83 -0
  190. package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
  191. package/clients/cli/commands/layout.doc.mjs +34 -0
  192. package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
  193. package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
  194. package/clients/cli/commands/layout.mjs +275 -0
  195. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  196. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  197. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  198. package/clients/cli/commands/manifest.doc.mjs +1 -1
  199. package/clients/cli/commands/search.doc.mjs +10 -3
  200. package/clients/cli/commands/search.mjs +21 -2
  201. package/clients/cli/commands/search.test.mjs +21 -4
  202. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  203. package/clients/cli/commands/template.doc.mjs +1 -1
  204. package/clients/cli/commands/text-json-parity.test.mjs +24 -1
  205. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  206. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
  207. package/clients/cli/commands/theme-palette.doc.mjs +1 -2
  208. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  209. package/clients/cli/commands/theme.doc.mjs +2 -1
  210. package/clients/cli/commands/upgrade.doc.mjs +62 -3
  211. package/clients/cli/index.mjs +32 -6
  212. package/clients/cli/lib/define-command.mjs +28 -4
  213. package/clients/cli/lib/define-command.test.mjs +54 -0
  214. package/clients/cli/lib/exit-codes.test.mjs +25 -2
  215. package/clients/cli/lib/json-shim.test.mjs +20 -6
  216. package/clients/cli/lib/manifest.mjs +23 -5
  217. package/foundation/agent-docs/agent-docs.mjs +1 -1
  218. package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
  219. package/foundation/discovery/cli-self-docs.mjs +16 -2
  220. package/foundation/discovery/cli-self-docs.test.mjs +20 -0
  221. package/foundation/discovery/docs-discovery.mjs +5 -1
  222. package/foundation/discovery/docs-discovery.test.mjs +21 -0
  223. package/foundation/discovery/docs-section-key.d.mts +1 -1
  224. package/foundation/discovery/docs-section-key.mjs +1 -1
  225. package/foundation/discovery/template-adapter.mjs +1 -1
  226. package/foundation/doc-compiler/doc-loads.test.mjs +15 -2
  227. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  228. package/foundation/doc-compiler/tree.d.mts +4 -0
  229. package/foundation/doc-compiler/tree.mjs +6 -1
  230. package/foundation/integrations/cli-requirement.d.mts +26 -6
  231. package/foundation/integrations/cli-requirement.mjs +46 -11
  232. package/foundation/integrations/cli-requirement.test.mjs +7 -2
  233. package/foundation/integrations/contribution-inventory.mjs +1 -1
  234. package/foundation/response/error-codes.doc.mjs +6 -8
  235. package/foundation/response/error-codes.test.mjs +30 -5
  236. package/foundation/response/response-types.doc.d.mts +4 -3
  237. package/foundation/response/response-types.doc.mjs +42 -6
  238. package/foundation/response/response.doc.mjs +11 -10
  239. package/foundation/xle/browser.d.mts +3 -3
  240. package/foundation/xle/browser.mjs +3 -3
  241. package/foundation/xle/expand.mjs +2 -2
  242. package/foundation/xle/parse.mjs +1 -1
  243. package/foundation/xle/print.mjs +2 -2
  244. package/foundation/xle/splice.mjs +1 -1
  245. package/package.json +9 -9
  246. package/api/docs/docs.test.mjs +0 -245
  247. package/api/docs/integration-tree.test.mjs +0 -555
  248. package/api/docs/integrationDocs.test.mjs +0 -314
  249. package/api/search/search.test.mjs +0 -530
  250. package/assets/docs/tree/integrations.test.mjs +0 -62
  251. package/assets/docs/tree/writing-docs.doc.mjs +0 -286
  252. package/clients/cli/commands/docs.test.mjs +0 -323
  253. package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
  254. package/foundation/doc-compiler/tree.test.mjs +0 -606
@@ -1,606 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- /**
4
- * @file Tests for the docs tree (spec:AST-046): one home per doc, decided by
5
- * explicit placement, then one adoption rule; every way a placement can fail;
6
- * generated kind levels; deterministic order; and the CLI's own tree.
7
- *
8
- * @input Plain fixture inputs for the pure builder, and this package's own
9
- * tree files and typed docs for the loader.
10
- * @output Assertions on routes, parents, slots, children order, and
11
- * diagnostics.
12
- * @position packages/cli/foundation/doc-compiler — tests for tree.mjs.
13
- */
14
-
15
- import {describe, expect, it} from 'vitest';
16
- import {
17
- buildDocsTree,
18
- KIND_GROUPS,
19
- loadDocsTree,
20
- loadTreeInputs,
21
- routeSegment,
22
- } from './tree.mjs';
23
- import {loadCliSelfDocs} from '../discovery/cli-self-docs.mjs';
24
- import * as api from '../../api/index.mjs';
25
- import {createDocId} from '../identity/provider-identity.mjs';
26
- import {CLI_PROVIDER_ID} from '../identity/providers.mjs';
27
-
28
- const SLOW = 60_000;
29
- const P = '@acme/kit';
30
-
31
- /**
32
- * @param {string} name
33
- * @param {object} [extra]
34
- * @returns {import('./tree.mjs').TreeNamespaceInput}
35
- */
36
- const ns = (name, extra = {}) => ({
37
- provider: P,
38
- providerId: P,
39
- source: `${P}/tree/${name}.doc.mjs`,
40
- doc: /** @type {any} */ ({
41
- type: 'namespace',
42
- name,
43
- title: name.toUpperCase(),
44
- summary: `The ${name} level.`,
45
- slots: {
46
- items: {title: 'Items', accepts: {kinds: ['generic', 'namespace']}},
47
- },
48
- ...extra,
49
- }),
50
- });
51
-
52
- /**
53
- * @param {string} name
54
- * @param {object} [extra]
55
- * @returns {import('./tree.mjs').TreeDocInput}
56
- */
57
- const guide = (name, extra = {}) => ({
58
- provider: P,
59
- providerId: P,
60
- source: `${P}/tree/${name}.doc.mjs`,
61
- kind: 'generic',
62
- name,
63
- title: `Guide ${name}`,
64
- summary: `About ${name}.`,
65
- group: null,
66
- placement: undefined,
67
- ...extra,
68
- });
69
-
70
- /**
71
- * @param {string} kind
72
- * @param {string} name
73
- * @param {string} group
74
- * @returns {import('./tree.mjs').TreeDocInput}
75
- */
76
- const typed = (kind, name, group) => ({
77
- provider: P,
78
- providerId: P,
79
- source: `${P}/api/${name}.doc.mjs`,
80
- kind,
81
- name,
82
- title: name,
83
- summary: `The ${name} ${kind}.`,
84
- group,
85
- placement: undefined,
86
- });
87
-
88
- /** @param {ReturnType<typeof buildDocsTree>} tree */
89
- const routes = tree => [...tree.nodes.keys()];
90
-
91
- /** @param {ReturnType<typeof buildDocsTree>} tree */
92
- const problems = tree => tree.diagnostics.map(d => [d.code, d.message]);
93
-
94
- describe('buildDocsTree', () => {
95
-
96
- it('withdraws a namespace placed in a withdrawn namespace, and what is placed in it', () => {
97
- const CLI = '@astryxdesign/cli';
98
- const tree = buildDocsTree({
99
- namespaces: [
100
- ns('tokens'),
101
- ns('alpha', {placement: {parent: 'namespace:tokens', slot: 'items'}}),
102
- ],
103
- docs: [guide('deep', {placement: {parent: 'namespace:alpha', slot: 'items'}})],
104
- topics: [
105
- {provider: CLI, providerId: CLI, name: 'tokens', title: 'Tokens', summary: 'Tokens.', source: `${CLI}/tokens.doc.mjs`},
106
- ],
107
- });
108
- expect(tree.get('tokens')?.provider).toBe(CLI);
109
- expect(routes(tree).filter(route => route.startsWith('tokens/'))).toEqual([]);
110
- expect(problems(tree)).toEqual(
111
- expect.arrayContaining([
112
- ['duplicate_route', expect.stringContaining('takes the route "tokens"')],
113
- ['invalid_placement', 'Namespace "alpha" has no route: the namespace it is placed in was withdrawn.'],
114
- ['invalid_placement', expect.stringContaining('names a namespace that has no route')],
115
- ]),
116
- );
117
- });
118
-
119
- it("keeps a name a topic answers to through replaces as that topic's route", () => {
120
- const tree = buildDocsTree({
121
- namespaces: [ns('tokens')],
122
- docs: [],
123
- topics: [
124
- {provider: '@acme/a', providerId: '@acme/a', name: 'acme-tokens', title: 'Acme tokens', summary: 'Acme tokens.', source: '@acme/a:acme-tokens', aliases: ['tokens', 'old-tokens']},
125
- ],
126
- });
127
- expect(tree.get('tokens')).toBeUndefined();
128
- expect(tree.get('acme-tokens')?.provider).toBe('@acme/a');
129
- expect(problems(tree)).toEqual([
130
- ['duplicate_route', expect.stringContaining('takes the route "tokens", which the topic "acme-tokens" also answers to')],
131
- ]);
132
- // Every name it answers to is reserved, not only the last one it replaced.
133
- const older = buildDocsTree({
134
- namespaces: [ns('old-tokens')],
135
- docs: [],
136
- topics: [
137
- {provider: '@acme/a', providerId: '@acme/a', name: 'acme-tokens', title: 'Acme tokens', summary: 'Acme tokens.', source: '@acme/a:acme-tokens', aliases: ['tokens', 'old-tokens']},
138
- ],
139
- });
140
- expect(older.get('old-tokens')).toBeUndefined();
141
- });
142
-
143
- it("keeps the CLI's own routes from an integration, compared without case", () => {
144
- const CLI = '@astryxdesign/cli';
145
- /** @param {string} name @param {string} [provider] */
146
- const topic = (name, provider = P) => ({
147
- provider,
148
- providerId: provider,
149
- name,
150
- title: name,
151
- summary: `The ${name} topic.`,
152
- source: `${provider}/${name}.doc.mjs`,
153
- });
154
- const cliNs = {...ns('cli'), provider: CLI, providerId: CLI, source: `${CLI}/tree/cli.doc.mjs`};
155
- // An integration's topics named `CLI` and `UNORGANIZED` claim the CLI's
156
- // namespace and the generated level; both are withdrawn.
157
- const tree = buildDocsTree({
158
- namespaces: [cliNs],
159
- docs: [],
160
- topics: [topic('theme', CLI), topic('CLI'), topic('UNORGANIZED')],
161
- });
162
- expect(tree.get('cli')?.provider).toBe(CLI);
163
- expect(tree.get('unorganized')?.provider).toBe(CLI);
164
- expect(routes(tree)).not.toContain('CLI');
165
- expect(routes(tree)).not.toContain('UNORGANIZED');
166
- expect(problems(tree)).toEqual(
167
- expect.arrayContaining([
168
- ['duplicate_route', expect.stringContaining('both have the route "CLI"')],
169
- ['duplicate_route', expect.stringContaining('takes the route "UNORGANIZED", which the CLI\'s own docs keep')],
170
- ]),
171
- );
172
- expect(tree.getFolded('THEME')?.provider).toBe(CLI);
173
- // A CLI topic whose own name has capitals keeps its route against a
174
- // lowercase namespace, although namespaces go into the tree first.
175
- const caps = buildDocsTree({namespaces: [ns('tokens')], docs: [], topics: [topic('Tokens', CLI)]});
176
- expect(caps.get('Tokens')?.provider).toBe(CLI);
177
- expect(caps.get('tokens')).toBeUndefined();
178
- expect(problems(caps)).toEqual([
179
- ['duplicate_route', expect.stringContaining('takes the route "tokens", which the CLI\'s own docs keep')],
180
- ]);
181
- });
182
- it('builds three authored levels and a guide below them', () => {
183
- const tree = buildDocsTree({
184
- namespaces: [
185
- ns('top'),
186
- ns('middle', {placement: {parent: 'namespace:top', slot: 'items'}}),
187
- ns('bottom', {placement: {parent: 'namespace:middle'}}),
188
- ],
189
- docs: [
190
- guide('deep', {placement: {parent: 'namespace:bottom', order: 1}}),
191
- ],
192
- });
193
- expect(problems(tree)).toEqual([]);
194
- expect(routes(tree)).toEqual([
195
- 'top',
196
- 'top/middle',
197
- 'top/middle/bottom',
198
- 'top/middle/bottom/deep',
199
- ]);
200
- const deep = /** @type {any} */ (tree.get('top/middle/bottom/deep'));
201
- expect(deep).toMatchObject({
202
- id: 'astryx:artifact:v1/%40acme%2Fkit/generic/deep',
203
- parent: 'top/middle/bottom',
204
- slot: 'items',
205
- order: 1,
206
- generated: false,
207
- });
208
- expect(tree.ancestors(deep).map(node => node.route)).toEqual([
209
- 'top',
210
- 'top/middle',
211
- 'top/middle/bottom',
212
- ]);
213
- expect(tree.roots().map(node => node.route)).toEqual(['top']);
214
- });
215
-
216
- it('adopts typed docs by group, with one generated level per kind', () => {
217
- const tree = buildDocsTree({
218
- namespaces: [
219
- ns('api', {
220
- slots: {kinds: {title: 'Reference', accepts: {kinds: ['namespace']}}},
221
- adopts: [
222
- {
223
- source: {group: 'kit/api', kinds: ['function', 'enum']},
224
- into: 'kinds',
225
- groupBy: 'kind',
226
- },
227
- ],
228
- }),
229
- ],
230
- docs: [
231
- typed('enum', 'errorCodes', 'kit/api'),
232
- typed('function', 'search', 'kit/api'),
233
- typed('function', 'build', 'kit/api'),
234
- typed('schema', 'config', 'kit/api'),
235
- typed('function', 'other', 'kit/elsewhere'),
236
- ],
237
- });
238
- expect(problems(tree)).toEqual([]);
239
- expect(routes(tree)).toEqual([
240
- 'api',
241
- 'api/enums',
242
- 'api/enums/error-codes',
243
- 'api/functions',
244
- 'api/functions/build',
245
- 'api/functions/search',
246
- ]);
247
- const functions = /** @type {any} */ (tree.get('api/functions'));
248
- expect(functions).toMatchObject({
249
- id: null,
250
- kind: 'namespace',
251
- title: KIND_GROUPS.function.title,
252
- generated: true,
253
- parent: 'api',
254
- slot: 'kinds',
255
- });
256
- // Kind levels follow the rule's kind order; docs inside sort by title.
257
- expect(tree.get('api')?.slots[0].children).toEqual([
258
- 'api/functions',
259
- 'api/enums',
260
- ]);
261
- expect(functions.slots[0].children).toEqual([
262
- 'api/functions/build',
263
- 'api/functions/search',
264
- ]);
265
- });
266
-
267
- it('adopts without grouping straight into the named slot', () => {
268
- const tree = buildDocsTree({
269
- namespaces: [
270
- ns('commands', {
271
- slots: {all: {title: 'All', accepts: {kinds: ['command']}}},
272
- adopts: [{source: {group: 'kit/commands'}, into: 'all'}],
273
- }),
274
- ],
275
- docs: [typed('command', 'theme add', 'kit/commands')],
276
- });
277
- expect(routes(tree)).toEqual(['commands', 'commands/theme-add']);
278
- expect(tree.get('commands')?.slots[0].children).toEqual([
279
- 'commands/theme-add',
280
- ]);
281
- });
282
-
283
- it('prefers explicit placement over adoption, and never falls back', () => {
284
- const namespaces = [
285
- ns('guides'),
286
- ns('api', {
287
- slots: {all: {title: 'All', accepts: {kinds: ['generic']}}},
288
- adopts: [{source: {group: 'kit/api'}, into: 'all'}],
289
- }),
290
- ];
291
- const placed = buildDocsTree({
292
- namespaces,
293
- docs: [
294
- guide('intro', {
295
- group: 'kit/api',
296
- placement: {parent: 'namespace:guides'},
297
- }),
298
- ],
299
- });
300
- expect(routes(placed)).toContain('guides/intro');
301
- expect(routes(placed)).not.toContain('api/intro');
302
-
303
- const broken = buildDocsTree({
304
- namespaces,
305
- docs: [
306
- guide('intro', {
307
- group: 'kit/api',
308
- placement: {parent: 'namespace:nope'},
309
- }),
310
- ],
311
- });
312
- expect(routes(broken).some(route => route.endsWith('/intro'))).toBe(false);
313
- expect(problems(broken)).toEqual([
314
- [
315
- 'invalid_placement',
316
- 'placement.parent "namespace:nope" names no namespace; @acme/kit declares "api", "guides".',
317
- ],
318
- ]);
319
- });
320
-
321
- it('reports every way a placement can fail', () => {
322
- const tree = buildDocsTree({
323
- namespaces: [
324
- ns('home', {
325
- slots: {
326
- one: {title: 'One', accepts: {kinds: ['generic']}},
327
- two: {title: 'Two', accepts: {kinds: ['command']}},
328
- },
329
- }),
330
- ],
331
- docs: [
332
- guide('no-slot', {placement: {parent: 'namespace:home'}}),
333
- guide('bad-slot', {
334
- placement: {parent: 'namespace:home', slot: 'three'},
335
- }),
336
- guide('wrong-kind', {
337
- placement: {parent: 'namespace:home', slot: 'two'},
338
- }),
339
- guide('other-package', {
340
- placement: {parent: '@other/pkg/namespace/home'},
341
- }),
342
- guide('not-a-ref', {placement: {parent: 'home'}}),
343
- ],
344
- });
345
- expect(routes(tree)).toEqual(['home']);
346
- expect(
347
- problems(tree)
348
- .map(([, message]) => message)
349
- .sort(),
350
- ).toEqual(
351
- [
352
- 'placement names no slot, and namespace "home" has 2 (one, two). Name one with placement.slot.',
353
- 'placement.slot "three" is not a slot of namespace "home"; it declares one, two.',
354
- 'slot "two" of namespace "home" does not accept generic docs; it accepts command.',
355
- 'placement.parent "@other/pkg/namespace/home" belongs to @other/pkg. A doc can only be placed in a namespace of its own package (@acme/kit).',
356
- 'placement.parent "home" is not a namespace reference. Write "namespace:<name>".',
357
- ].sort(),
358
- );
359
- expect(tree.diagnostics.every(d => d.code === 'invalid_placement')).toBe(
360
- true,
361
- );
362
- expect(tree.diagnostics.every(d => d.severity === 'error')).toBe(true);
363
- });
364
-
365
- it('fails a doc two namespaces adopt, naming both', () => {
366
- const rule = {source: {group: 'kit/api'}, into: 'items'};
367
- const tree = buildDocsTree({
368
- namespaces: [ns('a', {adopts: [rule]}), ns('b', {adopts: [rule]})],
369
- docs: [guide('shared', {group: 'kit/api'})],
370
- });
371
- expect(routes(tree)).toEqual(['a', 'b']);
372
- expect(problems(tree)).toEqual([
373
- [
374
- 'overlapping_adoption',
375
- '@acme/kit/generic/shared is adopted by "a" and "b"; exactly one namespace may adopt a doc.',
376
- ],
377
- ]);
378
- });
379
-
380
- it('fails two docs at one route, keeping the first by identity', () => {
381
- const tree = buildDocsTree({
382
- namespaces: [ns('home')],
383
- docs: [
384
- guide('fooBar', {placement: {parent: 'namespace:home'}}),
385
- guide('foo_bar', {placement: {parent: 'namespace:home'}}),
386
- ],
387
- });
388
- expect(routes(tree)).toEqual(['home', 'home/foo-bar']);
389
- expect(tree.get('home/foo-bar')?.id).toBe(
390
- 'astryx:artifact:v1/%40acme%2Fkit/generic/fooBar',
391
- );
392
- expect(problems(tree)).toEqual([
393
- [
394
- 'duplicate_route',
395
- 'astryx:artifact:v1/%40acme%2Fkit/generic/foo_bar and astryx:artifact:v1/%40acme%2Fkit/generic/fooBar both have the route "home/foo-bar". Rename or move one of them.',
396
- ],
397
- ]);
398
- });
399
-
400
- it('fails a namespace cycle and everything under it', () => {
401
- const tree = buildDocsTree({
402
- namespaces: [
403
- ns('a', {placement: {parent: 'namespace:b'}}),
404
- ns('b', {placement: {parent: 'namespace:a'}}),
405
- ],
406
- docs: [guide('lost', {placement: {parent: 'namespace:a'}})],
407
- });
408
- expect(routes(tree)).toEqual([]);
409
- expect(problems(tree).map(([code]) => code)).toEqual([
410
- 'invalid_placement',
411
- 'invalid_placement',
412
- 'invalid_placement',
413
- ]);
414
- });
415
-
416
- it('fails a duplicate or unsafe namespace name', () => {
417
- const tree = buildDocsTree({
418
- namespaces: [
419
- ns('twice'),
420
- {...ns('twice'), source: `${P}/other.doc.mjs`},
421
- ns('Bad Name'),
422
- ],
423
- docs: [],
424
- });
425
- expect(routes(tree)).toEqual(['twice']);
426
- expect(tree.diagnostics.map(d => d.code)).toEqual([
427
- 'invalid_namespace',
428
- 'invalid_namespace',
429
- ]);
430
- });
431
-
432
- it('leaves a doc with no placement and no adoption out, in phase 1', () => {
433
- const tree = buildDocsTree({
434
- namespaces: [ns('home')],
435
- docs: [guide('loose'), typed('function', 'free', 'kit/nowhere')],
436
- });
437
- expect(routes(tree)).toEqual(['home']);
438
- expect(tree.diagnostics).toEqual([]);
439
- });
440
-
441
- it('is the same tree whatever order its inputs arrive in', () => {
442
- const namespaces = [
443
- ns('top'),
444
- ns('child', {placement: {parent: 'namespace:top'}}),
445
- ];
446
- const docs = [
447
- guide('b', {placement: {parent: 'namespace:child'}}),
448
- guide('a', {placement: {parent: 'namespace:child'}}),
449
- ];
450
- const one = buildDocsTree({namespaces, docs});
451
- const two = buildDocsTree({
452
- namespaces: [...namespaces].reverse(),
453
- docs: [...docs].reverse(),
454
- });
455
- expect(JSON.stringify([...two.nodes])).toBe(JSON.stringify([...one.nodes]));
456
- });
457
-
458
- it('orders children by order, then title', () => {
459
- const tree = buildDocsTree({
460
- namespaces: [ns('home')],
461
- docs: [
462
- guide('z', {
463
- title: 'Zed',
464
- placement: {parent: 'namespace:home', order: 1},
465
- }),
466
- guide('b', {title: 'Beta', placement: {parent: 'namespace:home'}}),
467
- guide('a', {title: 'Alpha', placement: {parent: 'namespace:home'}}),
468
- ],
469
- });
470
- expect(tree.get('home')?.slots[0].children).toEqual([
471
- 'home/z',
472
- 'home/a',
473
- 'home/b',
474
- ]);
475
- });
476
- });
477
-
478
- describe('routeSegment', () => {
479
- it('joins lowercase words with hyphens, whatever the name looks like', () => {
480
- expect(routeSegment('integrationPackCheck')).toBe('integration-pack-check');
481
- expect(routeSegment('doctor integration validate')).toBe(
482
- 'doctor-integration-validate',
483
- );
484
- expect(routeSegment('error-codes')).toBe('error-codes');
485
- expect(routeSegment('isError')).toBe('is-error');
486
- expect(routeSegment('!!')).toBe('');
487
- });
488
- });
489
-
490
- describe("the CLI's own docs tree", () => {
491
- it(
492
- 'builds with no diagnostic, rooted at cli',
493
- async () => {
494
- const tree = await loadDocsTree({fresh: true});
495
- expect(tree.diagnostics).toEqual([]);
496
- expect(tree.roots().map(node => node.route)).toEqual(['cli']);
497
- expect(
498
- tree.get('cli')?.slots.map(slot => [slot.name, slot.children]),
499
- ).toEqual([
500
- [
501
- 'guides',
502
- [
503
- 'cli/component-lookups',
504
- 'cli/integrations',
505
- 'cli/writing-docs',
506
- ],
507
- ],
508
- ['reference', ['cli/commands', 'cli/api']],
509
- ]);
510
- expect(tree.get('cli/api')?.slots[0].children).toEqual([
511
- 'cli/api/functions',
512
- 'cli/api/schemas',
513
- 'cli/api/enums',
514
- ]);
515
- },
516
- SLOW,
517
- );
518
-
519
- it(
520
- 'gives every CLI typed doc in a cli group one route, by its kind',
521
- async () => {
522
- const tree = await loadDocsTree();
523
- const {loaded} = await loadCliSelfDocs();
524
- const inTree = loaded.filter(({doc}) =>
525
- String(doc.namespace).startsWith('cli/'),
526
- );
527
- expect(inTree.length).toBeGreaterThan(0);
528
- const expected = inTree.map(({doc}) =>
529
- doc.type === 'command'
530
- ? `cli/commands/${routeSegment(doc.name)}`
531
- : `cli/api/${KIND_GROUPS[doc.type].segment}/${routeSegment(doc.name)}`,
532
- );
533
- expect(expected.filter(route => tree.get(route) == null)).toEqual([]);
534
- // A doc in the authoring group stays a section of `astryx docs authoring`.
535
- const authoring = loaded.filter(({doc}) => doc.namespace === 'authoring');
536
- expect(authoring.length).toBeGreaterThan(0);
537
- const ids = new Set([...tree.nodes.values()].map(node => node.id));
538
- expect(
539
- authoring.filter(({doc}) =>
540
- ids.has(createDocId(CLI_PROVIDER_ID, doc.type, doc.name)),
541
- ),
542
- ).toEqual([]);
543
- },
544
- SLOW,
545
- );
546
-
547
- it(
548
- 'gives every function @astryxdesign/cli/api exports a route',
549
- async () => {
550
- const tree = await loadDocsTree();
551
- const exported = Object.entries(api)
552
- .filter(
553
- ([, value]) =>
554
- typeof value === 'function' &&
555
- Object.getOwnPropertyDescriptor(value, 'prototype')?.writable !==
556
- false,
557
- )
558
- .map(([name]) => `cli/api/functions/${routeSegment(name)}`);
559
- expect(exported.length).toBeGreaterThan(0);
560
- expect(exported.filter(route => tree.get(route) == null)).toEqual([]);
561
- },
562
- SLOW,
563
- );
564
-
565
- it(
566
- 'reads only namespace and generic docs from the tree directory, each named after its file',
567
- async () => {
568
- const inputs = await loadTreeInputs({selfDocs: false});
569
- expect(inputs.diagnostics).toEqual([]);
570
- expect(inputs.namespaces.map(n => n.doc.name).sort()).toEqual([
571
- 'api',
572
- 'cli',
573
- 'commands',
574
- ]);
575
- expect(inputs.docs.map(d => [d.name, d.placement?.parent])).toEqual([
576
- ['component-lookups', 'namespace:cli'],
577
- ['integrations', 'namespace:cli'],
578
- ['writing-docs', 'namespace:cli'],
579
- ]);
580
- },
581
- SLOW,
582
- );
583
- });
584
-
585
- describe('doc identity', () => {
586
- it('builds each id from the provider id, never the package name', () => {
587
- const provider = '@acme/tree-provider';
588
- const tree = buildDocsTree({
589
- namespaces: [{...ns('home'), providerId: provider}],
590
- docs: [
591
- {
592
- ...guide('intro', {placement: {parent: 'namespace:home'}}),
593
- providerId: provider,
594
- },
595
- ],
596
- });
597
- expect(problems(tree)).toEqual([]);
598
- expect(tree.get('home')?.id).toBe(
599
- 'astryx:artifact:v1/%40acme%2Ftree-provider/namespace/home',
600
- );
601
- expect(tree.get('home/intro')).toMatchObject({
602
- id: 'astryx:artifact:v1/%40acme%2Ftree-provider/generic/intro',
603
- provider: P,
604
- });
605
- });
606
- });