@astryxdesign/cli 0.7.2-canary.ebcc404 → 0.7.2-canary.f8f7cf1

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 (134) hide show
  1. package/README.md +9 -8
  2. package/api/build/help/help.mjs +1 -1
  3. package/api/component/_adapter.d.mts +14 -0
  4. package/api/component/_adapter.mjs +48 -0
  5. package/api/component/component.mjs +10 -2
  6. package/api/component/integration-export-name.test.mjs +233 -8
  7. package/api/docs/_adapter.d.mts +11 -0
  8. package/api/docs/_adapter.mjs +244 -1
  9. package/api/docs/integrationDocs.test.mjs +124 -0
  10. package/api/doctor/theme-checks.mjs +1 -1
  11. package/api/gap-report/gap-report.mjs +2 -1
  12. package/api/index.d.mts +2 -0
  13. package/api/index.mjs +2 -0
  14. package/api/init/run/run.mjs +2 -2
  15. package/api/integration/_adapter.d.mts +12 -0
  16. package/api/integration/_adapter.mjs +54 -0
  17. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  18. package/api/integration/add-contribution.mjs +8 -4
  19. package/api/integration/add-contribution.test.mjs +4 -2
  20. package/api/integration/authoring-checks.mjs +11 -2
  21. package/api/integration/authoring-checks.test.mjs +39 -0
  22. package/api/integration/authoring-checks.type.d.mts +9 -0
  23. package/api/integration/authoring-checks.type.mjs +14 -1
  24. package/api/integration/check/check.d.mts +96 -0
  25. package/api/integration/check/check.mjs +357 -0
  26. package/api/integration/check/check.test.mjs +489 -0
  27. package/api/integration/integration-check.type.d.mts +118 -0
  28. package/api/integration/integration-check.type.mjs +84 -0
  29. package/api/integration/integrationCheck.doc.d.mts +10 -0
  30. package/api/integration/integrationCheck.doc.mjs +73 -0
  31. package/api/integration/integrationComponentConflicts.doc.mjs +36 -5
  32. package/api/integration/pack-check.mjs +48 -3
  33. package/api/integration/pack-check.test.mjs +247 -0
  34. package/api/integration/validate-integration.mjs +8 -0
  35. package/api/integration/validate-integration.test.mjs +11 -0
  36. package/api/integration/validate-integration.type.d.mts +8 -0
  37. package/api/integration/validate-integration.type.mjs +12 -1
  38. package/api/integration/validateIntegration.doc.mjs +17 -3
  39. package/api/search/search.integration-check.test.mjs +24 -0
  40. package/api/search/search.mjs +20 -8
  41. package/api/theme/build/build.family.test.mjs +54 -6
  42. package/api/theme/build/build.mjs +114 -17
  43. package/api/theme/themeBuild.doc.mjs +4 -4
  44. package/assets/docs/README.md +10 -0
  45. package/assets/docs/author-a-theme.doc.mjs +1 -1
  46. package/assets/docs/getting-started.doc.mjs +3 -3
  47. package/assets/docs/theme.doc.dense.mjs +1 -1
  48. package/assets/docs/theme.doc.mjs +1 -1
  49. package/assets/docs/theme.doc.zh.mjs +1 -1
  50. package/assets/docs/tree/add-a-component.doc.mjs +10 -0
  51. package/assets/docs/tree/block-template.doc.mjs +2 -2
  52. package/assets/docs/tree/check-the-props.doc.mjs +196 -0
  53. package/assets/docs/tree/check-your-docs.doc.mjs +44 -3
  54. package/assets/docs/tree/checks.doc.mjs +21 -13
  55. package/assets/docs/tree/component-family.doc.mjs +5 -1
  56. package/assets/docs/tree/component-imports.doc.mjs +1 -1
  57. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  58. package/assets/docs/tree/links.doc.mjs +2 -2
  59. package/assets/docs/tree/page-template.doc.mjs +1 -1
  60. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  61. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  62. package/assets/docs/tree/single-component.doc.mjs +3 -2
  63. package/assets/docs/tree/troubleshooting.doc.mjs +9 -1
  64. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  65. package/assets/docs/tree/versioning.doc.mjs +1 -0
  66. package/assets/docs/tree/write-the-template-file.doc.mjs +2 -1
  67. package/assets/docs/use-a-theme.doc.mjs +2 -2
  68. package/assets/theme.template.ts +1 -1
  69. package/authoring/doctypes/component/component.doc.mjs +1 -1
  70. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  71. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  72. package/authoring/doctypes/template/parse.d.mts +6 -2
  73. package/authoring/doctypes/template/parse.mjs +3 -1
  74. package/authoring/doctypes/template/template.doc.mjs +1 -1
  75. package/authoring/doctypes/template/type.ts +22 -0
  76. package/authoring/index.d.ts +1 -0
  77. package/authoring/integration/integration.doc.mjs +8 -1
  78. package/authoring/integration/parse.test.mjs +10 -0
  79. package/authoring/integration/schema.d.mts +2 -0
  80. package/authoring/integration/schema.mjs +1 -0
  81. package/authoring/integration/type.ts +4 -0
  82. package/clients/cli/commands/build-theme.batch-report.test.mjs +3 -1
  83. package/clients/cli/commands/build-theme.mjs +1 -1
  84. package/clients/cli/commands/build-theme.path-safety.test.mjs +155 -21
  85. package/clients/cli/commands/doctor-integration-components.doc.mjs +15 -4
  86. package/clients/cli/commands/doctor-integration-validate.doc.mjs +11 -2
  87. package/clients/cli/commands/doctor-integration.test.mjs +837 -0
  88. package/clients/cli/commands/doctor.mjs +190 -0
  89. package/clients/cli/commands/integration-check.doc.mjs +85 -0
  90. package/clients/cli/commands/integration-check.test.mjs +267 -0
  91. package/clients/cli/commands/integration.doc.mjs +6 -2
  92. package/clients/cli/commands/integration.mjs +107 -1
  93. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  94. package/clients/cli/commands/theme-build.doc.mjs +2 -2
  95. package/clients/cli/index.mjs +1 -0
  96. package/foundation/config/project.mjs +82 -0
  97. package/foundation/discovery/authoring-self-docs.mjs +1 -0
  98. package/foundation/discovery/component-discovery.d.mts +108 -17
  99. package/foundation/discovery/component-discovery.export-names.test.mjs +469 -2
  100. package/foundation/discovery/component-discovery.mjs +380 -47
  101. package/foundation/discovery/component-replacement.test.mjs +20 -0
  102. package/foundation/discovery/doc-types.d.mts +157 -0
  103. package/foundation/discovery/doc-types.mjs +1224 -0
  104. package/foundation/discovery/doc-types.test.mjs +652 -0
  105. package/foundation/discovery/docs-discovery.d.mts +81 -0
  106. package/foundation/discovery/docs-discovery.mjs +57 -0
  107. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  108. package/foundation/discovery/integration-doc-files.mjs +231 -0
  109. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  110. package/foundation/discovery/module-exports.d.mts +6 -0
  111. package/foundation/discovery/module-exports.mjs +15 -2
  112. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  113. package/foundation/discovery/package-tsconfig.mjs +228 -0
  114. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  115. package/foundation/discovery/project-typescript.d.mts +23 -0
  116. package/foundation/discovery/project-typescript.mjs +61 -0
  117. package/foundation/discovery/prop-drift.d.mts +278 -0
  118. package/foundation/discovery/prop-drift.mjs +2263 -0
  119. package/foundation/discovery/prop-drift.test.mjs +2219 -0
  120. package/foundation/discovery/type-match.d.mts +53 -0
  121. package/foundation/discovery/type-match.mjs +660 -0
  122. package/foundation/discovery/type-match.test.mjs +169 -0
  123. package/foundation/discovery/type-text.d.mts +68 -0
  124. package/foundation/discovery/type-text.mjs +658 -0
  125. package/foundation/discovery/type-text.test.mjs +196 -0
  126. package/foundation/discovery/unresolved-references.d.mts +62 -0
  127. package/foundation/discovery/unresolved-references.mjs +790 -0
  128. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  129. package/foundation/doc-compiler/doc-loads.test.mjs +6 -0
  130. package/foundation/doc-compiler/links.d.mts +9 -5
  131. package/foundation/doc-compiler/links.mjs +4 -2
  132. package/foundation/integrations/contribution-inventory.mjs +6 -1
  133. package/foundation/response/response-types.doc.mjs +7 -2
  134. package/package.json +9 -9
@@ -3,18 +3,22 @@
3
3
  /** @file CLI bindings for integration authoring and package verification. */
4
4
 
5
5
  import {integrationAdd} from '../../../api/integration/add-contribution.mjs';
6
+ import {integrationCheck} from '../../../api/integration/check/check.mjs';
6
7
  import {integrationPackCheck} from '../../../api/integration/pack-check.mjs';
7
8
  import {jsonOut} from '../../../foundation/response/json.mjs';
8
9
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
9
10
  import {NO_RESULT_SET} from '../../../foundation/debug/index.mjs';
10
- import {emit, list, section, text} from '../formatters/index.mjs';
11
+ import {emit, list, records, section, text} from '../formatters/index.mjs';
11
12
  import {cliError} from '../lib/cli-error.mjs';
12
13
  import {defineCommand} from '../lib/define-command.mjs';
13
14
  import {doc as integrationGroup} from './integration.doc.mjs';
14
15
  import {doc as integrationAddCommand} from './integration-add.doc.mjs';
16
+ import {doc as integrationCheckCommand} from './integration-check.doc.mjs';
15
17
  import {doc as integrationVerifyCommand} from './integration-verify.doc.mjs';
16
18
  import {doc as integrationPackCommand} from './integration-pack.doc.mjs';
17
19
  import {doc as integrationAddFn} from '../../../api/integration/integrationAdd.doc.mjs';
20
+ import {doc as integrationCheckFn} from '../../../api/integration/integrationCheck.doc.mjs';
21
+ import {printIntegrationCheckResponse} from './doctor.mjs';
18
22
  import {doc as integrationPackCheckFn} from '../../../api/integration/integrationPackCheck.doc.mjs';
19
23
 
20
24
  /**
@@ -88,6 +92,102 @@ async function verifyPackage(program) {
88
92
  return NO_RESULT_SET;
89
93
  }
90
94
 
95
+ /**
96
+ * Run `integration check` and print each check as its own subcommand prints
97
+ * it, then one summary: each check's errors, warnings and skipped work, and
98
+ * what decided the exit code.
99
+ * @param {import('commander').Command} program
100
+ * @param {{strict?: boolean}} options
101
+ */
102
+ async function checkPackage(program, options) {
103
+ const result = await integrationCheck({strict: options.strict === true});
104
+ if (program.opts().json) {
105
+ jsonOut(result);
106
+ } else {
107
+ const {data} = result;
108
+ const noPackage = data.checks[0]?.response.data.issues.some(
109
+ issue => issue.code === 'no_package',
110
+ );
111
+ if (noPackage) {
112
+ emit(
113
+ text(
114
+ 'Nothing was checked: no package.json found. Run this command inside a package directory.',
115
+ ),
116
+ );
117
+ } else if (!data.validated) {
118
+ emit(
119
+ text(
120
+ 'Nothing was checked: no astryx.integration.* found next to package.json. ' +
121
+ "Run this command in an integration package's folder.",
122
+ ),
123
+ );
124
+ } else {
125
+ data.checks.forEach((check, index) => {
126
+ // A blank line keeps each check's output apart from the next.
127
+ if (index > 0) emit(text(''));
128
+ printIntegrationCheckResponse(check.response);
129
+ });
130
+ }
131
+ const plural = (/** @type {number} */ n, /** @type {string} */ word) =>
132
+ `${n} ${word}${n === 1 ? '' : 's'}`;
133
+ const counts =
134
+ `${plural(data.errors, 'error')}, ${plural(data.warnings, 'warning')}, ` +
135
+ `${data.skipped.length} skipped`;
136
+ const notCompared =
137
+ data.unchecked > 0
138
+ ? ` ${plural(data.unchecked, 'item')} could not be compared; the checks above list them.`
139
+ : '';
140
+ const closing = noPackage
141
+ ? `[fail] ${counts}. No package.json was found here, so nothing was checked.`
142
+ : data.checks.some(check => check.failed)
143
+ ? `[fail] ${counts}. Address the items marked [fail] above.`
144
+ : data.failed
145
+ ? `[fail] ${counts}. --strict fails on every warning and on skipped work; fix them, or drop --strict to fail only on errors.`
146
+ : data.warnings > 0 || data.skipped.length > 0
147
+ ? `[ok] ${counts}. No check failed; review the warnings and skipped work above (--strict fails on them).${notCompared}`
148
+ : notCompared
149
+ ? `[ok] ${counts}. No check failed.${notCompared}`
150
+ : `[ok] ${counts}. Every check passed.`;
151
+ const identity = noPackage
152
+ ? '(no package.json)'
153
+ : data.version != null
154
+ ? `${data.name}@${data.version}`
155
+ : data.name;
156
+ emit(
157
+ // --strict lists each warning and each skipped check again, as the
158
+ // error it now is, in the issue format the checks use.
159
+ ...(data.failures.length > 0
160
+ ? [
161
+ section('Failing under --strict'),
162
+ records(data.failures, {
163
+ fields: [
164
+ 'severity',
165
+ 'check',
166
+ 'code',
167
+ 'component',
168
+ 'prop',
169
+ 'message',
170
+ 'fix',
171
+ 'file',
172
+ 'line',
173
+ ],
174
+ format: {severity: () => '[fail]'},
175
+ }),
176
+ ]
177
+ : []),
178
+ section(
179
+ `Integration check: ${identity ?? '(local package)'}${data.strict ? ' (strict)' : ''}`,
180
+ ),
181
+ records(data.checks, {
182
+ fields: ['check', 'failed', 'errors', 'warnings', 'skipped'],
183
+ }),
184
+ text(closing),
185
+ );
186
+ }
187
+ if (result.data.failed) process.exitCode = 1;
188
+ return NO_RESULT_SET;
189
+ }
190
+
91
191
  /** @param {import('commander').Command} program */
92
192
  export function registerIntegration(program) {
93
193
  /** @type {import('commander').Command} */
@@ -159,6 +259,12 @@ export function registerIntegration(program) {
159
259
  });
160
260
  addCmd.allowExcessArguments(false);
161
261
 
262
+ // It takes no package name, so a name passed by mistake is an error, not a
263
+ // silent check of the current folder.
264
+ defineCommand(integration, integrationCheckCommand, {
265
+ fn: integrationCheckFn,
266
+ action: async options => checkPackage(program, options),
267
+ }).allowExcessArguments(false);
162
268
  defineCommand(integration, integrationVerifyCommand, {
163
269
  fn: integrationPackCheckFn,
164
270
  action: async () => verifyPackage(program),
@@ -341,6 +341,11 @@ const CASES = [
341
341
  args: ['doctor', 'integration', 'validate'],
342
342
  skipFieldChecks: true,
343
343
  },
344
+ {
345
+ name: 'integration check',
346
+ args: ['integration', 'check'],
347
+ skipFieldChecks: true,
348
+ },
344
349
  {
345
350
  name: 'doctor integration components',
346
351
  args: ['doctor', 'integration', 'components'],
@@ -30,7 +30,7 @@ export const doc = {
30
30
  {
31
31
  flag: '--family',
32
32
  description:
33
- 'Build the selected extension family into one keyed CSS, JS, and declaration set, written beside the family root theme file, which must be inside the working directory (ERR_PATH_TRAVERSAL). Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
33
+ 'Build the selected extension family into one keyed CSS, JS, and declaration set, written beside the family root theme file, which must be inside the working directory to build (ERR_PATH_TRAVERSAL); --check only reads, so it compares outputs anywhere. Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
34
34
  },
35
35
  {
36
36
  flag: '--family-key <key>',
@@ -41,7 +41,7 @@ export const doc = {
41
41
  flag: '-o, --out <path>',
42
42
  param: 'options.out',
43
43
  description:
44
- 'Output CSS file path, relative or absolute; the .js and .d.ts are written beside it (default: <theme name>.css next to the source). Every output must resolve inside the working directory, the defaults beside the source too (ERR_PATH_TRAVERSAL). Single theme only: refused with more than one file or with --family (ERR_THEME_INVALID)',
44
+ 'Output CSS file path, relative or absolute; the .js and .d.ts are written beside it (default: <theme name>.css next to the source). A build writes only inside the working directory, the defaults beside the source too (ERR_PATH_TRAVERSAL); --check writes nothing, so it compares outputs anywhere and refuses only a relative path that escapes the working directory. Single theme only: refused with more than one file or with --family (ERR_THEME_INVALID)',
45
45
  },
46
46
  {
47
47
  flag: '--icons-specifier <specifier>',
@@ -98,6 +98,7 @@ export const JSON_SUPPORTED = new Set([
98
98
  'theme targets',
99
99
  'theme palette generate',
100
100
  'integration add',
101
+ 'integration check',
101
102
  'integration verify',
102
103
  'integration pack',
103
104
  'upgrade',
@@ -60,6 +60,7 @@ import {
60
60
  applyTemplateReplacements,
61
61
  effectiveTemplateDiscovery,
62
62
  discoverAllUnresolved as discoverTemplates,
63
+ discoverCoreTemplates,
63
64
  discoverIntegrationTemplatesForOne,
64
65
  } from '../discovery/template-adapter.mjs';
65
66
  import {
@@ -821,6 +822,85 @@ export class Project {
821
822
  });
822
823
  }
823
824
 
825
+ /**
826
+ * The components and templates this project reads, as links name them: by
827
+ * owner provider id, kind, and name (a template by its id).
828
+ * @returns {Promise<import('../discovery/docs-discovery.mjs').LinkableContributions>}
829
+ */
830
+ async #linkableContributions() {
831
+ /** @type {Map<string, string>} */
832
+ const providers = new Map([[CORE_PACKAGE, CORE_PACKAGE]]);
833
+ for (const integration of this.#loadedIntegrations) {
834
+ if (integration?.name) {
835
+ providers.set(
836
+ integration.name,
837
+ integration.providerId ?? integration.name,
838
+ );
839
+ }
840
+ }
841
+ const providerOf = (/** @type {string | undefined} */ pkg) =>
842
+ providers.get(pkg ?? CORE_PACKAGE) ?? pkg ?? CORE_PACKAGE;
843
+ // An active replacement answers to the Core name it replaces, so a link
844
+ // to that Core component has to select Core (spec:AST-035 FR11).
845
+ /** @type {Map<string, string>} */
846
+ const replacing = new Map();
847
+ if (this.#loadedIntegrations.length > 0) {
848
+ const {active} = await resolveComponentReplacements(
849
+ findCoreDir(this.#cwd),
850
+ this.#loadedIntegrations,
851
+ );
852
+ for (const each of active) {
853
+ replacing.set(`${each.package}\0${each.name}`, each.target);
854
+ }
855
+ }
856
+ const components = (await this.components()).map(record => {
857
+ const replaces = replacing.get(`${record.package}\0${record.name}`);
858
+ return {
859
+ providerId: providerOf(record.package),
860
+ package: record.package,
861
+ name: record.name,
862
+ docPath: record.docPath,
863
+ ...(replaces == null ? {} : {replaces}),
864
+ };
865
+ });
866
+ /** @param {any} template @param {string} pkg */
867
+ const linkable = (template, pkg) => ({
868
+ providerId: providerOf(pkg),
869
+ package: pkg,
870
+ type: template.type,
871
+ id: template.dirName,
872
+ name: template.name,
873
+ ...(template.displayName == null
874
+ ? {}
875
+ : {displayName: template.displayName}),
876
+ ...(template.description == null
877
+ ? {}
878
+ : {description: template.description}),
879
+ ...(typeof template.replaces === 'string'
880
+ ? {replaces: template.replaces}
881
+ : {}),
882
+ });
883
+ /** @param {any} template */
884
+ const isPageOrBlock = template =>
885
+ template.type === 'page' || template.type === 'block';
886
+ // Every Core template, a replaced one too, since `--package
887
+ // @astryxdesign/core` still opens it; everyone else's as unqualified
888
+ // lookups see them, an active replacement with the id it takes over.
889
+ const templates = [
890
+ ...(await discoverCoreTemplates())
891
+ .filter(isPageOrBlock)
892
+ .map(template => linkable(template, CORE_PACKAGE)),
893
+ .../** @type {any[]} */ (await this.templates())
894
+ .filter(
895
+ template =>
896
+ isPageOrBlock(template) &&
897
+ (template.package ?? CORE_PACKAGE) !== CORE_PACKAGE,
898
+ )
899
+ .map(template => linkable(template, template.package)),
900
+ ];
901
+ return {components, templates};
902
+ }
903
+
824
904
  /**
825
905
  * The CLI's own doc topics plus the ones the configured integrations
826
906
  * contribute, resolved into one catalog: additions, replacements (with the
@@ -838,6 +918,8 @@ export class Project {
838
918
  async docs() {
839
919
  return this.#memo('docs', async () => {
840
920
  const catalog = DocsCatalog.fromBuiltins();
921
+ // Links name component, page, and block docs too (spec:AST-047 FR9).
922
+ catalog.setContributions(() => this.#linkableContributions());
841
923
  let rank = 0;
842
924
 
843
925
  for (const integration of this.#loadedIntegrations) {
@@ -46,6 +46,7 @@ export const AUTHORING_SELF_DOCS = [
46
46
  'doctypes/reference/reference.doc.mjs',
47
47
  'doctypes/schema/schema.doc.mjs',
48
48
  'doctypes/template/template.doc.mjs',
49
+ 'doctypes/template/integration-template.doc.mjs',
49
50
  'doctypes/theme/theme.doc.mjs',
50
51
  ];
51
52
 
@@ -117,6 +117,58 @@ export function discoverExternalComponentsGrouped(docsDir: string): Record<strin
117
117
  export function findExternalComponentDoc(docsDir: string, name: string): string | null;
118
118
  /** @param {string} docPath */
119
119
  export function docStem(docPath: string): string;
120
+ /**
121
+ * The `.tsx` file in `folder`, or below it, that declares `name`, taking files
122
+ * in path order: the source of a member whose import never reaches a `.tsx`
123
+ * file, as when the import names built output.
124
+ *
125
+ * @param {string} folder
126
+ * @param {string} name
127
+ * @returns {string | null}
128
+ */
129
+ export function folderSourceFor(folder: string, name: string): string | null;
130
+ /**
131
+ * Each family doc under an integration's components root that names an
132
+ * import, with the file the import resolves to: null when it resolves to none,
133
+ * such as build output that does not exist yet.
134
+ *
135
+ * @param {{name: string, components?: string, issuesUrl?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
136
+ * @returns {Array<{docPath: string, module: string | null}>}
137
+ */
138
+ export function familyImports(integration: {
139
+ name: string;
140
+ components?: string;
141
+ issuesUrl?: string;
142
+ __packageDir?: string;
143
+ __packageExports?: Record<string, unknown> | null;
144
+ }): Array<{
145
+ docPath: string;
146
+ module: string | null;
147
+ }>;
148
+ /**
149
+ * Whether a discovered integration record is a member its family doc lists,
150
+ * decided from the files, not from whether the doc loads.
151
+ *
152
+ * @param {{name: string, docPath: string|null}} record
153
+ * @returns {boolean}
154
+ */
155
+ export function isIntegrationMember(record: {
156
+ name: string;
157
+ docPath: string | null;
158
+ }): boolean;
159
+ /**
160
+ * The entry a family doc gives a member that discovery lists under its own
161
+ * name, or null when the record is the doc's own component or the loaded doc
162
+ * has no entry for it.
163
+ *
164
+ * @param {any} doc the doc loaded from `record.docPath`
165
+ * @param {{name: string, docPath: string|null}} record
166
+ * @returns {any}
167
+ */
168
+ export function integrationMemberEntry(doc: any, record: {
169
+ name: string;
170
+ docPath: string | null;
171
+ }): any;
120
172
  /**
121
173
  * Visit every component doc under an integration's components root, hidden
122
174
  * ones included, in discovery order, with its stem.
@@ -124,15 +176,24 @@ export function docStem(docPath: string): string;
124
176
  * @param {(docPath: string, stem: string) => void} visit
125
177
  */
126
178
  export function forEachIntegrationDoc(componentsDir: string | undefined, visit: (docPath: string, stem: string) => void): void;
179
+ /**
180
+ * Given an integration component doc path, return the sibling component source
181
+ * (`{Name}.tsx`) if one exists, else null.
182
+ *
183
+ * @param {string} docPath absolute path to a `{Name}.doc.{ts,mjs,js}` file
184
+ * @returns {string|null}
185
+ */
186
+ export function integrationSourceForDoc(docPath: string): string | null;
127
187
  /**
128
188
  * Discover ownership records for the components contributed by a single loaded
129
189
  * integration. Scans the integration's resolved `components` dir for same-stem
130
190
  * doc files and records each with owner package + issuesUrl + sourcePath. A doc
131
191
  * that names a differently named export of its file is recorded under that
132
- * export unless the name is taken: every doc's file name (a hidden doc's too),
133
- * a Core name a doc in the package replaces, and a name an earlier doc records.
192
+ * export, and each family member it lists, under the member's name, unless the
193
+ * name is taken: every doc's file name (a hidden doc's too), a Core name a doc
194
+ * in the package replaces, and a name an earlier doc records.
134
195
  *
135
- * @param {{name: string, components?: string, issuesUrl?: string}} integration
196
+ * @param {{name: string, components?: string, issuesUrl?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
136
197
  * a single entry from `loadedIntegrations` (lib/integrations.mjs)
137
198
  * @param {{documentedNames?: boolean}} [options] `documentedNames: false`
138
199
  * records each doc under its file name alone, without reading the names
@@ -143,6 +204,8 @@ export function discoverIntegrationComponents(integration: {
143
204
  name: string;
144
205
  components?: string;
145
206
  issuesUrl?: string;
207
+ __packageDir?: string;
208
+ __packageExports?: Record<string, unknown> | null;
146
209
  }, { documentedNames }?: {
147
210
  documentedNames?: boolean;
148
211
  }): Array<{
@@ -158,14 +221,16 @@ export function discoverIntegrationComponents(integration: {
158
221
  * Invalid metadata or a missing same-stem source removes only that component;
159
222
  * callers can report the returned errors while retaining valid siblings. A
160
223
  * component whose doc sets `replaces` carries the value as written; the
161
- * component replacement resolver decides what it means. A doc that fails to
162
- * load is reported under its file name: what it says cannot be trusted.
224
+ * component replacement resolver decides what it means. A family member is
225
+ * read through its family's doc: it never carries the family's `replaces`, and
226
+ * a family doc that fails to load is reported once. A doc that fails to load
227
+ * is reported under its file name: what it says cannot be trusted.
163
228
  *
164
229
  * @param {{name: string, components?: string, issuesUrl?: string}} integration
165
230
  * @param {{documentedNames?: boolean}} [options] as for
166
231
  * {@link discoverIntegrationComponents}
167
232
  * @returns {Promise<{
168
- * components: Array<{name: string, package: string, docPath: string, sourcePath: string, issuesUrl: string|undefined, group: string|null, replaces?: unknown}>,
233
+ * components: Array<{name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null, replaces?: unknown}>,
169
234
  * discovered: Array<{name: string, package: string, docPath: string, sourcePath: string|null, issuesUrl: string|undefined, group: string|null}>,
170
235
  * errors: Array<{name: string, message: string}>,
171
236
  * }>}
@@ -181,7 +246,7 @@ export function discoverValidIntegrationComponents(integration: {
181
246
  name: string;
182
247
  package: string;
183
248
  docPath: string;
184
- sourcePath: string;
249
+ sourcePath: string | null;
185
250
  issuesUrl: string | undefined;
186
251
  group: string | null;
187
252
  replaces?: unknown;
@@ -203,8 +268,8 @@ export function discoverValidIntegrationComponents(integration: {
203
268
  * How an owner holds a component name, which the owner rule
204
269
  * ({@link unscopedComponentOwners}) reads: `core` when Core documents it;
205
270
  * `file` when an integration doc named after its file is recorded under it;
206
- * `doc` when only a doc records it: an export name, or the file name of a doc
207
- * recorded under another export.
271
+ * `doc` when only a doc records it: an export name, a member, or the file name
272
+ * of a doc recorded under another export.
208
273
  * @typedef {'core' | 'file' | 'doc'} NameHold
209
274
  */
210
275
  /**
@@ -218,43 +283,69 @@ export function integrationRecordHold(record: {
218
283
  }): "file" | "doc";
219
284
  /**
220
285
  * The integration component a name finds, as discovery records it, and its
221
- * hold: the same-stem doc first, then a doc that records the name.
286
+ * hold: the same-stem doc first, then a doc that records the name as an export
287
+ * or a member.
222
288
  *
223
- * @param {{components?: string}} integration
289
+ * @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
224
290
  * @param {string} name
225
291
  * @returns {{docPath: string, sourcePath: string|null, hold: 'file' | 'doc'} | null}
226
292
  */
227
293
  export function findIntegrationComponent(integration: {
294
+ name?: string;
228
295
  components?: string;
296
+ __packageDir?: string;
297
+ __packageExports?: Record<string, unknown> | null;
229
298
  }, name: string): {
230
299
  docPath: string;
231
300
  sourcePath: string | null;
232
301
  hold: "file" | "doc";
233
302
  } | null;
303
+ /**
304
+ * The name discovery records a component doc under: the export its `name`
305
+ * documents when the doc is recorded under it, else the doc's file name.
306
+ *
307
+ * @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
308
+ * @param {string} docPath
309
+ * @returns {string}
310
+ */
311
+ export function recordedDocName(integration: {
312
+ name?: string;
313
+ components?: string;
314
+ __packageDir?: string;
315
+ __packageExports?: Record<string, unknown> | null;
316
+ }, docPath: string): string;
234
317
  /**
235
318
  * Find an integration component's doc file by name within a loaded
236
319
  * integration's resolved `components` dir. Honors the same-stem convention
237
320
  * (`{Name}.doc.{ts,mjs,js}`), preferring `.ts` → `.mjs` → `.js`, and finds a
238
- * doc that records the component under a differently named export of its file.
321
+ * doc that records the component under another name: a differently named
322
+ * export of its file, or a member its family doc lists.
239
323
  *
240
- * @param {{components?: string}} integration
324
+ * @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
241
325
  * @param {string} name bare component name (no `XDS`/`Astryx` prefix)
242
326
  * @returns {string|null}
243
327
  */
244
328
  export function findIntegrationComponentDoc(integration: {
329
+ name?: string;
245
330
  components?: string;
331
+ __packageDir?: string;
332
+ __packageExports?: Record<string, unknown> | null;
246
333
  }, name: string): string | null;
247
334
  /**
248
335
  * Find an integration component's swizzleable source file by name within a
249
336
  * loaded integration's resolved `components` dir: the same-stem `{Name}.tsx`
250
- * of its doc. Returns null when the integration ships docs without source.
337
+ * of its doc, or, for a family member, the file that declares it. Returns null
338
+ * when the integration ships docs without source.
251
339
  *
252
- * @param {{components?: string}} integration
340
+ * @param {{name?: string, components?: string, __packageDir?: string, __packageExports?: Record<string, unknown>|null}} integration
253
341
  * @param {string} name bare component name
254
342
  * @returns {string|null}
255
343
  */
256
344
  export function findIntegrationComponentSource(integration: {
345
+ name?: string;
257
346
  components?: string;
347
+ __packageDir?: string;
348
+ __packageExports?: Record<string, unknown> | null;
258
349
  }, name: string): string | null;
259
350
  /**
260
351
  * The owner rule: an owner that holds a name only because a doc records it
@@ -302,7 +393,7 @@ export const CORE_PACKAGE: import("../../authoring/index.js").ProviderId;
302
393
  * How an owner holds a component name, which the owner rule
303
394
  * ({@link unscopedComponentOwners}) reads: `core` when Core documents it;
304
395
  * `file` when an integration doc named after its file is recorded under it;
305
- * `doc` when only a doc records it: an export name, or the file name of a doc
306
- * recorded under another export.
396
+ * `doc` when only a doc records it: an export name, a member, or the file name
397
+ * of a doc recorded under another export.
307
398
  */
308
399
  export type NameHold = "core" | "file" | "doc";