@astryxdesign/cli 0.7.2-canary.c9f2659 → 0.7.2-canary.cbb7fa3

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 (72) hide show
  1. package/api/component/_adapter.d.mts +41 -1
  2. package/api/component/_adapter.mjs +93 -7
  3. package/api/component/component.mjs +22 -5
  4. package/api/component/component.test.mjs +420 -167
  5. package/api/component/integration-export-name.test.mjs +644 -0
  6. package/api/component/list/list.mjs +42 -4
  7. package/api/gap-report/gap-report.mjs +67 -4
  8. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  9. package/api/integration/add-contribution.mjs +7 -3
  10. package/api/integration/pack-check.mjs +61 -107
  11. package/api/integration/pack-check.test.mjs +359 -0
  12. package/api/search/search.d.mts +9 -0
  13. package/api/search/search.mjs +108 -16
  14. package/api/search/search.test.mjs +36 -0
  15. package/api/swizzle/copy/copy.mjs +28 -14
  16. package/api/template/template.test.mjs +120 -3
  17. package/api/theme/build/build.family.test.mjs +16 -0
  18. package/api/theme/build/build.mjs +77 -18
  19. package/api/theme/themeBuild.doc.mjs +3 -3
  20. package/assets/codemods/transforms/next/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  21. package/assets/codemods/transforms/next/index.mjs +11 -1
  22. package/assets/codemods/transforms/next/migrate-list-edge-compensation-to-edge.mjs +198 -0
  23. package/assets/docs/tree/add-a-component.doc.mjs +10 -0
  24. package/assets/docs/tree/checks.doc.mjs +1 -1
  25. package/assets/docs/tree/component-family.doc.mjs +5 -1
  26. package/assets/docs/tree/component-imports.doc.mjs +8 -0
  27. package/assets/docs/tree/single-component.doc.mjs +1 -1
  28. package/assets/docs/tree/troubleshooting.doc.mjs +9 -1
  29. package/assets/docs/tree/versioning.doc.mjs +1 -0
  30. package/assets/docs/working-with-ai.doc.mjs +43 -4
  31. package/assets/templates/blocks/components/Drawer/DrawerForm.doc.mjs +24 -0
  32. package/assets/templates/blocks/components/Drawer/DrawerForm.tsx +62 -0
  33. package/assets/templates/blocks/components/Drawer/DrawerScrim.doc.mjs +22 -0
  34. package/assets/templates/blocks/components/Drawer/DrawerScrim.tsx +68 -0
  35. package/assets/templates/blocks/components/Drawer/DrawerShowcase.doc.mjs +22 -0
  36. package/assets/templates/blocks/components/Drawer/DrawerShowcase.tsx +41 -0
  37. package/assets/templates/blocks/components/Drawer/DrawerStart.doc.mjs +21 -0
  38. package/assets/templates/blocks/components/Drawer/DrawerStart.tsx +41 -0
  39. package/assets/templates/blocks/components/Drawer/DrawerWidths.doc.mjs +22 -0
  40. package/assets/templates/blocks/components/Drawer/DrawerWidths.tsx +54 -0
  41. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.doc.mjs +15 -0
  42. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderShowcase.tsx +73 -0
  43. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.doc.mjs +22 -0
  44. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderSubtitleEndContent.tsx +49 -0
  45. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.doc.mjs +21 -0
  46. package/assets/templates/blocks/components/DrawerHeader/DrawerHeaderWithCloseButton.tsx +42 -0
  47. package/assets/templates/pages/detail-page/page.tsx +3 -3
  48. package/assets/templates/pages/table-inbox/page.tsx +10 -8
  49. package/authoring/doctypes/base/type.ts +6 -0
  50. package/authoring/doctypes/component/component.doc.mjs +4 -4
  51. package/authoring/doctypes/component/type.ts +6 -3
  52. package/clients/cli/commands/build-theme.path-safety.test.mjs +84 -0
  53. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  54. package/clients/cli/commands/theme-build.doc.mjs +2 -2
  55. package/clients/cli/lib/component-format.mjs +5 -1
  56. package/foundation/discovery/component-discovery.d.mts +172 -10
  57. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  58. package/foundation/discovery/component-discovery.mjs +863 -55
  59. package/foundation/discovery/component-discovery.test.mjs +96 -0
  60. package/foundation/discovery/component-replacement.mjs +18 -0
  61. package/foundation/discovery/component-replacement.test.mjs +40 -0
  62. package/foundation/discovery/module-exports.d.mts +27 -0
  63. package/foundation/discovery/module-exports.mjs +356 -31
  64. package/foundation/discovery/module-exports.test.mjs +172 -0
  65. package/foundation/discovery/template-adapter.mjs +74 -5
  66. package/foundation/doc-compiler/doc-loads.test.mjs +5 -1
  67. package/foundation/doc-compiler/inputs.mjs +23 -0
  68. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  69. package/foundation/integrations/contribution-inventory.mjs +6 -1
  70. package/foundation/xle/registry.mjs +4 -0
  71. package/foundation/xle/registry.test.mjs +28 -0
  72. package/package.json +10 -10
@@ -26,6 +26,8 @@
26
26
  * @property {string|null} sourcePath
27
27
  * @property {string|undefined} issuesUrl
28
28
  * @property {import('../../foundation/integrations/integrations.mjs').LoadedIntegration|null} integration
29
+ * @property {import('../../foundation/discovery/component-discovery.mjs').NameHold} hold
30
+ * how the owner holds the name, for the owner rule
29
31
  */
30
32
  /**
31
33
  * What ownership shaping needs from an owner. The core and legacy-external
@@ -138,6 +140,25 @@ export function assertUnambiguousOwners(owners: ComponentOwner[], dirName: strin
138
140
  * @returns {string | null}
139
141
  */
140
142
  export function resolveLegacyExternalDoc(ext: ExternalPackageRef, dirName: string): string | null;
143
+ /**
144
+ * Whether a legacy external package documents `dirName`.
145
+ * @param {string} cwd
146
+ * @param {string} dirName
147
+ * @returns {boolean}
148
+ */
149
+ export function legacyExternalDocuments(cwd: string, dirName: string): boolean;
150
+ /**
151
+ * Whether a legacy docs package keeps `dirName` over these owners, each of which
152
+ * holds it only as an export a doc records.
153
+ * @param {string} cwd
154
+ * @param {string} dirName
155
+ * @param {Array<{hold?: string, docPath?: string | null}>} owners
156
+ * @returns {boolean}
157
+ */
158
+ export function legacyExternalKeeps(cwd: string, dirName: string, owners: Array<{
159
+ hold?: string;
160
+ docPath?: string | null;
161
+ }>): boolean;
141
162
  /**
142
163
  * The core source file for `dirName` (the no-scope `--source` path reads core
143
164
  * directly — it does NOT fall back to externals or fuzzy search).
@@ -210,7 +231,20 @@ export function scopeSubComponent(docs: LoadedComponentDoc, dirName: string, cor
210
231
  };
211
232
  matchingComponent: any;
212
233
  } | null;
213
- export { CORE_PACKAGE };
234
+ /**
235
+ * The doc an integration owner shows for `dirName`: when `dirName` is a member
236
+ * its family doc lists under its own name (`AcmeSlide` in an `AcmeCarousel`
237
+ * doc), the doc scoped to that member as {@link scopeSubComponent} scopes
238
+ * Core's, keeping the import the family doc states; otherwise `docs` itself.
239
+ * @param {LoadedComponentDoc} docs
240
+ * @param {OwnershipSubject} owner
241
+ * @param {string} dirName
242
+ * @returns {LoadedComponentDoc & {parentDoc?: string, import?: string}}
243
+ */
244
+ export function scopeIntegrationMember(docs: LoadedComponentDoc, owner: OwnershipSubject, dirName: string): LoadedComponentDoc & {
245
+ parentDoc?: string;
246
+ import?: string;
247
+ };
214
248
  /**
215
249
  * Internal ambiguity marker. Single-component callers still receive the same
216
250
  * AstryxError code, message, and suggestions; batch callers can additionally
@@ -261,6 +295,10 @@ export type ComponentOwner = {
261
295
  sourcePath: string | null;
262
296
  issuesUrl: string | undefined;
263
297
  integration: import("../../foundation/integrations/integrations.mjs").LoadedIntegration | null;
298
+ /**
299
+ * how the owner holds the name, for the owner rule
300
+ */
301
+ hold: import("../../foundation/discovery/component-discovery.mjs").NameHold;
264
302
  };
265
303
  /**
266
304
  * What ownership shaping needs from an owner. The core and legacy-external
@@ -303,4 +341,6 @@ export type ResolvedUnscopedDoc = {
303
341
  resolvedSourcePath: string | null;
304
342
  };
305
343
  import { CORE_PACKAGE } from '../../foundation/discovery/component-discovery.mjs';
344
+ import { unscopedComponentOwners } from '../../foundation/discovery/component-discovery.mjs';
306
345
  import { AstryxError } from '../error.mjs';
346
+ export { CORE_PACKAGE, unscopedComponentOwners };
@@ -28,13 +28,17 @@ import {
28
28
  import {
29
29
  CORE_PACKAGE,
30
30
  discoverComponents,
31
+ docStem,
31
32
  findComponentReadme,
32
33
  findComponentSource,
33
34
  findExternalComponentDoc,
34
- findIntegrationComponentDoc,
35
- findIntegrationComponentSource,
35
+ findIntegrationComponent,
36
+ integrationMemberEntry,
37
+ isIntegrationMember,
38
+ recordedDocName,
36
39
  resolveImportPath,
37
40
  resolveIntegrationImportPath as resolveIntegrationImport,
41
+ unscopedComponentOwners,
38
42
  } from '../../foundation/discovery/component-discovery.mjs';
39
43
  import {Project} from '../../foundation/config/project.mjs';
40
44
  import {loadComponentDoc as loadValidatedComponentDoc} from '../../foundation/discovery/component-loader.mjs';
@@ -42,7 +46,7 @@ import {resolveComponentReplacements} from '../../foundation/discovery/component
42
46
  import {searchComponents} from '../../foundation/text/string-utils.mjs';
43
47
  import {AstryxError} from '../error.mjs';
44
48
 
45
- export {CORE_PACKAGE};
49
+ export {CORE_PACKAGE, unscopedComponentOwners};
46
50
 
47
51
  /**
48
52
  * A loaded component doc. The shared validated loader accepts stamped and legacy
@@ -71,6 +75,8 @@ export {CORE_PACKAGE};
71
75
  * @property {string|null} sourcePath
72
76
  * @property {string|undefined} issuesUrl
73
77
  * @property {import('../../foundation/integrations/integrations.mjs').LoadedIntegration|null} integration
78
+ * @property {import('../../foundation/discovery/component-discovery.mjs').NameHold} hold
79
+ * how the owner holds the name, for the owner rule
74
80
  */
75
81
 
76
82
  /**
@@ -240,17 +246,19 @@ export function resolveOwners(coreDir, dirName, loadedIntegrations) {
240
246
  sourcePath: findComponentSource(coreDir, dirName),
241
247
  issuesUrl: undefined,
242
248
  integration: null,
249
+ hold: 'core',
243
250
  });
244
251
  }
245
252
  for (const integration of loadedIntegrations) {
246
- const docPath = findIntegrationComponentDoc(integration, dirName);
247
- if (!docPath) continue;
253
+ const found = findIntegrationComponent(integration, dirName);
254
+ if (!found) continue;
248
255
  owners.push({
249
256
  package: integration.name,
250
- docPath,
251
- sourcePath: findIntegrationComponentSource(integration, dirName),
257
+ docPath: found.docPath,
258
+ sourcePath: found.sourcePath,
252
259
  issuesUrl: integration.issuesUrl,
253
260
  integration,
261
+ hold: found.hold,
254
262
  });
255
263
  }
256
264
  return owners;
@@ -361,6 +369,39 @@ export function resolveLegacyExternalDoc(ext, dirName) {
361
369
  return extDocPath && extDocPath.endsWith('.doc.mjs') ? extDocPath : null;
362
370
  }
363
371
 
372
+ /**
373
+ * Whether a legacy external package documents `dirName`.
374
+ * @param {string} cwd
375
+ * @param {string} dirName
376
+ * @returns {boolean}
377
+ */
378
+ export function legacyExternalDocuments(cwd, dirName) {
379
+ return discoverExternalPackages(cwd).some(
380
+ ext => resolveLegacyExternalDoc(ext, dirName) != null,
381
+ );
382
+ }
383
+
384
+ /**
385
+ * Whether a legacy docs package keeps `dirName` over these owners, each of which
386
+ * holds it only as an export a doc records.
387
+ * @param {string} cwd
388
+ * @param {string} dirName
389
+ * @param {Array<{hold?: string, docPath?: string | null}>} owners
390
+ * @returns {boolean}
391
+ */
392
+ export function legacyExternalKeeps(cwd, dirName, owners) {
393
+ return (
394
+ owners.length > 0 &&
395
+ owners.every(
396
+ owner =>
397
+ owner.hold === 'doc' &&
398
+ owner.docPath != null &&
399
+ docStem(owner.docPath).toLowerCase() !== dirName.toLowerCase(),
400
+ ) &&
401
+ legacyExternalDocuments(cwd, dirName)
402
+ );
403
+ }
404
+
364
405
  /**
365
406
  * The core source file for `dirName` (the no-scope `--source` path reads core
366
407
  * directly — it does NOT fall back to externals or fuzzy search).
@@ -583,3 +624,48 @@ export function scopeSubComponent(docs, dirName, coreDir) {
583
624
 
584
625
  return {scoped, matchingComponent};
585
626
  }
627
+
628
+ /**
629
+ * The doc an integration owner shows for `dirName`: when `dirName` is a member
630
+ * its family doc lists under its own name (`AcmeSlide` in an `AcmeCarousel`
631
+ * doc), the doc scoped to that member as {@link scopeSubComponent} scopes
632
+ * Core's, keeping the import the family doc states; otherwise `docs` itself.
633
+ * @param {LoadedComponentDoc} docs
634
+ * @param {OwnershipSubject} owner
635
+ * @param {string} dirName
636
+ * @returns {LoadedComponentDoc & {parentDoc?: string, import?: string}}
637
+ */
638
+ export function scopeIntegrationMember(docs, owner, dirName) {
639
+ const record = {name: dirName, docPath: owner.docPath ?? null};
640
+ if (!isIntegrationMember(record)) return docs;
641
+ const entry = integrationMemberEntry(docs, record);
642
+ // The files list the member but the doc this process loaded has no entry
643
+ // for it (the doc changed after it loaded): never answer with the family.
644
+ if (entry == null) {
645
+ throw new AstryxError(
646
+ `No component named "${dirName}": its family doc changed after this process loaded it.`,
647
+ undefined,
648
+ ERROR_CODES.ERR_UNKNOWN_COMPONENT,
649
+ );
650
+ }
651
+ // Built from the entry, as scopeSubComponent scopes Core's, so a member that
652
+ // shares the doc's own `name` is scoped too.
653
+ /** @type {LoadedComponentDoc & {parentDoc?: string, import?: string}} */
654
+ const scoped = {
655
+ name: dirName,
656
+ description: entry.description,
657
+ props: entry.props,
658
+ components: [entry],
659
+ // The name the family doc opens under, for every member.
660
+ parentDoc:
661
+ owner.integration && record.docPath
662
+ ? recordedDocName(owner.integration, record.docPath)
663
+ : docs.name === dirName && record.docPath
664
+ ? docStem(record.docPath)
665
+ : docs.name,
666
+ };
667
+ if (docs.usage) scoped.usage = docs.usage;
668
+ if (docs.theming) scoped.theming = docs.theming;
669
+ if (docs.import) scoped.import = docs.import;
670
+ return scoped;
671
+ }
@@ -19,9 +19,11 @@ import {
19
19
  requireCoreDir,
20
20
  loadIntegrationsSafely,
21
21
  resolveOwners,
22
+ scopeIntegrationMember,
22
23
  classifyScope,
23
24
  assertUnambiguousOwners,
24
25
  resolveLegacyExternalDoc,
26
+ legacyExternalKeeps,
25
27
  resolveCoreSourcePath,
26
28
  resolveUnscopedDoc,
27
29
  loadComponentDoc,
@@ -29,6 +31,7 @@ import {
29
31
  ComponentAmbiguityError,
30
32
  installedComponentPackageVersion,
31
33
  resolveComponentReplacement,
34
+ unscopedComponentOwners,
32
35
  } from './_adapter.mjs';
33
36
  import {componentList} from './list/list.mjs';
34
37
  import {componentDetail} from './detail/detail.mjs';
@@ -427,7 +430,13 @@ export async function component(name, options = {}) {
427
430
  if (blocks) {
428
431
  return componentDetailBlocks(dirName, cwd, owner.package);
429
432
  }
430
- const docs = await loadComponentDoc(owner.docPath, docOpts);
433
+ const loaded = await loadComponentDoc(owner.docPath, docOpts);
434
+ // A family member an integration lists under its own name reads as that
435
+ // member, the way a Core sub-component does below.
436
+ const docs =
437
+ scoped.kind === 'integration'
438
+ ? scopeIntegrationMember(loaded, owner, dirName)
439
+ : loaded;
431
440
  if (props) return componentDetailProps(docs, owner.package);
432
441
  return componentDetail(docs, owner, dirName, coreDir);
433
442
  }
@@ -481,12 +490,14 @@ export async function component(name, options = {}) {
481
490
  });
482
491
  }
483
492
 
493
+ // The owner rule runs first, over valid and invalid owners alike.
494
+ const ruled = unscopedComponentOwners(owners);
484
495
  // Invalid integration metadata does not create ambiguity against a valid
485
- // owner. Keep raw owners only when no doc owner is valid, so an integration-
496
+ // owner. Keep ruled owners only when no doc owner is valid, so an integration-
486
497
  // only component still exposes its source and returns ERR_INVALID_DOC for
487
498
  // detail instead of degrading to an unrelated unknown-component error.
488
499
  const validOwners = [];
489
- for (const owner of owners) {
500
+ for (const owner of ruled) {
490
501
  if (owner.package === CORE_PACKAGE) {
491
502
  validOwners.push(owner);
492
503
  continue;
@@ -498,7 +509,12 @@ export async function component(name, options = {}) {
498
509
  // Project/Doctor report invalid metadata; it is not an effective owner.
499
510
  }
500
511
  }
501
- const effectiveOwners = validOwners.length > 0 ? validOwners : owners;
512
+ const loadable = validOwners.length > 0 ? validOwners : ruled;
513
+ // A legacy docs package's exact doc keeps its name over an export name a doc
514
+ // records, as Core and file-named owners do.
515
+ const effectiveOwners = legacyExternalKeeps(cwd, dirName, loadable)
516
+ ? []
517
+ : loadable;
502
518
 
503
519
  // ── Ambiguity: owned by MORE THAN ONE valid package, no --package ──
504
520
  assertUnambiguousOwners(effectiveOwners, dirName);
@@ -516,7 +532,8 @@ export async function component(name, options = {}) {
516
532
  // Integration components don't carry showcases — fail without scanning.
517
533
  return componentDetailShowcase(dirName, {cwd, name, resolve: false, ownerPackage: owner.package});
518
534
  }
519
- const docs = await loadComponentDoc(owner.docPath, docOpts);
535
+ const loaded = await loadComponentDoc(owner.docPath, docOpts);
536
+ const docs = scopeIntegrationMember(loaded, owner, dirName);
520
537
  if (props) return componentDetailProps(docs, owner.package);
521
538
  return componentDetail(docs, owner, dirName, coreDir);
522
539
  }