@astryxdesign/cli 0.1.2-canary.39ed50d → 0.1.2-canary.3f9afb0

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 (41) hide show
  1. package/CHANGELOG.md +0 -1
  2. package/README.md +49 -55
  3. package/package.json +7 -7
  4. package/src/api/doctor.mjs +3 -3
  5. package/src/codemods/transforms/v0.0.15/index.mjs +0 -13
  6. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-css-surfaces.test.mjs +67 -0
  7. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-declare-module.test.mjs +61 -0
  8. package/src/codemods/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +104 -0
  9. package/src/codemods/transforms/{v0.0.15 → v0.1.0}/drop-xds-prefix-imports.mjs +15 -4
  10. package/src/codemods/transforms/v0.1.0/index.mjs +46 -0
  11. package/src/codemods/transforms/v0.1.0/migrate-xds-css-surfaces.mjs +77 -0
  12. package/src/codemods/transforms/v0.1.0/migrate-xds-declare-module.mjs +78 -0
  13. package/src/commands/agent-docs.mjs +1 -1
  14. package/templates/blocks/components/Blockquote/BlockquoteTestimonials.doc.mjs +14 -0
  15. package/templates/blocks/components/Blockquote/BlockquoteTestimonials.tsx +34 -0
  16. package/templates/blocks/components/Blockquote/BlockquoteWithCite.doc.mjs +14 -0
  17. package/templates/blocks/components/Blockquote/BlockquoteWithCite.tsx +21 -0
  18. package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.doc.mjs +14 -0
  19. package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.tsx +46 -0
  20. package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.doc.mjs +14 -0
  21. package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.tsx +52 -0
  22. package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.doc.mjs +14 -0
  23. package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.tsx +43 -0
  24. package/templates/blocks/components/DropdownMenu/DropdownMenuShowcase.tsx +0 -1
  25. package/templates/blocks/components/DropdownMenuItem/DropdownMenuItemShowcase.tsx +0 -1
  26. package/templates/blocks/components/Hooks/useKeyboardHintHookUsage.doc.mjs +14 -0
  27. package/templates/blocks/components/Hooks/useKeyboardHintHookUsage.tsx +57 -0
  28. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.doc.mjs +1 -1
  29. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.tsx +9 -4
  30. package/templates/blocks/components/Lightbox/LightboxGallery.doc.mjs +14 -0
  31. package/templates/blocks/components/Lightbox/LightboxGallery.tsx +53 -0
  32. package/templates/blocks/components/Lightbox/LightboxShowcase.tsx +4 -3
  33. package/templates/blocks/components/Lightbox/LightboxVideo.doc.mjs +14 -0
  34. package/templates/blocks/components/Lightbox/LightboxVideo.tsx +27 -0
  35. package/templates/blocks/components/Lightbox/LightboxZoom.doc.mjs +14 -0
  36. package/templates/blocks/components/Lightbox/LightboxZoom.tsx +33 -0
  37. package/templates/blocks/components/MoreMenu/MoreMenuShowcase.tsx +0 -1
  38. package/templates/blocks/components/Tab/TabWithSelectedIcon.doc.mjs +13 -0
  39. package/templates/blocks/components/Tab/TabWithSelectedIcon.tsx +39 -0
  40. package/templates/pages/shell-side-nav/page.tsx +0 -1
  41. /package/src/codemods/transforms/{v0.0.15 → v0.1.0}/__tests__/drop-xds-prefix-imports.test.mjs +0 -0
package/CHANGELOG.md CHANGED
@@ -110,7 +110,6 @@ Thanks to everyone who contributed to this release:
110
110
  Thanks to everyone who contributed to this release:
111
111
 
112
112
  - @ejhammond
113
- - @joeyfarina
114
113
  - @josephfarina
115
114
  - @nynexman4464
116
115
 
package/README.md CHANGED
@@ -62,8 +62,7 @@ Options:
62
62
  | `upgrade` | Run codemods to migrate between versions |
63
63
  | `theme build` | Compile a defineTheme file to production CSS and JS |
64
64
  | `discover` | Discover external packages and components |
65
- | `gap-report` | Report a gap when a component doesn't meet your needs |
66
- | `doctor` | Diagnose your XDS setup and report problems with fixes (CI-friendly via exit code) |
65
+ | `doctor` | Diagnose your Astryx setup and report problems with fixes (CI-friendly via exit code) |
67
66
 
68
67
  ### Global options
69
68
 
@@ -161,7 +160,6 @@ if (isError(result)) {
161
160
  | `ERR_INVALID_VERSION` | A `--from`/`--to` value was not a valid semver string. |
162
161
  | `ERR_DEP_MISSING` | A required external dependency (e.g. jscodeshift) is missing. |
163
162
  | `ERR_GH_CLI` | GitHub CLI (`gh`) is not installed or not authenticated. |
164
- | `ERR_GAP_REPORT_FAILED` | Filing a gap report failed (disabled, or the integration errored). |
165
163
 
166
164
  ## Capability manifest (agent discovery)
167
165
 
@@ -172,7 +170,7 @@ discriminators each command can emit. Think of it as an OpenAPI spec for the CLI
172
170
 
173
171
  ```bash
174
172
  astryx manifest --json # dedicated surface — type: "manifest"
175
- xds --json # bare invocation — embeds the same payload under data.manifest
173
+ astryx --json # bare invocation — embeds the same payload under data.manifest
176
174
  ```
177
175
 
178
176
  Shape:
@@ -182,7 +180,7 @@ Shape:
182
180
  "apiVersion": 1,
183
181
  "type": "manifest",
184
182
  "data": {
185
- "name": "xds",
183
+ "name": "astryx",
186
184
  "version": "0.0.14",
187
185
  "description": "Design system CLI — components, themes, and tooling",
188
186
  "globalOptions": [
@@ -249,14 +247,14 @@ the `JSON_SUPPORTED` allowlist and a small declarative `RESPONSE_TYPES` map in
249
247
  `src/lib/manifest.mjs`, guarded by a drift test (`manifest.test.mjs`) so adding a
250
248
  command without describing it fails CI.
251
249
 
252
- **Backwards-compat:** the bare `xds --json` envelope keeps `type: "help"` and its
250
+ **Backwards-compat:** the bare `astryx --json` envelope keeps `type: "help"` and its
253
251
  original shallow fields (`name`, `version`, `commands` as a `string[]` of names,
254
252
  `jsonSupported`); the full structured manifest is additive under `data.manifest`.
255
253
  For the standalone manifest envelope (`type: "manifest"`), use `astryx manifest --json`.
256
254
 
257
255
  ## Programmatic API
258
256
 
259
- The same logic that powers `xds --json` is available as importable, type-safe functions:
257
+ The same logic that powers `astryx --json` is available as importable, type-safe functions:
260
258
 
261
259
  ```typescript
262
260
  import {
@@ -269,20 +267,20 @@ import {
269
267
  AstryxError,
270
268
  } from '@astryxdesign/cli/api';
271
269
 
272
- // Same result as: xds --json component Button
270
+ // Same result as: astryx --json component Button
273
271
  const btn = await component('Button');
274
272
  btn.type; // 'component.detail'
275
273
  btn.data.name; // 'Button' (typed as ComponentDoc)
276
274
 
277
- // Same result as: xds --json component --list
275
+ // Same result as: astryx --json component --list
278
276
  const list = await component(undefined, {list: true});
279
277
  list.data; // Record<string, string[]>
280
278
 
281
- // Same result as: xds --json docs principles
279
+ // Same result as: astryx --json docs principles
282
280
  const principles = await docs('principles');
283
- principles.data.title; // 'XDS Principles'
281
+ principles.data.title; // 'Principles'
284
282
 
285
- // Same result as: xds --json hook useMediaQuery
283
+ // Same result as: astryx --json hook useMediaQuery
286
284
  const useMediaQuery = await hook('useMediaQuery');
287
285
  useMediaQuery.data.params; // typed as HookParamDoc[]
288
286
 
@@ -296,7 +294,7 @@ try {
296
294
  }
297
295
  ```
298
296
 
299
- The CLI command handlers are thin wrappers around these functions: they parse args, call the API, then format the output (JSON or text). This guarantees that `@astryxdesign/cli/api` and `xds --json` always return identical data.
297
+ The CLI command handlers are thin wrappers around these functions: they parse args, call the API, then format the output (JSON or text). This guarantees that `@astryxdesign/cli/api` and `astryx --json` always return identical data.
300
298
 
301
299
  ### Consumer utilities
302
300
 
@@ -326,43 +324,41 @@ detail.data.name; // already narrowed
326
324
 
327
325
  Every response has a `type` string that uniquely identifies it:
328
326
 
329
- | Command | Type | Response |
330
- | ---------------------------------------------- | --------------------------- | --------------------------------- |
331
- | `xds --json component [--list]` | `component.list` | `ComponentListResponse` |
332
- | `xds --json component --list --detail compact` | `component.brief` | `ComponentBriefResponse` |
333
- | `xds --json component --list --detail full` | `component.full` | `ComponentFullResponse` |
334
- | `xds --json component <name>` | `component.detail` | `ComponentDetailResponse` |
335
- | `xds --json component <name> --props` | `component.detail.props` | `ComponentDetailPropsResponse` |
336
- | `xds --json component <name> --source` | `component.detail.source` | `ComponentDetailSourceResponse` |
337
- | `xds --json component <name> --showcase` | `component.detail.showcase` | `ComponentDetailShowcaseResponse` |
338
- | `xds --json component <name> --blocks` | `component.detail.blocks` | `ComponentDetailBlocksResponse` |
339
- | `xds --json discover` | `discover.list` | `DiscoverListResponse` |
340
- | `xds --json discover @scope/name` | `discover.detail` | `DiscoverDetailResponse` |
341
- | `xds --json discover @scope/name/Comp` | `discover.detail.doc` | `DiscoverDetailDocResponse` |
342
- | `xds --json discover <search>` | `discover.search` | `DiscoverSearchResponse` |
343
- | `xds --json docs` | `docs.list` | `DocsListResponse` |
344
- | `xds --json docs <topic>` | `docs.detail` | `DocsDetailResponse` |
345
- | `xds --json docs <topic> <section>` | `docs.detail.section` | `DocsDetailSectionResponse` |
346
- | `xds --json template [--list]` | `template.list` | `TemplateListResponse` |
347
- | `xds --json template <name>` | `template.show` | `TemplateShowResponse` |
348
- | `xds --json template <name> --skeleton` | `template.skeleton` | `TemplateSkeletonResponse` |
349
- | `xds --json template <name> [path]` | `template.copy` | `TemplateCopyResponse` |
350
- | `xds --json hook [--list]` | `hook.list` | `HookListResponse` |
351
- | `xds --json hook --list --detail compact` | `hook.brief` | `HookBriefResponse` |
352
- | `xds --json hook --list --detail full` | `hook.full` | `HookFullResponse` |
353
- | `xds --json hook <name>` | `hook.detail` | `HookDetailResponse` |
354
- | `xds --json hook <name> --params` | `hook.detail.params` | `HookDetailParamsResponse` |
355
- | `xds --json search <query>` | `search` | `SearchResponse` |
356
- | `xds --json swizzle [--list]` | `swizzle.list` | `SwizzleListResponse` |
357
- | `xds --json swizzle <component>` | `swizzle.copy` | `SwizzleCopyResponse` |
358
- | `xds --json theme build <file>` | `theme.build` | `ThemeBuildResponse` |
359
- | `xds --json upgrade --list` | `upgrade.list` | `UpgradeListResponse` |
360
- | `xds --json upgrade [--apply]` | `upgrade.run` | `UpgradeRunResponse` |
361
- | `xds --json gap-report --list-categories` | `gap-report.categories` | `GapReportCategoriesResponse` |
362
- | `xds --json gap-report --component X ...` | `gap-report.file` | `GapReportFileResponse` |
363
- | `xds --json doctor` | `doctor` | `DoctorResponse` |
364
- | any error | — | `CLIError` |
365
- | unsupported command | — | `CLIUnsupportedError` |
327
+ | Command | Type | Response |
328
+ | ------------------------------------------------- | --------------------------- | --------------------------------- |
329
+ | `astryx --json component [--list]` | `component.list` | `ComponentListResponse` |
330
+ | `astryx --json component --list --detail compact` | `component.brief` | `ComponentBriefResponse` |
331
+ | `astryx --json component --list --detail full` | `component.full` | `ComponentFullResponse` |
332
+ | `astryx --json component <name>` | `component.detail` | `ComponentDetailResponse` |
333
+ | `astryx --json component <name> --props` | `component.detail.props` | `ComponentDetailPropsResponse` |
334
+ | `astryx --json component <name> --source` | `component.detail.source` | `ComponentDetailSourceResponse` |
335
+ | `astryx --json component <name> --showcase` | `component.detail.showcase` | `ComponentDetailShowcaseResponse` |
336
+ | `astryx --json component <name> --blocks` | `component.detail.blocks` | `ComponentDetailBlocksResponse` |
337
+ | `astryx --json discover` | `discover.list` | `DiscoverListResponse` |
338
+ | `astryx --json discover @scope/name` | `discover.detail` | `DiscoverDetailResponse` |
339
+ | `astryx --json discover @scope/name/Comp` | `discover.detail.doc` | `DiscoverDetailDocResponse` |
340
+ | `astryx --json discover <search>` | `discover.search` | `DiscoverSearchResponse` |
341
+ | `astryx --json docs` | `docs.list` | `DocsListResponse` |
342
+ | `astryx --json docs <topic>` | `docs.detail` | `DocsDetailResponse` |
343
+ | `astryx --json docs <topic> <section>` | `docs.detail.section` | `DocsDetailSectionResponse` |
344
+ | `astryx --json template [--list]` | `template.list` | `TemplateListResponse` |
345
+ | `astryx --json template <name>` | `template.show` | `TemplateShowResponse` |
346
+ | `astryx --json template <name> --skeleton` | `template.skeleton` | `TemplateSkeletonResponse` |
347
+ | `astryx --json template <name> [path]` | `template.copy` | `TemplateCopyResponse` |
348
+ | `astryx --json hook [--list]` | `hook.list` | `HookListResponse` |
349
+ | `astryx --json hook --list --detail compact` | `hook.brief` | `HookBriefResponse` |
350
+ | `astryx --json hook --list --detail full` | `hook.full` | `HookFullResponse` |
351
+ | `astryx --json hook <name>` | `hook.detail` | `HookDetailResponse` |
352
+ | `astryx --json hook <name> --params` | `hook.detail.params` | `HookDetailParamsResponse` |
353
+ | `astryx --json search <query>` | `search` | `SearchResponse` |
354
+ | `astryx --json swizzle [--list]` | `swizzle.list` | `SwizzleListResponse` |
355
+ | `astryx --json swizzle <component>` | `swizzle.copy` | `SwizzleCopyResponse` |
356
+ | `astryx --json theme build <file>` | `theme.build` | `ThemeBuildResponse` |
357
+ | `astryx --json upgrade --list` | `upgrade.list` | `UpgradeListResponse` |
358
+ | `astryx --json upgrade [--apply]` | `upgrade.run` | `UpgradeRunResponse` |
359
+ | `astryx --json doctor` | `doctor` | `DoctorResponse` |
360
+ | any error | — | `CLIError` |
361
+ | unsupported command | — | `CLIUnsupportedError` |
366
362
 
367
363
  ## Doctor
368
364
 
@@ -383,7 +379,7 @@ astryx doctor — diagnosing your setup
383
379
  @astryxdesign/core v0.0.14 is in step with @astryxdesign/cli v0.0.14.
384
380
  ⚠ Theme packages
385
381
  No @astryxdesign/theme-* packages are installed.
386
- → fix: Install a theme, e.g. `npm install @astryxdesign/theme-neutral`, then import its CSS or set xds.theme.
382
+ → fix: Install a theme, e.g. `npm install @astryxdesign/theme-neutral`, then import its CSS or set astryx.theme.
387
383
  ℹ astryx.config.mjs
388
384
  No astryx.config.mjs found — using defaults.
389
385
  ℹ AI agent docs
@@ -408,7 +404,7 @@ No failures — but review the ⚠ warnings above when you can.
408
404
  | Version alignment | pass / warn / info | Installed `@astryxdesign/core` is in step with `@astryxdesign/cli` |
409
405
  | Theme packages | pass / warn | An `@astryxdesign/theme-*` package is installed and a theme is wired |
410
406
  | astryx.config.mjs | pass / fail / info | Config (if present) loads cleanly with a valid shape |
411
- | AI agent docs | pass / warn / info | Agent docs exist and contain the XDS section markers |
407
+ | AI agent docs | pass / warn / info | Agent docs exist and contain the Astryx section markers |
412
408
  | Peer dependencies | pass / warn / info | `@astryxdesign/core`'s peer deps (react, …) are installed |
413
409
  | Package manager | info | Reports the detected package manager |
414
410
 
@@ -434,8 +430,6 @@ export default {
434
430
  templates: {
435
431
  get: async id => fetchTemplateFromAPI(id),
436
432
  },
437
- gapReport: {
438
- url: 'https://your-api.com/gaps',
439
- },
433
+ issuesUrl: 'https://github.com/your-org/your-repo/issues',
440
434
  };
441
435
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.1.2-canary.39ed50d",
3
+ "version": "0.1.2-canary.3f9afb0",
4
4
  "displayName": "CLI",
5
5
  "description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
6
6
  "author": "Meta Open Source",
@@ -75,9 +75,9 @@
75
75
  "zod": "^4.4.3"
76
76
  },
77
77
  "peerDependencies": {
78
- "@astryxdesign/core": "0.1.2-canary.39ed50d",
79
- "@astryxdesign/lab": "0.1.2-canary.39ed50d",
80
- "@astryxdesign/theme-neutral": "0.1.2-canary.39ed50d",
78
+ "@astryxdesign/core": "0.1.2-canary.3f9afb0",
79
+ "@astryxdesign/lab": "0.1.2-canary.3f9afb0",
80
+ "@astryxdesign/theme-neutral": "0.1.2-canary.3f9afb0",
81
81
  "gpt-tokenizer": "^2.0.0"
82
82
  },
83
83
  "peerDependenciesMeta": {
@@ -92,9 +92,9 @@
92
92
  }
93
93
  },
94
94
  "devDependencies": {
95
- "@astryxdesign/core": "0.1.2-canary.39ed50d",
96
- "@astryxdesign/lab": "0.1.2-canary.39ed50d",
97
- "@astryxdesign/theme-neutral": "0.1.2-canary.39ed50d",
95
+ "@astryxdesign/core": "0.1.2-canary.3f9afb0",
96
+ "@astryxdesign/lab": "0.1.2-canary.3f9afb0",
97
+ "@astryxdesign/theme-neutral": "0.1.2-canary.3f9afb0",
98
98
  "gpt-tokenizer": "^2.0.0"
99
99
  },
100
100
  "scripts": {
@@ -240,7 +240,7 @@ export function checkThemes(ctx) {
240
240
  label: 'Theme packages',
241
241
  status: 'warn',
242
242
  message: 'No @astryxdesign/theme-* packages are installed.',
243
- fix: 'Install a theme, e.g. `npm install @astryxdesign/theme-neutral`, then import its CSS or set xds.theme.',
243
+ fix: 'Install a theme, e.g. `npm install @astryxdesign/theme-neutral`, then import its CSS or set astryx.theme.',
244
244
  };
245
245
  }
246
246
 
@@ -354,7 +354,7 @@ export function checkAgentDocs(ctx) {
354
354
  label: 'AI agent docs',
355
355
  status: 'warn',
356
356
  message: `Agent docs present (${present.join(', ')}) but no Astryx section markers found.`,
357
- fix: 'Add the XDS section to your agent docs with `astryx init --features agents`.',
357
+ fix: 'Add the Astryx section to your agent docs with `astryx init --features agents`.',
358
358
  };
359
359
  }
360
360
 
@@ -362,7 +362,7 @@ export function checkAgentDocs(ctx) {
362
362
  id: 'agent-docs',
363
363
  label: 'AI agent docs',
364
364
  status: 'pass',
365
- message: `XDS agent docs section present in ${withMarkers.join(', ')}.`,
365
+ message: `Astryx agent docs section present in ${withMarkers.join(', ')}.`,
366
366
  };
367
367
  }
368
368
 
@@ -34,10 +34,6 @@ import migrateSelectorChildrenToRenderOption, {
34
34
  meta as migrateSelectorChildrenToRenderOptionMeta,
35
35
  } from './migrate-selector-children-to-render-option.mjs';
36
36
 
37
- import dropXdsPrefixImports, {
38
- meta as dropXdsPrefixImportsMeta,
39
- } from './drop-xds-prefix-imports.mjs';
40
-
41
37
  export default [
42
38
  {
43
39
  name: 'rename-date-picker-to-input',
@@ -75,13 +71,4 @@ export default [
75
71
  transform: migrateSelectorChildrenToRenderOption,
76
72
  meta: migrateSelectorChildrenToRenderOptionMeta,
77
73
  },
78
- {
79
- // XDS-prefix migration (P2380608025). Optional + not tied to a version
80
- // bump: consumers run it explicitly during their migration, e.g.
81
- // astryx upgrade --codemod drop-xds-prefix-imports --codemod-only --apply
82
- name: 'drop-xds-prefix-imports',
83
- transform: dropXdsPrefixImports,
84
- meta: dropXdsPrefixImportsMeta,
85
- optional: true,
86
- },
87
74
  ];
@@ -0,0 +1,67 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+
5
+ async function applyTransform(source, filePath = 'test.css') {
6
+ const {default: transform} = await import('../migrate-xds-css-surfaces.mjs');
7
+ // api.jscodeshift is unused by this CSS codemod, but the runner still
8
+ // passes it — mirror that shape.
9
+ const jscodeshift = (await import('jscodeshift')).default;
10
+ const api = {jscodeshift, stats: () => {}, report: () => {}};
11
+ const file = {source, path: filePath};
12
+ const result = transform(file, api);
13
+ return result ?? source;
14
+ }
15
+
16
+ describe('migrate-xds-css-surfaces', () => {
17
+ it('rewrites the .xds- class-selector prefix', async () => {
18
+ const input = '.xds-heading { color: red; }';
19
+ const output = await applyTransform(input);
20
+ expect(output).toBe('.astryx-heading { color: red; }');
21
+ });
22
+
23
+ it('rewrites the [data-xds-theme] attribute selector', async () => {
24
+ const input = '[data-xds-theme="dark"] .xds-card { background: black; }';
25
+ const output = await applyTransform(input);
26
+ expect(output).toContain('[data-astryx-theme="dark"]');
27
+ expect(output).toContain('.astryx-card');
28
+ expect(output).not.toContain('xds-');
29
+ });
30
+
31
+ it('rewrites data-xds-theme-prose and data-xds-media attribute selectors', async () => {
32
+ const input =
33
+ '[data-xds-theme-prose] {} [data-xds-media="print"] {}';
34
+ const output = await applyTransform(input);
35
+ expect(output).toContain('[data-astryx-theme-prose]');
36
+ expect(output).toContain('[data-astryx-media="print"]');
37
+ });
38
+
39
+ it('rewrites @layer xds-theme and @layer xds-base', async () => {
40
+ const input = '@layer xds-theme, xds-base;\n@layer xds-theme { a { color: red; } }';
41
+ const output = await applyTransform(input);
42
+ expect(output).toContain('astryx-theme');
43
+ expect(output).toContain('astryx-base');
44
+ expect(output).not.toContain('xds-theme');
45
+ expect(output).not.toContain('xds-base');
46
+ });
47
+
48
+ it('does NOT rewrite a bare "xds" in a comment or value', async () => {
49
+ const input = '/* xds tokens live here */\n.card { content: "xds"; }';
50
+ const output = await applyTransform(input);
51
+ expect(output).toBe(input);
52
+ });
53
+
54
+ it('does NOT rewrite a class-like word missing the leading dot', async () => {
55
+ // e.g. inside a JS-ish string or a comment fragment — not a class selector.
56
+ const input = '/* use xds-heading in markup */';
57
+ const output = await applyTransform(input);
58
+ expect(output).toBe(input);
59
+ });
60
+
61
+ it('leaves already-migrated CSS unchanged', async () => {
62
+ const input =
63
+ '@layer astryx-theme { [data-astryx-theme="dark"] .astryx-card {} }';
64
+ const output = await applyTransform(input);
65
+ expect(output).toBe(input);
66
+ });
67
+ });
@@ -0,0 +1,61 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+
5
+ async function applyTransform(source, filePath = 'test.d.ts') {
6
+ const {default: transform} = await import('../migrate-xds-declare-module.mjs');
7
+ const jscodeshift = (await import('jscodeshift')).default;
8
+ const j = jscodeshift.withParser('tsx');
9
+ const api = {jscodeshift: j, stats: () => {}, report: () => {}};
10
+ const file = {source, path: filePath};
11
+ const result = transform(file, api);
12
+ return result ?? source;
13
+ }
14
+
15
+ describe('migrate-xds-declare-module', () => {
16
+ it('renames a subpath declare module', async () => {
17
+ const input = [
18
+ "declare module '@xds/core/Heading' {",
19
+ ' export const HeadingLevelMap: Record<string, number>;',
20
+ '}',
21
+ ].join('\n');
22
+
23
+ const output = await applyTransform(input);
24
+ expect(output).toMatch(/declare module ['"]@astryxdesign\/core\/Heading['"]/);
25
+ expect(output).not.toContain('@xds/core');
26
+ // The augmentation body identifier is untouched.
27
+ expect(output).toContain('HeadingLevelMap');
28
+ });
29
+
30
+ it('renames a bare @xds/core declare module', async () => {
31
+ const input = "declare module '@xds/core' {\n const x: number;\n}";
32
+ const output = await applyTransform(input);
33
+ expect(output).toMatch(/declare module ['"]@astryxdesign\/core['"]/);
34
+ expect(output).not.toContain('@xds/core');
35
+ });
36
+
37
+ it('renames @xds/lab declare module using the shared map', async () => {
38
+ const input = "declare module '@xds/lab/Thing' {\n const y: number;\n}";
39
+ const output = await applyTransform(input);
40
+ expect(output).toMatch(/declare module ['"]@astryxdesign\/lab\/Thing['"]/);
41
+ expect(output).not.toContain('@xds/lab');
42
+ });
43
+
44
+ it('leaves non-xds declare modules alone', async () => {
45
+ const input = "declare module 'react' {\n const z: number;\n}";
46
+ const output = await applyTransform(input);
47
+ expect(output).toBe(input);
48
+ });
49
+
50
+ it('leaves an already-migrated declare module unchanged', async () => {
51
+ const input = "declare module '@astryxdesign/core/Heading' {\n const a: number;\n}";
52
+ const output = await applyTransform(input);
53
+ expect(output).toBe(input);
54
+ });
55
+
56
+ it('does not touch namespace declarations (Identifier id)', async () => {
57
+ const input = 'declare namespace XDSCore {\n const b: number;\n}';
58
+ const output = await applyTransform(input, 'test.ts');
59
+ expect(output).toBe(input);
60
+ });
61
+ });
@@ -0,0 +1,104 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import {describe, it, expect} from 'vitest';
4
+
5
+ /**
6
+ * End-to-end test proving the v0.1.0 manifest ordering: the prefix-drop
7
+ * codemod runs BEFORE migrate-xds-module-specifiers, so a prefixed identifier
8
+ * imported from `@xds/core` is un-prefixed AND the module path is rewritten to
9
+ * `@astryxdesign/core` in a single upgrade pass.
10
+ */
11
+ async function applyManifestInOrder(source, filePath = 'test.tsx') {
12
+ const {default: manifest} = await import('../index.mjs');
13
+ const jscodeshift = (await import('jscodeshift')).default;
14
+ const j = jscodeshift.withParser('tsx');
15
+ const api = {jscodeshift: j, stats: () => {}, report: () => {}};
16
+
17
+ let current = source;
18
+ for (const {transform} of manifest) {
19
+ const file = {source: current, path: filePath};
20
+ const result = transform(file, api);
21
+ current = result ?? current;
22
+ }
23
+ return current;
24
+ }
25
+
26
+ describe('v0.1.0 codemod manifest ordering', () => {
27
+ it('runs drop-xds-prefix-imports before migrate-xds-module-specifiers', async () => {
28
+ const {default: manifest} = await import('../index.mjs');
29
+ const names = manifest.map(entry => entry.name);
30
+ const prefixDropIdx = names.indexOf('drop-xds-prefix-imports');
31
+ const specifierIdx = names.indexOf('migrate-xds-module-specifiers');
32
+ expect(prefixDropIdx).toBeGreaterThanOrEqual(0);
33
+ expect(specifierIdx).toBeGreaterThanOrEqual(0);
34
+ expect(prefixDropIdx).toBeLessThan(specifierIdx);
35
+ });
36
+
37
+ it('drop-xds-prefix-imports is mandatory (not optional) in v0.1.0', async () => {
38
+ const {default: manifest} = await import('../index.mjs');
39
+ const entry = manifest.find(e => e.name === 'drop-xds-prefix-imports');
40
+ expect(entry).toBeDefined();
41
+ expect(entry.optional).toBeFalsy();
42
+ });
43
+
44
+ it('orders declare-module and CSS codemods after migrate-xds-module-specifiers', async () => {
45
+ const {default: manifest} = await import('../index.mjs');
46
+ const names = manifest.map(entry => entry.name);
47
+ expect(names).toEqual([
48
+ 'drop-xds-prefix-imports',
49
+ 'migrate-xds-module-specifiers',
50
+ 'migrate-xds-declare-module',
51
+ 'migrate-xds-css-surfaces',
52
+ ]);
53
+ });
54
+
55
+ it('marks migrate-xds-declare-module and migrate-xds-css-surfaces mandatory', async () => {
56
+ const {default: manifest} = await import('../index.mjs');
57
+ for (const name of [
58
+ 'migrate-xds-declare-module',
59
+ 'migrate-xds-css-surfaces',
60
+ ]) {
61
+ const entry = manifest.find(e => e.name === name);
62
+ expect(entry, name).toBeDefined();
63
+ expect(entry.optional).toBeFalsy();
64
+ }
65
+ });
66
+
67
+ it('un-prefixes useXDSTheme from @xds/core/theme AND renames the scope to @astryxdesign', async () => {
68
+ const input = [
69
+ "import {useXDSTheme} from '@xds/core/theme';",
70
+ '',
71
+ 'function App() {',
72
+ ' const theme = useXDSTheme();',
73
+ ' return theme;',
74
+ '}',
75
+ ].join('\n');
76
+
77
+ const output = await applyManifestInOrder(input);
78
+
79
+ expect(output).toMatch(/import\s*\{useTheme\}\s*from\s*['"]@astryxdesign\/core\/theme['"]/);
80
+ expect(output).toContain('const theme = useTheme();');
81
+ // The prefixed name and old scope must be fully gone.
82
+ expect(output).not.toContain('useXDSTheme');
83
+ expect(output).not.toContain('@xds/core');
84
+ });
85
+
86
+ it('un-prefixes XDSButton and XDSIconRegistry alongside the scope rename', async () => {
87
+ const input = [
88
+ "import {XDSButton, XDSIconRegistry} from '@xds/core';",
89
+ '',
90
+ 'const registry = new XDSIconRegistry();',
91
+ 'const el = <XDSButton />;',
92
+ ].join('\n');
93
+
94
+ const output = await applyManifestInOrder(input);
95
+
96
+ expect(output).toMatch(/from\s*['"]@astryxdesign\/core['"]/);
97
+ expect(output).toContain('Button');
98
+ expect(output).toContain('IconRegistry');
99
+ expect(output).toContain('new IconRegistry()');
100
+ expect(output).not.toContain('XDSButton');
101
+ expect(output).not.toContain('XDSIconRegistry');
102
+ expect(output).not.toContain('@xds/core');
103
+ });
104
+ });
@@ -4,12 +4,23 @@
4
4
  * @file Codemod: Drop the `XDS` prefix from @xds/core imports and their usages
5
5
  *
6
6
  * Part of the XDS-prefix migration (P2380608025). Rewrites consumer code from
7
- * the prefixed names to the bare compatibility aliases shipped in P1:
7
+ * the prefixed names to their bare equivalents:
8
8
  *
9
9
  * XDSButton -> Button
10
10
  * XDSButtonProps -> ButtonProps
11
11
  * useXDSTheme -> useTheme
12
12
  * useXDSToast -> useToast
13
+ * XDSIconRegistry-> IconRegistry
14
+ *
15
+ * Mandatory in v0.1.0: the prefixed compatibility aliases (which made both
16
+ * prefixed and bare names valid during the 0.0.x compat window) were dropped
17
+ * in v0.1.0, so the un-prefixing is now required for code to keep type-
18
+ * checking against @astryxdesign/core.
19
+ *
20
+ * Ordering: within the v0.1.0 manifest this runs BEFORE
21
+ * migrate-xds-module-specifiers so it still matches `@xds/core` imports (its
22
+ * source matcher) and un-prefixes the identifiers first; the specifier codemod
23
+ * then rewrites the module paths `@xds/*` -> `@astryxdesign/*`.
13
24
  *
14
25
  * Safety: this codemod ONLY renames identifiers that are imported from
15
26
  * `@xds/core` (bare or subpath, e.g. `@xds/core/Button`). It tracks the local
@@ -36,9 +47,9 @@ export const meta = {
36
47
  title: 'Drop XDS prefix from @xds/core imports (XDSButton -> Button)',
37
48
  description:
38
49
  'Rewrites @xds/core imports and their usages from the prefixed names ' +
39
- '(XDSButton, useXDSTheme, XDSButtonProps) to the bare compatibility ' +
40
- 'aliases (Button, useTheme, ButtonProps). Only renames bindings imported ' +
41
- 'from @xds/core, leaving unrelated identifiers untouched.',
50
+ '(XDSButton, useXDSTheme, XDSIconRegistry, XDSButtonProps) to their bare ' +
51
+ 'names (Button, useTheme, IconRegistry, ButtonProps). Only renames ' +
52
+ 'bindings imported from @xds/core, leaving unrelated identifiers untouched.',
42
53
  pr: '#2880',
43
54
  };
44
55
 
@@ -6,14 +6,60 @@
6
6
  * Lists all codemods for the v0.1.0 release in the order they should run.
7
7
  */
8
8
 
9
+ import dropXdsPrefixImports, {
10
+ meta as dropXdsPrefixImportsMeta,
11
+ } from './drop-xds-prefix-imports.mjs';
12
+
9
13
  import migrateXdsModuleSpecifiers, {
10
14
  meta as migrateXdsModuleSpecifiersMeta,
11
15
  } from './migrate-xds-module-specifiers.mjs';
12
16
 
17
+ import migrateXdsDeclareModule, {
18
+ meta as migrateXdsDeclareModuleMeta,
19
+ } from './migrate-xds-declare-module.mjs';
20
+
21
+ import migrateXdsCssSurfaces, {
22
+ meta as migrateXdsCssSurfacesMeta,
23
+ } from './migrate-xds-css-surfaces.mjs';
24
+
13
25
  export default [
26
+ {
27
+ // XDS-prefix migration (P2380608025). Mandatory in v0.1.0: the release
28
+ // dropped the prefixed compatibility aliases (useXDSTheme, XDSButton,
29
+ // XDSIconRegistry, ...), so consumers upgrading from 0.0.x MUST rewrite
30
+ // prefixed imports to their bare names.
31
+ //
32
+ // Ordered BEFORE migrate-xds-module-specifiers so it sees `@xds/core`
33
+ // imports (its source matcher) and un-prefixes the identifiers first;
34
+ // the specifier codemod then rewrites the paths to `@astryxdesign/core`.
35
+ // The runner preserves this array order (see runner.mjs).
36
+ name: 'drop-xds-prefix-imports',
37
+ transform: dropXdsPrefixImports,
38
+ meta: dropXdsPrefixImportsMeta,
39
+ },
14
40
  {
15
41
  name: 'migrate-xds-module-specifiers',
16
42
  transform: migrateXdsModuleSpecifiers,
17
43
  meta: migrateXdsModuleSpecifiersMeta,
18
44
  },
45
+ {
46
+ // Cleans up TypeScript `declare module "@xds/core/..."` augmentations,
47
+ // which reference the package by string specifier and so are dead after
48
+ // the scope rename. Mandatory in v0.1.0. Ordered AFTER the specifier
49
+ // codemod: it uses the same @xds -> @astryxdesign mapping and repairs an
50
+ // adjacent surface the specifier codemod does not visit (module-decl ids
51
+ // are not import/export/require sources).
52
+ name: 'migrate-xds-declare-module',
53
+ transform: migrateXdsDeclareModule,
54
+ meta: migrateXdsDeclareModuleMeta,
55
+ },
56
+ {
57
+ // Rewrites consumer CSS surfaces broken by the v0.1.0 DOM-namespace rename
58
+ // (core no longer dual-emits `xds-*`): the `.xds-*` class-selector prefix,
59
+ // `[data-xds-*]` attribute selectors, and `@layer xds-*` layer names all
60
+ // become their `astryx-*` forms. Mandatory in v0.1.0.
61
+ name: 'migrate-xds-css-surfaces',
62
+ transform: migrateXdsCssSurfaces,
63
+ meta: migrateXdsCssSurfacesMeta,
64
+ },
19
65
  ];