@astryxdesign/cli 0.7.2-canary.e2909cf → 0.7.2-canary.e4bdd70

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 (194) hide show
  1. package/README.md +15 -14
  2. package/api/build/help/help.mjs +1 -1
  3. package/api/component/_adapter.d.mts +41 -1
  4. package/api/component/_adapter.mjs +93 -7
  5. package/api/component/component.mjs +22 -5
  6. package/api/component/integration-export-name.test.mjs +644 -0
  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 +67 -4
  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 +51 -97
  33. package/api/integration/pack-check.test.mjs +359 -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.d.mts +22 -6
  40. package/api/search/search.domain-priority.test.mjs +93 -18
  41. package/api/search/search.integration-check.test.mjs +24 -0
  42. package/api/search/search.mjs +133 -22
  43. package/api/swizzle/copy/copy.mjs +28 -14
  44. package/api/theme/build/build.family.test.mjs +54 -6
  45. package/api/theme/build/build.mjs +114 -17
  46. package/api/theme/themeBuild.doc.mjs +4 -4
  47. package/assets/docs/README.md +10 -0
  48. package/assets/docs/author-a-theme.doc.mjs +1 -1
  49. package/assets/docs/getting-started.doc.mjs +3 -3
  50. package/assets/docs/styling-overview.doc.mjs +1 -1
  51. package/assets/docs/theme.doc.dense.mjs +1 -1
  52. package/assets/docs/theme.doc.mjs +1 -1
  53. package/assets/docs/theme.doc.zh.mjs +1 -1
  54. package/assets/docs/tree/add-a-component.doc.mjs +11 -1
  55. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  56. package/assets/docs/tree/add-a-topic.doc.mjs +10 -7
  57. package/assets/docs/tree/agent-guidance.doc.mjs +2 -2
  58. package/assets/docs/tree/block-template.doc.mjs +8 -20
  59. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  60. package/assets/docs/tree/check-the-props.doc.mjs +196 -0
  61. package/assets/docs/tree/check-your-docs.doc.mjs +51 -5
  62. package/assets/docs/tree/checks.doc.mjs +24 -16
  63. package/assets/docs/tree/codemods.doc.mjs +4 -4
  64. package/assets/docs/tree/component-family.doc.mjs +8 -6
  65. package/assets/docs/tree/component-imports.doc.mjs +10 -2
  66. package/assets/docs/tree/components.doc.mjs +1 -1
  67. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +7 -18
  68. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  69. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  70. package/assets/docs/tree/docs.doc.mjs +3 -3
  71. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  72. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  73. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  74. package/assets/docs/tree/extend-or-replace.doc.mjs +6 -6
  75. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  76. package/assets/docs/tree/generate-a-palette.doc.mjs +5 -3
  77. package/assets/docs/tree/grade-template-with-agent.doc.mjs +1 -1
  78. package/assets/docs/tree/integrations.doc.mjs +8 -0
  79. package/assets/docs/tree/links.doc.mjs +6 -5
  80. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  81. package/assets/docs/tree/page-template.doc.mjs +4 -8
  82. package/assets/docs/tree/publishing.doc.mjs +1 -1
  83. package/assets/docs/tree/quick-start.doc.mjs +37 -21
  84. package/assets/docs/tree/replace-a-core-component.doc.mjs +4 -6
  85. package/assets/docs/tree/replace-a-core-template.doc.mjs +6 -8
  86. package/assets/docs/tree/sections-and-placement.doc.mjs +4 -6
  87. package/assets/docs/tree/see-it-in-an-app.doc.mjs +4 -2
  88. package/assets/docs/tree/short-and-findable.doc.mjs +6 -5
  89. package/assets/docs/tree/single-component.doc.mjs +15 -6
  90. package/assets/docs/tree/start-a-template.doc.mjs +3 -3
  91. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  92. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  93. package/assets/docs/tree/template-doc-overview.doc.mjs +4 -15
  94. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  95. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  96. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  97. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  98. package/assets/docs/tree/templates.doc.mjs +1 -1
  99. package/assets/docs/tree/test-in-an-app.doc.mjs +3 -3
  100. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  101. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  102. package/assets/docs/tree/troubleshooting.doc.mjs +13 -5
  103. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  104. package/assets/docs/tree/upgrading.doc.mjs +3 -3
  105. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +3 -1
  106. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  107. package/assets/docs/tree/versioning.doc.mjs +7 -6
  108. package/assets/docs/tree/write-the-template-file.doc.mjs +4 -3
  109. package/assets/docs/use-a-theme.doc.mjs +2 -2
  110. package/assets/theme.template.ts +1 -1
  111. package/authoring/codemod/codemod.doc.mjs +7 -3
  112. package/authoring/config/config.doc.mjs +9 -6
  113. package/authoring/config/type.ts +4 -2
  114. package/authoring/discover/discover.doc.mjs +1 -1
  115. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  116. package/authoring/doctypes/command/command.doc.mjs +14 -12
  117. package/authoring/doctypes/component/component.doc.mjs +29 -26
  118. package/authoring/doctypes/component/type.ts +13 -8
  119. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  120. package/authoring/doctypes/function/function.doc.mjs +9 -8
  121. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  122. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  123. package/authoring/doctypes/reference/reference.doc.mjs +11 -10
  124. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  125. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  126. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  127. package/authoring/doctypes/template/parse.d.mts +6 -2
  128. package/authoring/doctypes/template/parse.mjs +3 -1
  129. package/authoring/doctypes/template/template.doc.mjs +3 -3
  130. package/authoring/doctypes/template/type.ts +22 -0
  131. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  132. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  133. package/authoring/identity/identity.doc.mjs +3 -3
  134. package/authoring/index.d.ts +1 -0
  135. package/authoring/integration/integration.doc.mjs +12 -4
  136. package/authoring/integration/parse.test.mjs +10 -0
  137. package/authoring/integration/schema.d.mts +2 -0
  138. package/authoring/integration/schema.mjs +1 -0
  139. package/authoring/integration/type.ts +4 -0
  140. package/clients/cli/commands/build-theme.batch-report.test.mjs +3 -1
  141. package/clients/cli/commands/build-theme.mjs +1 -1
  142. package/clients/cli/commands/build-theme.path-safety.test.mjs +155 -21
  143. package/clients/cli/commands/doctor-integration-components.doc.mjs +15 -4
  144. package/clients/cli/commands/doctor-integration-validate.doc.mjs +11 -2
  145. package/clients/cli/commands/doctor-integration.test.mjs +837 -0
  146. package/clients/cli/commands/doctor.mjs +190 -0
  147. package/clients/cli/commands/integration-check.doc.mjs +85 -0
  148. package/clients/cli/commands/integration-check.test.mjs +267 -0
  149. package/clients/cli/commands/integration.doc.mjs +6 -2
  150. package/clients/cli/commands/integration.mjs +107 -1
  151. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  152. package/clients/cli/commands/theme-build.doc.mjs +2 -2
  153. package/clients/cli/index.mjs +1 -0
  154. package/foundation/config/project.mjs +82 -0
  155. package/foundation/discovery/authoring-self-docs.mjs +2 -1
  156. package/foundation/discovery/component-discovery.d.mts +178 -10
  157. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  158. package/foundation/discovery/component-discovery.mjs +659 -62
  159. package/foundation/discovery/component-replacement.mjs +18 -0
  160. package/foundation/discovery/component-replacement.test.mjs +40 -0
  161. package/foundation/discovery/doc-types.d.mts +157 -0
  162. package/foundation/discovery/doc-types.mjs +1224 -0
  163. package/foundation/discovery/doc-types.test.mjs +652 -0
  164. package/foundation/discovery/docs-discovery.d.mts +81 -0
  165. package/foundation/discovery/docs-discovery.mjs +57 -0
  166. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  167. package/foundation/discovery/integration-doc-files.mjs +231 -0
  168. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  169. package/foundation/discovery/module-exports.d.mts +27 -0
  170. package/foundation/discovery/module-exports.mjs +356 -31
  171. package/foundation/discovery/module-exports.test.mjs +172 -0
  172. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  173. package/foundation/discovery/package-tsconfig.mjs +228 -0
  174. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  175. package/foundation/discovery/project-typescript.d.mts +23 -0
  176. package/foundation/discovery/project-typescript.mjs +61 -0
  177. package/foundation/discovery/prop-drift.d.mts +278 -0
  178. package/foundation/discovery/prop-drift.mjs +2263 -0
  179. package/foundation/discovery/prop-drift.test.mjs +2219 -0
  180. package/foundation/discovery/type-match.d.mts +53 -0
  181. package/foundation/discovery/type-match.mjs +660 -0
  182. package/foundation/discovery/type-match.test.mjs +169 -0
  183. package/foundation/discovery/type-text.d.mts +68 -0
  184. package/foundation/discovery/type-text.mjs +658 -0
  185. package/foundation/discovery/type-text.test.mjs +196 -0
  186. package/foundation/discovery/unresolved-references.d.mts +62 -0
  187. package/foundation/discovery/unresolved-references.mjs +790 -0
  188. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  189. package/foundation/doc-compiler/doc-loads.test.mjs +6 -0
  190. package/foundation/doc-compiler/links.d.mts +9 -5
  191. package/foundation/doc-compiler/links.mjs +4 -2
  192. package/foundation/integrations/contribution-inventory.mjs +6 -1
  193. package/foundation/response/response-types.doc.mjs +7 -2
  194. package/package.json +9 -9
@@ -0,0 +1,357 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `astryx integration check`: every `doctor integration` check in one
5
+ * call, for a package's own build.
6
+ *
7
+ * Runs `validate`, `components`, `templates` and `docs` against the
8
+ * integration package in `cwd` and counts each result the way that check's
9
+ * own subcommand exits, so the
10
+ * run fails exactly when one of them would. With `strict` it also fails on
11
+ * any warning and on any check, or comparison a check carries, that did not
12
+ * run, so a package can gate its build on its docs staying in sync. `--strict`
13
+ * adds no check: it reports the same warnings and skipped work again, as
14
+ * errors in the checks' own issue format, and fails on them.
15
+ *
16
+ * What fails it is a contract for the builds that run it: a new check, or a
17
+ * new failure code, joins only in a minor release that names it.
18
+ * `test/integration-check-failures.test.mjs` lists each one, so adding one
19
+ * shows up in review.
20
+ *
21
+ * @input A local or installed integration package.
22
+ * @output Each check's own envelope with its errors, warnings and skipped
23
+ * work, the totals, and whether the run fails.
24
+ * @position Backs `astryx integration check`.
25
+ */
26
+
27
+ import {
28
+ integrationComponentConflicts,
29
+ integrationDocConflicts,
30
+ integrationTemplateConflicts,
31
+ } from '../authoring-checks.mjs';
32
+ import {
33
+ summarizeIssues,
34
+ validateIntegration,
35
+ } from '../validate-integration.mjs';
36
+
37
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckName} IntegrationCheckName */
38
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckReport} IntegrationCheckReport */
39
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckResponse} IntegrationCheckResponse */
40
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckResult} IntegrationCheckResult */
41
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckOptions} IntegrationCheckOptions */
42
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckCommandResponse} IntegrationCheckCommandResponse */
43
+ /** @typedef {import('../integration-check.type.mjs').IntegrationCheckFailure} IntegrationCheckFailure */
44
+
45
+ /** The checks `integration check` runs, in the order it runs them. */
46
+ export const INTEGRATION_CHECKS = /** @type {const} */ ([
47
+ 'validate',
48
+ 'components',
49
+ 'templates',
50
+ 'docs',
51
+ ]);
52
+
53
+ /** The skipped-work line for a package with no integration manifest. */
54
+ export const NO_MANIFEST_SKIP =
55
+ 'No astryx.integration.* found next to package.json, so nothing was checked.';
56
+
57
+ /**
58
+ * The code a conflict carries when `--strict` reports it: conflicts have no
59
+ * code of their own.
60
+ */
61
+ export const CONFLICT_CODES = /** @type {const} */ ({
62
+ components: 'component_name_conflict',
63
+ templates: 'template_id_conflict',
64
+ });
65
+
66
+ /** The code a check with no integration manifest carries under `--strict`. */
67
+ export const NO_MANIFEST_CODE = 'missing_manifest';
68
+
69
+ /**
70
+ * The code a skipped comparison carries under `--strict`: its key in the
71
+ * check's result, in snake case, then `_skipped` (`propDrift` is
72
+ * `prop_drift_skipped`).
73
+ * @param {string} key
74
+ */
75
+ export function skippedCode(key) {
76
+ return `${key.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase()}_skipped`;
77
+ }
78
+
79
+ /** Each check's envelope type, by check. */
80
+ const CHECK_OF_TYPE = /** @type {const} */ ({
81
+ 'integration.validate': 'validate',
82
+ 'integration.component-conflicts': 'components',
83
+ 'integration.template-conflicts': 'templates',
84
+ 'integration.doc-conflicts': 'docs',
85
+ });
86
+
87
+ /**
88
+ * Whether a value is a report a check carries beside its issues: a
89
+ * comparison with a `status` of `checked` or `skipped` and its `findings`.
90
+ * @param {unknown} value
91
+ * @returns {value is IntegrationCheckReport}
92
+ */
93
+ export function isCheckReport(value) {
94
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
95
+ return false;
96
+ }
97
+ const report = /** @type {{status?: unknown, findings?: unknown}} */ (value);
98
+ return (
99
+ (report.status === 'checked' || report.status === 'skipped') &&
100
+ Array.isArray(report.findings)
101
+ );
102
+ }
103
+
104
+ /**
105
+ * One finding as an issue: its own code, message, fix, file, and line where
106
+ * it has them.
107
+ * @param {any} finding
108
+ * @param {string} code
109
+ * @returns {Omit<IntegrationCheckFailure, 'check' | 'severity'>}
110
+ */
111
+ function asIssue(finding, code) {
112
+ const file = finding.file ?? finding.doc ?? finding.source;
113
+ return {
114
+ code,
115
+ message: String(finding.message ?? ''),
116
+ ...(typeof finding.component === 'string'
117
+ ? {component: finding.component}
118
+ : {}),
119
+ ...(typeof finding.prop === 'string' ? {prop: finding.prop} : {}),
120
+ ...(typeof finding.fix === 'string' ? {fix: finding.fix} : {}),
121
+ ...(typeof file === 'string' ? {file} : {}),
122
+ ...(typeof finding.line === 'number' ? {line: finding.line} : {}),
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Every warning in one check's result, and every piece of work it skipped,
128
+ * as issues: what `--strict` reports as errors. The counts come from the
129
+ * same list, so the default run and the strict run read the same data.
130
+ * @param {IntegrationCheckName} check
131
+ * @param {IntegrationCheckResponse} response
132
+ * @param {{noPackage?: boolean}} [context] `noPackage`: validate found no
133
+ * package.json, which only it reports; then no other check names a
134
+ * missing manifest as skipped work
135
+ * @returns {{warnings: Array<Omit<IntegrationCheckFailure, 'check' | 'severity'>>, skipped: Array<Omit<IntegrationCheckFailure, 'check' | 'severity'>>}}
136
+ */
137
+ export function strictIssues(check, response, context = {}) {
138
+ const data =
139
+ /** @type {Record<string, unknown> & IntegrationCheckResponse['data']} */ (
140
+ response.data
141
+ );
142
+ const warnings = [];
143
+ for (const issue of data.issues) {
144
+ if (issue.severity === 'warning') {
145
+ warnings.push(asIssue(issue, issue.code));
146
+ }
147
+ }
148
+ const conflictCode = /** @type {Record<string, string>} */ (CONFLICT_CODES)[
149
+ check
150
+ ];
151
+ for (const conflict of /** @type {any[]} */ (
152
+ Array.isArray(data.conflicts) ? data.conflicts : []
153
+ )) {
154
+ if (conflict.severity === 'warning' && conflictCode) {
155
+ warnings.push(asIssue(conflict, conflictCode));
156
+ }
157
+ }
158
+ for (const finding of /** @type {any[]} */ (
159
+ Array.isArray(data.findings) ? data.findings : []
160
+ )) {
161
+ if (finding.severity === 'warning') {
162
+ warnings.push(asIssue(finding, finding.code ?? finding.relationship));
163
+ }
164
+ }
165
+ const skipped = [];
166
+ const noPackage =
167
+ context.noPackage === true ||
168
+ data.issues.some(issue => issue.code === 'no_package');
169
+ if (!data.validated && !noPackage) {
170
+ skipped.push({code: NO_MANIFEST_CODE, message: NO_MANIFEST_SKIP});
171
+ }
172
+ for (const [key, value] of Object.entries(data)) {
173
+ if (!isCheckReport(value)) continue;
174
+ if (value.status === 'skipped') {
175
+ skipped.push({
176
+ code: skippedCode(key),
177
+ message: value.reason ?? `${key} did not run`,
178
+ });
179
+ continue;
180
+ }
181
+ for (const finding of /** @type {any[]} */ (value.findings)) {
182
+ if ((finding.severity ?? 'warning') !== 'warning') continue;
183
+ warnings.push(
184
+ asIssue(finding, finding.code ?? finding.problem ?? finding.kind),
185
+ );
186
+ }
187
+ // A comparison that ran but judged no prop type (a TypeScript older than
188
+ // the type comparison reads) skipped that work.
189
+ const typesSkipped = /** @type {{typesSkipped?: unknown}} */ (value)
190
+ .typesSkipped;
191
+ if (typeof typesSkipped === 'string') {
192
+ skipped.push({
193
+ code: 'prop_types_skipped',
194
+ message: `prop types not judged: ${typesSkipped}`,
195
+ });
196
+ }
197
+ }
198
+ return {warnings, skipped};
199
+ }
200
+
201
+ /**
202
+ * Count one check's result the way its own subcommand exits. Errors are what
203
+ * make it exit 1: error-severity issues, and for `docs` error findings.
204
+ * Warnings are warning issues, conflicts and findings, and the findings of
205
+ * any report the check carries. Skipped names work that did not run: no
206
+ * integration manifest, or a report that was skipped.
207
+ * @param {IntegrationCheckResponse} response
208
+ * @param {{noPackage?: boolean}} [context] as {@link strictIssues} takes it
209
+ * @returns {Omit<IntegrationCheckResult, 'check' | 'response'>}
210
+ */
211
+ export function summarizeCheck(response, context = {}) {
212
+ const check = /** @type {IntegrationCheckName} */ (
213
+ CHECK_OF_TYPE[response.type]
214
+ );
215
+ let errors = summarizeIssues(response.data.issues).errors;
216
+ // The docs check is the one whose findings fail it.
217
+ if (response.type === 'integration.doc-conflicts') {
218
+ errors += response.data.findings.filter(
219
+ finding => finding.severity === 'error',
220
+ ).length;
221
+ }
222
+ const {warnings, skipped} = strictIssues(check, response, context);
223
+ return {
224
+ errors,
225
+ warnings: warnings.length,
226
+ skipped: skipped.map(each => each.message),
227
+ failed: errors > 0,
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Run every `doctor integration` check against the integration package in
233
+ * `cwd`, as its own build does, and return the `integration.check` envelope.
234
+ *
235
+ * @param {IntegrationCheckOptions} [options]
236
+ * @returns {Promise<IntegrationCheckCommandResponse>}
237
+ */
238
+ export async function integrationCheck(options = {}) {
239
+ const {cwd = process.cwd(), strict = false} = options;
240
+ // One after another: each loads the package through the same discovery.
241
+ /** @type {IntegrationCheckResponse[]} */
242
+ const responses = [];
243
+ responses.push(await validateIntegration(undefined, {cwd}));
244
+ responses.push(await integrationComponentConflicts(undefined, {cwd}));
245
+ responses.push(await integrationTemplateConflicts(undefined, {cwd}));
246
+ responses.push(await integrationDocConflicts(undefined, {cwd}));
247
+
248
+ const [validation] = responses;
249
+ return {
250
+ type: 'integration.check',
251
+ data: {
252
+ validated: validation.data.validated,
253
+ name: validation.data.name,
254
+ version: validation.data.version,
255
+ strict,
256
+ ...combineChecks(responses, strict),
257
+ },
258
+ };
259
+ }
260
+
261
+ /**
262
+ * Count the four checks' results into one summary. Each check's own row
263
+ * counts as its subcommand exits; the totals and `failures` count validate's
264
+ * issues once, though the other checks repeat them, and every other finding
265
+ * on its own, so two findings with one message stay two.
266
+ * @param {IntegrationCheckResponse[]} responses validate first
267
+ * @param {boolean} strict
268
+ */
269
+ export function combineChecks(responses, strict) {
270
+ const [validation] = responses;
271
+ // Only validate reports a missing package.json; the other checks read it
272
+ // from there instead of each naming a missing manifest.
273
+ const noPackage = validation.data.issues.some(
274
+ issue => issue.code === 'no_package',
275
+ );
276
+ /** @type {IntegrationCheckResult[]} */
277
+ const checks = responses.map(response => ({
278
+ check: /** @type {IntegrationCheckName} */ (CHECK_OF_TYPE[response.type]),
279
+ ...summarizeCheck(response, {noPackage}),
280
+ response,
281
+ }));
282
+ // Later checks repeat validate's issues and its missing manifest: each
283
+ // match is consumed once per check, as from a multiset. Every other issue
284
+ // counts, even a repeat within one check.
285
+ const keyOf = (/** @type {string} */ kind, /** @type {object} */ issue) =>
286
+ `${kind}\0${JSON.stringify(issue)}`;
287
+ const validateFound = strictIssues('validate', validation, {noPackage});
288
+ /** @type {Map<string, number>} */
289
+ const fromValidate = new Map();
290
+ for (const key of [
291
+ ...validation.data.issues
292
+ .filter(issue => issue.severity === 'error')
293
+ .map(issue => keyOf('error', asIssue(issue, issue.code))),
294
+ ...validateFound.warnings.map(issue => keyOf('warning', issue)),
295
+ ...validateFound.skipped.map(issue => keyOf('skipped', issue)),
296
+ ]) {
297
+ fromValidate.set(key, (fromValidate.get(key) ?? 0) + 1);
298
+ }
299
+
300
+ let errors = 0;
301
+ let warnings = 0;
302
+ let unchecked = 0;
303
+ /** @type {string[]} */
304
+ const skipped = [];
305
+ // `--strict` adds no check: it reports the same warnings and skipped work
306
+ // as errors, and they fail the run.
307
+ /** @type {IntegrationCheckFailure[]} */
308
+ const failures = [];
309
+ checks.forEach(({check, response}, index) => {
310
+ /** @type {Map<string, number>} */
311
+ const repeats = index === 0 ? new Map() : new Map(fromValidate);
312
+ const isRepeat = (/** @type {string} */ key) => {
313
+ const left = repeats.get(key) ?? 0;
314
+ if (left === 0) return false;
315
+ repeats.set(key, left - 1);
316
+ return true;
317
+ };
318
+ for (const issue of response.data.issues) {
319
+ if (
320
+ issue.severity === 'error' &&
321
+ !isRepeat(keyOf('error', asIssue(issue, issue.code)))
322
+ ) {
323
+ errors++;
324
+ }
325
+ }
326
+ if (response.type === 'integration.doc-conflicts') {
327
+ errors += response.data.findings.filter(
328
+ finding => finding.severity === 'error',
329
+ ).length;
330
+ }
331
+ for (const value of Object.values(response.data)) {
332
+ const list = /** @type {{unchecked?: unknown}} */ (value)?.unchecked;
333
+ if (isCheckReport(value) && Array.isArray(list)) unchecked += list.length;
334
+ }
335
+ const found =
336
+ index === 0 ? validateFound : strictIssues(check, response, {noPackage});
337
+ for (const issue of found.warnings) {
338
+ if (isRepeat(keyOf('warning', issue))) continue;
339
+ warnings++;
340
+ if (strict) failures.push({check, severity: 'error', ...issue});
341
+ }
342
+ for (const issue of found.skipped) {
343
+ if (isRepeat(keyOf('skipped', issue))) continue;
344
+ skipped.push(`${check}: ${issue.message}`);
345
+ if (strict) failures.push({check, severity: 'error', ...issue});
346
+ }
347
+ });
348
+ return {
349
+ checks,
350
+ errors,
351
+ warnings,
352
+ skipped,
353
+ unchecked,
354
+ failures,
355
+ failed: checks.some(check => check.failed) || failures.length > 0,
356
+ };
357
+ }