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

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 (236) hide show
  1. package/README.md +20 -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/docs.doc.mjs +1 -0
  9. package/api/docs/list/list.mjs +3 -7
  10. package/api/doctor/doctor.d.mts +25 -3
  11. package/api/doctor/doctor.doc.mjs +1 -0
  12. package/api/doctor/doctor.mjs +185 -13
  13. package/api/doctor/doctor.test.mjs +326 -1
  14. package/api/gap-report/gap-report.doc.mjs +1 -0
  15. package/api/hook/_adapter.mjs +19 -5
  16. package/api/hook/hook.doc.mjs +1 -0
  17. package/api/hook/list/list.d.mts +1 -1
  18. package/api/hook/list/list.mjs +69 -17
  19. package/api/init/init.doc.mjs +1 -0
  20. package/api/integration/add-contribution.mjs +7 -7
  21. package/api/integration/add-contribution.test.mjs +37 -3
  22. package/api/integration/add-theme.mjs +34 -64
  23. package/api/integration/add-theme.test.mjs +105 -21
  24. package/api/integration/authoring-checks.test.mjs +8 -4
  25. package/api/integration/integration-block-exports.test.mjs +10 -6
  26. package/api/integration/integrationAdd.doc.mjs +1 -0
  27. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  28. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  29. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  30. package/api/integration/integrationAddDoc.doc.mjs +1 -0
  31. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  32. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  33. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  34. package/api/integration/integrationDocConflicts.doc.mjs +1 -0
  35. package/api/integration/integrationPackCheck.doc.mjs +1 -0
  36. package/api/integration/integrationTemplateConflicts.doc.mjs +1 -0
  37. package/api/integration/pack-check.test.mjs +17 -47
  38. package/api/integration/summarizeIssues.doc.mjs +1 -0
  39. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  40. package/api/integration/validate-integration.mjs +50 -102
  41. package/api/integration/validate-integration.test.mjs +85 -23
  42. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  43. package/api/integration/validateIntegration.doc.mjs +1 -0
  44. package/api/json/assertResponse.doc.mjs +1 -0
  45. package/api/json/isError.doc.mjs +1 -0
  46. package/api/json/parseResponse.doc.mjs +1 -0
  47. package/api/layout/layoutCheck.doc.mjs +1 -0
  48. package/api/layout/layoutExpand.doc.mjs +1 -0
  49. package/api/layout/layoutGrammar.doc.mjs +1 -0
  50. package/api/search/search.doc.mjs +1 -0
  51. package/api/search/search.mjs +16 -6
  52. package/api/swizzle/swizzle.doc.mjs +1 -0
  53. package/api/template/template-suffix.test.mjs +41 -21
  54. package/api/template/template.doc.mjs +1 -0
  55. package/api/theme/_adapter.d.mts +2 -3
  56. package/api/theme/_adapter.mjs +4 -5
  57. package/api/theme/add/add.binary.test.mjs +10 -17
  58. package/api/theme/add/add.test.mjs +14 -1
  59. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  60. package/api/theme/integration-themes.test.mjs +39 -28
  61. package/api/theme/list/list.test.mjs +19 -20
  62. package/api/theme/listThemes.doc.mjs +6 -5
  63. package/api/theme/themeAdd.doc.mjs +4 -3
  64. package/api/theme/themeBuild.doc.mjs +1 -0
  65. package/api/theme/themeList.doc.mjs +6 -3
  66. package/api/theme/themeListAvailable.doc.mjs +5 -3
  67. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  68. package/api/theme/themeTargets.doc.mjs +1 -0
  69. package/api/theme/themeTemplate.doc.mjs +1 -0
  70. package/api/upgrade/_adapter.d.mts +12 -3
  71. package/api/upgrade/_adapter.mjs +81 -66
  72. package/api/upgrade/provider-agreement.test.mjs +152 -0
  73. package/api/upgrade/run/run.mjs +1 -0
  74. package/api/upgrade/upgrade.doc.mjs +1 -0
  75. package/assets/codemods/integration-discovery.mjs +8 -2
  76. package/assets/codemods/integration-discovery.test.mjs +15 -0
  77. package/assets/codemods/runner.mjs +115 -5
  78. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +216 -0
  79. package/assets/codemods/transforms/next/index.mjs +11 -1
  80. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  81. package/assets/docs/cli-integrations.doc.mjs +17 -30
  82. package/assets/docs/cli.doc.mjs +15 -0
  83. package/assets/docs/theme.doc.mjs +1 -1
  84. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  85. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  86. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  87. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  88. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  89. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  90. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  91. package/authoring/codemod/codemod.doc.mjs +1 -1
  92. package/authoring/config/config.doc.mjs +1 -1
  93. package/authoring/debug/debug.doc.d.mts +11 -0
  94. package/authoring/debug/debug.doc.mjs +182 -0
  95. package/authoring/doctypes/base/graph-fields.doc.mjs +1 -1
  96. package/authoring/doctypes/command/command.doc.mjs +1 -1
  97. package/authoring/doctypes/command/type.ts +1 -1
  98. package/authoring/doctypes/component/type.ts +2 -2
  99. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  100. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  101. package/authoring/doctypes/enum/type.ts +1 -1
  102. package/authoring/doctypes/function/function.doc.mjs +1 -1
  103. package/authoring/doctypes/function/type.ts +1 -1
  104. package/authoring/doctypes/hook/type.ts +2 -2
  105. package/authoring/doctypes/load-contract.test.mjs +2 -1
  106. package/authoring/doctypes/parse.d.mts +4 -2
  107. package/authoring/doctypes/parse.mjs +7 -3
  108. package/authoring/doctypes/reference/reference.doc.mjs +1 -1
  109. package/authoring/doctypes/reference/type.ts +2 -2
  110. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  111. package/authoring/doctypes/schema/type.ts +1 -2
  112. package/authoring/doctypes/template/template.doc.mjs +3 -3
  113. package/authoring/doctypes/theme/parse.d.mts +35 -0
  114. package/authoring/doctypes/theme/parse.mjs +76 -0
  115. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  116. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  117. package/authoring/doctypes/theme/type.ts +42 -0
  118. package/authoring/doctypes/types.ts +2 -1
  119. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  120. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  121. package/authoring/index.d.mts +1 -0
  122. package/authoring/index.d.ts +6 -4
  123. package/authoring/index.mjs +2 -1
  124. package/authoring/integration/integration.doc.mjs +2 -2
  125. package/authoring/integration/type.ts +3 -9
  126. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  127. package/clients/cli/commands/blog.doc.mjs +1 -1
  128. package/clients/cli/commands/build.doc.mjs +1 -1
  129. package/clients/cli/commands/component.doc.mjs +1 -1
  130. package/clients/cli/commands/discover.doc.mjs +1 -1
  131. package/clients/cli/commands/docs.doc.mjs +1 -1
  132. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  133. package/clients/cli/commands/doctor-integration-docs.doc.mjs +1 -1
  134. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -1
  135. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  136. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  137. package/clients/cli/commands/doctor-integration.test.mjs +15 -7
  138. package/clients/cli/commands/doctor.doc.mjs +1 -1
  139. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  140. package/clients/cli/commands/hook.doc.mjs +1 -1
  141. package/clients/cli/commands/init.doc.mjs +1 -1
  142. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  143. package/clients/cli/commands/integration-add.doc.mjs +1 -1
  144. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  145. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  146. package/clients/cli/commands/integration.doc.mjs +1 -1
  147. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  148. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  149. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  150. package/clients/cli/commands/layout.doc.mjs +1 -1
  151. package/clients/cli/commands/manifest.doc.mjs +1 -1
  152. package/clients/cli/commands/search.doc.mjs +1 -1
  153. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  154. package/clients/cli/commands/template.doc.mjs +1 -1
  155. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  156. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  157. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  158. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  159. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  160. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  161. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  162. package/clients/cli/commands/theme.doc.mjs +1 -1
  163. package/clients/cli/commands/upgrade.doc.mjs +1 -1
  164. package/clients/cli/lib/hook-format.mjs +14 -5
  165. package/foundation/config/project-themes.test.mjs +11 -19
  166. package/foundation/config/project.d.mts +8 -0
  167. package/foundation/config/project.mjs +50 -21
  168. package/foundation/config/project.test.mjs +3 -16
  169. package/foundation/discovery/authoring-self-docs.d.mts +6 -0
  170. package/foundation/discovery/authoring-self-docs.mjs +17 -7
  171. package/foundation/discovery/authoring-surface.d.mts +74 -0
  172. package/foundation/discovery/authoring-surface.mjs +525 -0
  173. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  174. package/foundation/discovery/cli-self-docs.d.mts +113 -0
  175. package/foundation/discovery/cli-self-docs.mjs +514 -0
  176. package/foundation/discovery/cli-self-docs.test.mjs +437 -0
  177. package/foundation/discovery/component-loader.d.mts +35 -38
  178. package/foundation/discovery/component-loader.mjs +53 -222
  179. package/foundation/discovery/docs-discovery.d.mts +3 -2
  180. package/foundation/discovery/docs-discovery.mjs +8 -12
  181. package/foundation/discovery/docs-discovery.test.mjs +8 -5
  182. package/foundation/discovery/template-adapter.d.mts +7 -0
  183. package/foundation/discovery/template-adapter.mjs +27 -40
  184. package/foundation/discovery/template-adapter.test.mjs +15 -0
  185. package/foundation/discovery/theme-discovery.d.mts +67 -7
  186. package/foundation/discovery/theme-discovery.mjs +916 -186
  187. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  188. package/foundation/doc-compiler/bundle.d.mts +47 -0
  189. package/foundation/doc-compiler/bundle.mjs +218 -0
  190. package/foundation/doc-compiler/bundle.test.mjs +255 -0
  191. package/foundation/doc-compiler/compile.d.mts +200 -0
  192. package/foundation/doc-compiler/compile.mjs +254 -5
  193. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  194. package/foundation/doc-compiler/diagnostics.mjs +277 -0
  195. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  196. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  197. package/foundation/doc-compiler/import.d.mts +24 -0
  198. package/foundation/doc-compiler/import.mjs +59 -0
  199. package/foundation/doc-compiler/inputs.d.mts +102 -0
  200. package/foundation/doc-compiler/inputs.mjs +286 -0
  201. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  202. package/foundation/doc-compiler/ir.d.mts +13 -0
  203. package/foundation/doc-compiler/ir.mjs +189 -12
  204. package/foundation/doc-compiler/lower-doc.test.mjs +492 -0
  205. package/foundation/doc-compiler/overlays.d.mts +37 -0
  206. package/foundation/doc-compiler/overlays.mjs +206 -0
  207. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  208. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  209. package/foundation/doc-compiler/read.d.mts +126 -0
  210. package/foundation/doc-compiler/read.mjs +320 -0
  211. package/foundation/doc-compiler/read.test.mjs +313 -0
  212. package/foundation/doc-compiler/source.d.mts +33 -0
  213. package/foundation/doc-compiler/source.mjs +128 -0
  214. package/foundation/integrations/autolink.d.mts +58 -1
  215. package/foundation/integrations/autolink.mjs +143 -57
  216. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  217. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  218. package/foundation/integrations/contribution-inventory.d.mts +1 -1
  219. package/foundation/integrations/contribution-inventory.mjs +17 -20
  220. package/foundation/integrations/contribution-inventory.test.mjs +67 -27
  221. package/foundation/integrations/integrations.d.mts +3 -0
  222. package/foundation/integrations/integrations.mjs +11 -97
  223. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  224. package/foundation/integrations/provider-resolution.d.mts +152 -0
  225. package/foundation/integrations/provider-resolution.mjs +576 -0
  226. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  227. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  228. package/foundation/integrations/theme-descriptor.mjs +44 -0
  229. package/foundation/integrations/validate-contributions.mjs +104 -10
  230. package/foundation/response/error-codes.doc.mjs +2 -2
  231. package/foundation/response/error-codes.mjs +1 -1
  232. package/foundation/response/response-types.doc.mjs +1 -1
  233. package/foundation/response/response.doc.mjs +1 -1
  234. package/foundation/text/string-utils.mjs +22 -10
  235. package/package.json +9 -9
  236. package/assets/templates/themes/manifest.json +0 -95
@@ -0,0 +1,216 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {afterEach, beforeEach, describe, expect, it} from 'vitest';
4
+ import * as fs from 'node:fs';
5
+ import * as os from 'node:os';
6
+ import * as path from 'node:path';
7
+ import manifest from '../index.mjs';
8
+ import migrate from '../migrate-theme-catalog-to-descriptors.mjs';
9
+ import {runCodemods} from '../../../runner.mjs';
10
+ import {discoverThemeDirectory} from '../../../../../foundation/discovery/theme-discovery.mjs';
11
+
12
+ let root;
13
+
14
+ beforeEach(() => {
15
+ root = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-theme-catalog-'));
16
+ });
17
+
18
+ afterEach(() => {
19
+ fs.rmSync(root, {recursive: true, force: true});
20
+ });
21
+
22
+ /** A catalog entry exactly as `astryx integration add theme <slug>` wrote it in 0.6. */
23
+ function entry(slug, overrides = {}) {
24
+ const identifier = slug.replace(/-([a-z0-9])/gu, (_, c) => c.toUpperCase());
25
+ const displayName = slug
26
+ .split('-')
27
+ .map(part => part[0].toUpperCase() + part.slice(1))
28
+ .join(' ');
29
+ return {
30
+ slug,
31
+ displayName,
32
+ description: `${displayName} theme.`,
33
+ maintained: true,
34
+ entry: `${identifier}Theme.ts`,
35
+ exportName: `${identifier}Theme`,
36
+ files: [`${identifier}Theme.ts`],
37
+ ...overrides,
38
+ };
39
+ }
40
+
41
+ /** @param {ReturnType<typeof entry>[]} themes */
42
+ function writeKit(themes, {sources = true} = {}) {
43
+ fs.writeFileSync(
44
+ path.join(root, 'package.json'),
45
+ '{"name":"@acme/kit","version":"1.0.0"}\n',
46
+ );
47
+ fs.writeFileSync(
48
+ path.join(root, 'astryx.integration.mjs'),
49
+ "export default {themes: './themes'};\n",
50
+ );
51
+ for (const theme of themes) {
52
+ const source = path.join(root, 'themes', theme.slug, theme.entry);
53
+ fs.mkdirSync(path.dirname(source), {recursive: true});
54
+ if (sources) {
55
+ fs.writeFileSync(
56
+ source,
57
+ `import {defineTheme} from '@astryxdesign/core/theme';\n\nexport const ${theme.exportName} = defineTheme({\n name: '${theme.slug}',\n});\n`,
58
+ );
59
+ }
60
+ }
61
+ fs.mkdirSync(path.join(root, 'themes'), {recursive: true});
62
+ fs.writeFileSync(
63
+ path.join(root, 'themes', 'manifest.json'),
64
+ `${JSON.stringify({version: 1, themes}, null, 2)}\n`,
65
+ );
66
+ }
67
+
68
+ const catalog = () => path.join(root, 'themes', 'manifest.json');
69
+
70
+ /** @param {boolean} apply @param {string} [srcPath] */
71
+ const run = (apply, srcPath = root) =>
72
+ runCodemods([{version: 'next', transforms: manifest}], {
73
+ apply,
74
+ path: srcPath,
75
+ codemod: undefined,
76
+ silent: true,
77
+ root,
78
+ });
79
+
80
+ describe('migrate-theme-catalog-to-descriptors', () => {
81
+ it('is staged for the next release as a project codemod', () => {
82
+ expect(manifest.map(t => [t.name, t.meta.codemodType])).toEqual([
83
+ ['migrate-theme-catalog-to-descriptors', 'project'],
84
+ ]);
85
+ });
86
+
87
+ it('writes one typed descriptor per catalog theme and removes the catalog', async () => {
88
+ writeKit([
89
+ entry('ocean'),
90
+ entry('deep-sea', {
91
+ displayName: "Deep 'Sea'",
92
+ description: 'Line one\nline two \\ done.',
93
+ maintained: false,
94
+ }),
95
+ ]);
96
+
97
+ const result = await run(true);
98
+
99
+ expect(result).toMatchObject({errors: [], totalFilesChanged: 3});
100
+ expect(fs.existsSync(catalog())).toBe(false);
101
+ expect(
102
+ fs.readFileSync(
103
+ path.join(root, 'themes/ocean/oceanTheme.doc.mjs'),
104
+ 'utf-8',
105
+ ),
106
+ ).toBe(
107
+ "/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */\nexport default {\n type: 'theme',\n name: 'ocean',\n displayName: 'Ocean',\n description: 'Ocean theme.',\n maintained: true,\n};\n",
108
+ );
109
+ const themes = discoverThemeDirectory(
110
+ path.join(root, 'themes'),
111
+ '@acme/kit',
112
+ );
113
+ expect(
114
+ themes.map(({slug, displayName, description, maintained}) => ({
115
+ slug,
116
+ displayName,
117
+ description,
118
+ maintained,
119
+ })),
120
+ ).toEqual([
121
+ {
122
+ slug: 'deep-sea',
123
+ displayName: "Deep 'Sea'",
124
+ description: 'Line one\nline two \\ done.',
125
+ maintained: false,
126
+ },
127
+ {
128
+ slug: 'ocean',
129
+ displayName: 'Ocean',
130
+ description: 'Ocean theme.',
131
+ maintained: true,
132
+ },
133
+ ]);
134
+ });
135
+
136
+ it('previews without writing, even with no source folder', async () => {
137
+ writeKit([entry('ocean')]);
138
+
139
+ const result = await run(false, path.join(root, 'src'));
140
+
141
+ expect(result).toMatchObject({
142
+ errors: [],
143
+ totalFilesChanged: 2,
144
+ writtenFiles: [],
145
+ });
146
+ expect(fs.existsSync(catalog())).toBe(true);
147
+ expect(
148
+ fs.existsSync(path.join(root, 'themes/ocean/oceanTheme.doc.mjs')),
149
+ ).toBe(false);
150
+ });
151
+
152
+ it('keeps a descriptor that already exists and still removes the catalog', async () => {
153
+ writeKit([entry('ocean'), entry('reef')]);
154
+ const kept = path.join(root, 'themes/ocean/oceanTheme.doc.mjs');
155
+ fs.writeFileSync(
156
+ kept,
157
+ "export default {type: 'theme', name: 'ocean', displayName: 'Mine', description: '', maintained: true};\n",
158
+ );
159
+
160
+ const result = await run(true);
161
+
162
+ expect(result.errors).toEqual([]);
163
+ expect(fs.readFileSync(kept, 'utf-8')).toContain("displayName: 'Mine'");
164
+ expect(
165
+ fs.existsSync(path.join(root, 'themes/reef/reefTheme.doc.mjs')),
166
+ ).toBe(true);
167
+ expect(fs.existsSync(catalog())).toBe(false);
168
+ });
169
+
170
+ it('changes nothing and names each entry that cannot convert', async () => {
171
+ writeKit([
172
+ entry('ocean'),
173
+ entry('sea', {exportName: 'oceanBlue'}),
174
+ entry('nested', {
175
+ entry: 'src/nestedTheme.ts',
176
+ files: ['src/nestedTheme.ts'],
177
+ }),
178
+ ]);
179
+ fs.rmSync(path.join(root, 'themes/ocean/oceanTheme.ts'));
180
+ const before = fs.readFileSync(catalog(), 'utf-8');
181
+
182
+ const result = await run(true);
183
+
184
+ expect(result.errors.map(e => [e.file, e.error])).toEqual([
185
+ ['themes/ocean', 'holds no oceanTheme.ts for theme "ocean".'],
186
+ [
187
+ 'themes/manifest.json',
188
+ 'theme "sea" exports "oceanBlue" from seaTheme.ts; a descriptor takes the export name from the file name, so rename the export or the file to match.',
189
+ ],
190
+ [
191
+ 'themes/manifest.json',
192
+ 'theme "nested" has entry "src/nestedTheme.ts"; a descriptor sits beside a .ts, .tsx, .mjs, or .js source directly in themes/nested/.',
193
+ ],
194
+ ]);
195
+ expect(fs.readFileSync(catalog(), 'utf-8')).toBe(before);
196
+ expect(fs.existsSync(path.join(root, 'themes/sea/seaTheme.doc.mjs'))).toBe(
197
+ false,
198
+ );
199
+ });
200
+
201
+ it('leaves a package with no catalog to convert alone', async () => {
202
+ const empty = {writes: [], deletes: [], problems: []};
203
+ await expect(migrate(root)).resolves.toEqual(empty);
204
+ fs.writeFileSync(
205
+ path.join(root, 'astryx.integration.mjs'),
206
+ 'export default {};\n',
207
+ );
208
+ await expect(migrate(root)).resolves.toEqual(empty);
209
+ fs.writeFileSync(
210
+ path.join(root, 'astryx.integration.mjs'),
211
+ "export default {themes: './themes'};\n",
212
+ );
213
+ fs.mkdirSync(path.join(root, 'themes'));
214
+ await expect(migrate(root)).resolves.toEqual(empty);
215
+ });
216
+ });
@@ -7,4 +7,14 @@
7
7
  * this file into the resolved version folder.
8
8
  */
9
9
 
10
- export default [];
10
+ import migrateThemeCatalogToDescriptors, {
11
+ meta as migrateThemeCatalogToDescriptorsMeta,
12
+ } from './migrate-theme-catalog-to-descriptors.mjs';
13
+
14
+ export default [
15
+ {
16
+ name: 'migrate-theme-catalog-to-descriptors',
17
+ transform: migrateThemeCatalogToDescriptors,
18
+ meta: migrateThemeCatalogToDescriptorsMeta,
19
+ },
20
+ ];
@@ -0,0 +1,141 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Give each integration theme a typed descriptor instead of the
5
+ * `manifest.json` theme catalog.
6
+ *
7
+ * Astryx 0.6 scaffolded integration themes into one catalog,
8
+ * `<themes root>/manifest.json`. Each theme now carries a strongly typed
9
+ * same-stem `.doc.mjs` descriptor, and a themes root that still holds the
10
+ * catalog is refused. This project codemod reads the package's integration
11
+ * manifest, plans one descriptor per catalog entry beside that theme's source,
12
+ * and removes the catalog. When any entry cannot convert, it plans nothing and
13
+ * names the entry, so the package is never left half converted.
14
+ */
15
+
16
+ import * as fs from 'node:fs';
17
+ import * as path from 'node:path';
18
+ import {loadManifestObject} from '../../../../foundation/integrations/integrations.mjs';
19
+ import {findLocalIntegrationManifestOrNull} from '../../../../foundation/integrations/manifest-writer.mjs';
20
+ import {themeDescriptorSource} from '../../../../foundation/integrations/theme-descriptor.mjs';
21
+
22
+ export const meta = {
23
+ title:
24
+ 'Give each integration theme a typed descriptor instead of the manifest.json catalog',
25
+ description:
26
+ 'Writes <name>Theme.doc.mjs beside each theme the catalog lists, then removes the catalog.',
27
+ codemodType: 'project',
28
+ };
29
+
30
+ const SLUG = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
31
+ const SOURCE = /\.(?:ts|tsx|mjs|js)$/u;
32
+
33
+ /**
34
+ * @param {string} root the package directory
35
+ * @returns {Promise<import('../../runner.mjs').ProjectCodemodPlan>}
36
+ */
37
+ export default async function migrateThemeCatalogToDescriptors(root) {
38
+ /** @type {import('../../runner.mjs').ProjectCodemodPlan} */
39
+ const plan = {writes: [], deletes: [], problems: []};
40
+ /** @param {string} file @returns {string} */
41
+ const rel = file => path.relative(root, file).split(path.sep).join('/');
42
+ /** @param {string} file @param {string} message */
43
+ const problem = (file, message) => plan.problems.push({file, message});
44
+
45
+ const manifestFile = findLocalIntegrationManifestOrNull(root);
46
+ if (!manifestFile) return plan;
47
+ const manifest = await loadManifestObject(
48
+ manifestFile,
49
+ `Integration manifest ${path.basename(manifestFile)}`,
50
+ {fresh: true},
51
+ );
52
+ if (typeof manifest.themes !== 'string') return plan;
53
+ const themesRoot = path.resolve(root, manifest.themes);
54
+ const fromRoot = path.relative(root, themesRoot);
55
+ if (fromRoot.startsWith('..') || path.isAbsolute(fromRoot)) return plan;
56
+ const catalogFile = path.join(themesRoot, 'manifest.json');
57
+ if (!fs.existsSync(catalogFile)) return plan;
58
+ const catalogPath = rel(catalogFile);
59
+
60
+ let catalog;
61
+ try {
62
+ catalog = JSON.parse(fs.readFileSync(catalogFile, 'utf-8'));
63
+ } catch (error) {
64
+ const reason = error instanceof Error ? error.message : String(error);
65
+ problem(catalogPath, `is not valid JSON (${reason}).`);
66
+ return plan;
67
+ }
68
+ if (
69
+ !catalog ||
70
+ typeof catalog !== 'object' ||
71
+ catalog.version !== 1 ||
72
+ !Array.isArray(catalog.themes)
73
+ ) {
74
+ problem(catalogPath, 'is not a version 1 theme catalog.');
75
+ return plan;
76
+ }
77
+
78
+ for (const [index, entry] of catalog.themes.entries()) {
79
+ const slug = entry?.slug;
80
+ if (typeof slug !== 'string' || !SLUG.test(slug)) {
81
+ problem(
82
+ catalogPath,
83
+ `themes[${index}] has no lowercase kebab-case slug; fix the entry or add that theme's descriptor by hand.`,
84
+ );
85
+ continue;
86
+ }
87
+ const themeDir = path.join(themesRoot, slug);
88
+ const source = entry.entry;
89
+ if (
90
+ typeof source !== 'string' ||
91
+ !SOURCE.test(source) ||
92
+ source !== path.basename(source)
93
+ ) {
94
+ problem(
95
+ catalogPath,
96
+ `theme "${slug}" has entry ${JSON.stringify(source)}; a descriptor sits beside a .ts, .tsx, .mjs, or .js source directly in ${rel(themeDir)}/.`,
97
+ );
98
+ continue;
99
+ }
100
+ const stem = source.replace(SOURCE, '');
101
+ if (entry.exportName !== stem) {
102
+ problem(
103
+ catalogPath,
104
+ `theme "${slug}" exports ${JSON.stringify(entry.exportName)} from ${source}; a descriptor takes the export name from the file name, so rename the export or the file to match.`,
105
+ );
106
+ continue;
107
+ }
108
+ if (typeof entry.displayName !== 'string' || !entry.displayName.trim()) {
109
+ problem(catalogPath, `theme "${slug}" has no displayName.`);
110
+ continue;
111
+ }
112
+ if (typeof entry.description !== 'string') {
113
+ problem(catalogPath, `theme "${slug}" has no description string.`);
114
+ continue;
115
+ }
116
+ if (typeof entry.maintained !== 'boolean') {
117
+ problem(catalogPath, `theme "${slug}" has no maintained flag.`);
118
+ continue;
119
+ }
120
+ if (!fs.existsSync(path.join(themeDir, source))) {
121
+ problem(rel(themeDir), `holds no ${source} for theme "${slug}".`);
122
+ continue;
123
+ }
124
+ const descriptor = path.join(themeDir, `${stem}.doc.mjs`);
125
+ if (fs.existsSync(descriptor)) continue;
126
+ plan.writes.push({
127
+ path: descriptor,
128
+ contents: themeDescriptorSource({
129
+ type: 'theme',
130
+ name: slug,
131
+ displayName: entry.displayName,
132
+ description: entry.description,
133
+ maintained: entry.maintained,
134
+ }),
135
+ });
136
+ }
137
+ if (plan.problems.length > 0)
138
+ return {writes: [], deletes: [], problems: plan.problems};
139
+ plan.deletes.push(catalogFile);
140
+ return plan;
141
+ }
@@ -84,7 +84,7 @@ export const docs = {
84
84
  content: [
85
85
  {
86
86
  type: 'prose',
87
- text: 'A useful theme package usually ships more than colors. Start with the source theme, author the palette request at `themes/ocean/palette.config.json`, then add the guides its consumers need. The `integration add` commands keep the package manifest in sync; add palette outputs to the theme catalog after generation.',
87
+ text: 'A useful theme package usually ships more than colors. Start with the source theme, author the palette request at `themes/ocean/palette.config.json`, then add the guides its consumers need. The `integration add` commands keep the package manifest in sync. Palette outputs live inside the theme directory, which ships as one unit, so there is nothing to register after generation.',
88
88
  },
89
89
  {
90
90
  type: 'code',
@@ -94,7 +94,7 @@ export const docs = {
94
94
  },
95
95
  {
96
96
  type: 'prose',
97
- text: "Edit the generated theme and guide files before publishing. The shown palette command writes `themes/ocean/tokens/ocean.palette.ts` and its sibling `themes/ocean/tokens/ocean.palette.receipt.json`. The TypeScript candidate directly exports `black`, `white`, and `palette`; import what the theme uses from `./tokens/ocean.palette`. Keep the request at `themes/ocean/palette.config.json`, and list the theme source, request, candidate, and receipt in the catalog entry's `files` array. Add any optional wrapper, refs, icon, or preview modules only when you author them, and list each one too. `integration pack --check` runs the real package lifecycle and compares local discovery with the npm tarball, so a missing source file or files allowlist entry fails before a consumer sees it.",
97
+ text: 'Edit the generated theme descriptor, source, and guide files before publishing. The shown palette command writes `themes/ocean/tokens/ocean.palette.ts` and its sibling `themes/ocean/tokens/ocean.palette.receipt.json`, a reproducibility receipt. The TypeScript candidate directly exports `black`, `white`, and `palette`; import what the theme uses from `./tokens/ocean.palette`. Keep the request at `themes/ocean/palette.config.json`. The whole theme directory is copied and packed as one unit, so an optional wrapper, refs, icon, or preview module you add inside it ships with the theme. `integration pack --check` runs the real package lifecycle and compares local discovery with the npm tarball, so a missing source or descriptor fails before a consumer sees it.',
98
98
  },
99
99
  {
100
100
  type: 'code',
@@ -104,7 +104,7 @@ export const docs = {
104
104
  },
105
105
  {
106
106
  type: 'prose',
107
- text: 'The package must be a direct dependency for automatic discovery. No `astryx.config` entry is needed unless the app must control integration order. `theme add` copies every file listed by the selected catalog entry, including nested token modules, and refuses to overwrite existing project files.',
107
+ text: "The package must be a direct dependency for automatic discovery. No `astryx.config` entry is needed unless the app must control integration order. `theme add` copies the selected theme's complete directory, including its typed `.doc.mjs`, nested token modules, and receipts, and refuses to overwrite existing project files.",
108
108
  },
109
109
  ],
110
110
  },
@@ -119,7 +119,7 @@ export const docs = {
119
119
  {
120
120
  type: 'code',
121
121
  lang: 'text',
122
- code: "Kind Metadata suffix type stamp Source file\n──────── ───────────────────────── ───────────── ──────────────────────\nComponent Name.doc.{ts,mjs,js} 'component' Name.tsx (same stem)\nTemplate Name.template.{ts,mjs,js} 'page'/'block' Name.tsx (same stem)\nDoc topic topic.doc.{ts,mjs,js} 'generic' (none — docs are prose)\nCodemod <version>/<id>.{ts,mjs,js} 'code'/'config' (the codemod IS the source)\nTheme manifest.json entry — <slug>/<entry>.ts",
122
+ code: "Kind Metadata file type stamp Source file\n──────── ───────────────────────── ───────────── ──────────────────────\nComponent Name.doc.mjs 'component' Name.tsx (same stem)\nTemplate Name.doc.mjs 'page'/'block' Name.tsx (same stem)\nDoc topic topic.doc.mjs 'generic' (none — docs are prose)\nCodemod <version>/<id>.{ts,mjs,js} 'code'/'config' (the codemod IS the source)\nTheme <slug>/nameTheme.doc.mjs 'theme' <slug>/nameTheme.ts (same stem)\n\nReleased .doc.js files, template .doc.ts files, and .template.{ts,mjs,js}\ntemplates still load. A component or topic .doc.ts loads only from a package\nlinked from outside node_modules; installed, it is listed but cannot be read.",
123
123
  },
124
124
  {
125
125
  type: 'prose',
@@ -152,7 +152,7 @@ export const docs = {
152
152
  content: [
153
153
  {
154
154
  type: 'prose',
155
- text: "Export your components from your library however you like, and consumers still import them from your package. For each component the CLI should document, ship a `.doc.{ts,mjs,js}` file with the same stem, for example `AcmeCarousel.tsx` alongside `AcmeCarousel.doc.ts`. The doc file must default-export an object with `type: 'component'` — not `'generic'` (that is for reference docs) and not `'page'`/`'block'` (those are for templates).",
155
+ text: "Export your components from your library however you like, and consumers still import them from your package. For each component the CLI should document, ship a strongly typed `.doc.mjs` file with the same stem, for example `AcmeCarousel.tsx` alongside `AcmeCarousel.doc.mjs`. The doc file must default-export an object with `type: 'component'` — not `'generic'` (that is for reference docs) and not `'page'`/`'block'` (those are for templates). Released `.doc.js` docs remain readable for compatibility. A `.doc.ts` component doc reads only when the package is linked from outside node_modules, not once it is installed. New authoring uses `.doc.mjs`.",
156
156
  },
157
157
  {
158
158
  type: 'prose',
@@ -161,7 +161,7 @@ export const docs = {
161
161
  {
162
162
  type: 'code',
163
163
  lang: 'typescript',
164
- code: "// AcmeCarousel.doc.ts\nexport default {\n type: 'component',\n name: 'AcmeCarousel',\n description: 'A carousel that cycles through slides.',\n // props, usage, examples, ...\n} satisfies import('@astryxdesign/cli/authoring').ComponentDoc;",
164
+ code: "// AcmeCarousel.doc.mjs\n/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */\nexport default {\n type: 'component',\n name: 'AcmeCarousel',\n displayName: 'Acme Carousel',\n usage: {description: 'A carousel that cycles through slides.'},\n props: [],\n};",
165
165
  },
166
166
  ],
167
167
  },
@@ -171,7 +171,7 @@ export const docs = {
171
171
  content: [
172
172
  {
173
173
  type: 'prose',
174
- text: "Templates are usually not exported from the package directly. Instead, consumers browse them through the CLI and materialize them into their app. Define a template as a plain object with `type: 'page'` (full pages) or `type: 'block'` (smaller chunks) as its default export in a `.template.{ts,mjs,js}` file next to the source, for example `AcmeLandingPage.tsx` and `AcmeLandingPage.template.ts`. Do not use the `.doc.{ts,mjs,js}` suffix — that is for component docs and reference docs.",
174
+ text: "Templates are usually not exported from the package directly. Instead, consumers browse them through the CLI and materialize them into their app. Define a template as a strongly typed plain object stamped with `type: 'page'` (full pages) or `type: 'block'` (smaller chunks) in a same-stem `.doc.mjs`, for example `AcmeLandingPage.tsx` and `AcmeLandingPage.doc.mjs`. Released `.template.*` files remain readable for compatibility.",
175
175
  },
176
176
  {
177
177
  type: 'prose',
@@ -180,7 +180,7 @@ export const docs = {
180
180
  {
181
181
  type: 'code',
182
182
  lang: 'typescript',
183
- code: "// AcmeLandingPage.template.ts\nexport default {\n type: 'page',\n // name, description, preview, ...\n};",
183
+ code: "// AcmeLandingPage.doc.mjs\n/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */\nexport default {\n type: 'page',\n name: 'acme-landing-page',\n displayName: 'Acme Landing Page',\n description: 'A complete product landing page.',\n};",
184
184
  },
185
185
  {
186
186
  type: 'prose',
@@ -194,12 +194,12 @@ export const docs = {
194
194
  content: [
195
195
  {
196
196
  type: 'prose',
197
- text: "Point the integration file's `docs` field at a directory of reference docs and every `{topic}.doc.{ts,mjs,js}` under it becomes a topic the CLI serves: `astryx docs` lists it, `astryx docs <topic>` prints it, `astryx search` indexes it, and `astryx init` names it in the agent block. A topic is a plain object with `type: 'generic'` as its default export — not `'component'` (that is for component docs with a same-stem source file). This is the same shape core's own topics use.",
197
+ text: "Point the integration file's `docs` field at a directory of reference docs and every strongly typed `{topic}.doc.mjs` under it becomes a topic the CLI serves: `astryx docs` lists it, `astryx docs <topic>` prints it, `astryx search` indexes it, and `astryx init` names it in the agent block. A topic is a plain object stamped `type: 'generic'` — not `'component'` (that is for component docs with a same-stem source file) — the same shape core's own topics use.",
198
198
  },
199
199
  {
200
200
  type: 'code',
201
201
  lang: 'typescript',
202
- code: "// docs/deploying.doc.ts\nexport default {\n type: 'generic',\n name: 'deploying',\n title: 'Deploying',\n description: 'Ship an app built with Acme widgets.',\n category: 'guide',\n sections: [\n {title: 'Overview', content: [{type: 'prose', text: '...'}]},\n ],\n};",
202
+ code: "// docs/deploying.doc.mjs\n/** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */\nexport default {\n type: 'generic',\n name: 'deploying',\n title: 'Deploying',\n description: 'Ship an app built with Acme widgets.',\n category: 'guide',\n sections: [\n {title: 'Overview', content: [{type: 'prose', text: '...'}]},\n ],\n};",
203
203
  },
204
204
  {
205
205
  type: 'prose',
@@ -212,7 +212,7 @@ export const docs = {
212
212
  },
213
213
  {
214
214
  type: 'prose',
215
- text: "`extends: 'x'` merges onto a topic instead of owning it: a section with the same key as one in the base (its `id`, or the key its title derives) or the same title replaces that section, and a section the base does not have is appended. Reach for it to correct or add to a topic you do not want to fork: a fork of someone else's guide stops receiving their fixes the day you write it.",
215
+ text: "`extends: 'x'` merges onto a topic instead of owning it: a section with the same key as one in the base (its `id`, or the key its title derives) or the same title replaces that section, and a section the base does not have is appended. The topic keeps its own title and description; only `replaces` renames it. Reach for it to correct or add to a topic you do not want to fork: a fork of someone else's guide stops receiving their fixes the day you write it.",
216
216
  },
217
217
  {
218
218
  type: 'list',
@@ -233,38 +233,25 @@ export const docs = {
233
233
  content: [
234
234
  {
235
235
  type: 'prose',
236
- text: "A theme contribution is editable `defineTheme` source, not compiled CSS. Add `themes: './themes'` to `astryx.integration.*`, place the source under one directory per slug, and list it in `themes/manifest.json`. If package.json has a `files` allowlist, include both the integration manifest and the themes root; packages with no allowlist already publish both. Do not add an `exports` map only for theme discovery.",
236
+ text: "A theme contribution is editable `defineTheme` source, not compiled CSS. Add `themes: './themes'` to `astryx.integration.*`. Give each lower-kebab slug its own directory containing a theme source and mandatory same-stem, strongly typed `.doc.mjs`. If package.json has a `files` allowlist, include both the integration manifest and the themes root; packages with no allowlist already publish both. Do not add an `exports` map only for theme discovery.",
237
237
  },
238
238
  {
239
239
  type: 'code',
240
240
  lang: 'text',
241
- code: 'themes/\n manifest.json\n ocean/\n oceanTheme.ts\n palette.config.json\n tokens/\n ocean.palette.ts\n ocean.palette.receipt.json',
241
+ code: 'themes/\n ocean/\n oceanTheme.ts\n oceanTheme.doc.mjs\n palette.config.json\n tokens/\n ocean.palette.ts\n ocean.palette.receipt.json',
242
242
  },
243
243
  {
244
244
  type: 'prose',
245
- text: 'The root catalog `manifest.json` must be `{ "version": 1, "themes": [...] }`. Each entry in the `themes` array requires every field shown below — omitting any one is a hard validation error:',
246
- },
247
- {
248
- type: 'list',
249
- style: 'unordered',
250
- items: [
251
- '`slug` — lowercase kebab-case starting with a letter (e.g. `"ocean"`). Must be unique within the catalog.',
252
- '`displayName` — human-readable label (e.g. `"Ocean"`).',
253
- '`description` — string description of the theme.',
254
- '`maintained` — boolean indicating active maintenance.',
255
- '`entry` — source file relative to `themes/<slug>/` (e.g. `"oceanTheme.ts"`).',
256
- '`exportName` — a valid JS identifier naming the runtime export in the entry file (e.g. `"oceanTheme"`). Astryx parses the source without executing it and rejects missing or type-only exports.',
257
- '`files` — non-empty array of filenames relative to `themes/<slug>/`. Must include the entry file and every local static import the entry source uses. Astryx validates that every listed file exists on disk and that every local import in the entry names a file in this list.',
258
- ],
245
+ text: '`ThemeDoc` owns `name` (the slug), `displayName`, `description`, and `maintained`. The descriptor/source stem supplies the source entry and required named runtime export. Astryx parses the source without executing it, confines every local static import and re-export to the theme directory, copies that complete directory, and rejects missing or type-only exports.',
259
246
  },
260
247
  {
261
248
  type: 'code',
262
- lang: 'json',
263
- code: '{\n "version": 1,\n "themes": [{\n "slug": "ocean",\n "displayName": "Ocean",\n "description": "Ocean theme with OKLCH palettes.",\n "maintained": true,\n "entry": "oceanTheme.ts",\n "exportName": "oceanTheme",\n "files": [\n "oceanTheme.ts",\n "palette.config.json",\n "tokens/ocean.palette.ts",\n "tokens/ocean.palette.receipt.json"\n ]\n }]\n}',
249
+ lang: 'javascript',
250
+ code: "/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */\nexport default {\n type: 'theme',\n name: 'ocean',\n displayName: 'Ocean',\n description: 'Ocean theme.',\n maintained: true,\n};",
264
251
  },
265
252
  {
266
253
  type: 'prose',
267
- text: 'The generated candidate is already importable: it exports `black`, `white`, `palette`, and a default palette value. Import it directly from `./tokens/ocean.palette`. A wrapper or palette-refs module is optional application code, not generator output; list it only if you create it.',
254
+ text: 'The generated palette candidate is already importable: it exports `black`, `white`, `palette`, and a default palette value. Import it directly from `./tokens/ocean.palette`. A wrapper or palette-refs module is optional application code, not generator output.',
268
255
  },
269
256
  {
270
257
  type: 'prose',
@@ -0,0 +1,15 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `astryx docs cli`: every command, API function, output schema, and
5
+ * enum the CLI documents, one section each.
6
+ *
7
+ * Built from the docs whose `namespace` is `cli/commands` or `cli/api`, so it
8
+ * cannot drift from the commands and functions it describes. `astryx doctor`
9
+ * names a CLI doc with no namespace, or one no topic reads.
10
+ */
11
+
12
+ import {buildCliTopic} from '../../foundation/discovery/cli-self-docs.mjs';
13
+
14
+ /** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */
15
+ export const docs = await buildCliTopic();
@@ -152,7 +152,7 @@ function App() {
152
152
  },
153
153
  {
154
154
  type: 'prose',
155
- text: 'The copy is editable project source, not a reference back into node_modules. Every file named by the theme catalog comes with it, including nested token or palette modules. A second add refuses to overwrite those files unless you pass `--overwrite`.',
155
+ text: 'The copy is editable project source, not a reference back into node_modules. The complete theme directory comes with it, including its typed `.doc.mjs`, nested token and palette modules, and receipts. A second add refuses to overwrite those files unless you pass `--overwrite`.',
156
156
  },
157
157
  ],
158
158
  },
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
4
+ export default {
5
+ type: 'theme',
6
+ name: 'butter',
7
+ displayName: 'Butter',
8
+ description:
9
+ 'Warm, creamy yellows with a friendly blue accent. Playful enough for consumer surfaces, soft enough to stay readable.',
10
+ maintained: false,
11
+ };
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
4
+ export default {
5
+ type: 'theme',
6
+ name: 'chocolate',
7
+ displayName: 'Chocolate',
8
+ description:
9
+ 'Warm chocolate theme for Astryx — rich brown palette with Fraunces headings, Albert Sans body, and Lucide icons',
10
+ maintained: false,
11
+ };
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
4
+ export default {
5
+ type: 'theme',
6
+ name: 'gothic',
7
+ displayName: 'Gothic',
8
+ description:
9
+ 'Deep blue-grays and a signature display serif. Dramatic and editorial, for surfaces that want to be remembered.',
10
+ maintained: false,
11
+ };
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
4
+ export default {
5
+ type: 'theme',
6
+ name: 'matcha',
7
+ displayName: 'Matcha',
8
+ description:
9
+ 'Earthy greens with a calm, organic feel. Naturalistic and grounded, great for wellness or content-first apps.',
10
+ maintained: false,
11
+ };
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
4
+ export default {
5
+ type: 'theme',
6
+ name: 'neutral',
7
+ displayName: 'Neutral',
8
+ description:
9
+ 'Restrained warm grays. Minimal and quiet, so the content stays the focus.',
10
+ maintained: true,
11
+ };
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
4
+ export default {
5
+ type: 'theme',
6
+ name: 'stone',
7
+ displayName: 'Stone',
8
+ description:
9
+ 'Warm stone and slate, earthy and understated, with just enough character to feel handcrafted.',
10
+ maintained: false,
11
+ };
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
4
+ export default {
5
+ type: 'theme',
6
+ name: 'y2k',
7
+ displayName: 'Y2K',
8
+ description:
9
+ 'Hot pinks, lime greens, and Poppins. Bubbly, playful, and unmistakably retro.',
10
+ maintained: false,
11
+ };
@@ -12,7 +12,7 @@ export const doc = {
12
12
  type: 'schema',
13
13
  name: 'codemod',
14
14
  displayName: 'Astryx Codemod',
15
- namespace: 'cli',
15
+ namespace: 'authoring',
16
16
  description:
17
17
  'A codemod module the CLI runs during `astryx upgrade`. Default-export a ' +
18
18
  "plain object with a `type` discriminant: 'code' rewrites source files, " +