@astryxdesign/cli 0.6.4-canary.f0355e3 → 0.6.4
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.
- package/README.md +96 -99
- package/api/build/build.doc.mjs +1 -6
- package/api/build/build.test.mjs +0 -22
- package/api/build/kit/kit.mjs +5 -44
- package/api/component/_adapter.d.mts +0 -25
- package/api/component/_adapter.mjs +5 -59
- package/api/component/component.d.mts +3 -6
- package/api/component/component.doc.mjs +17 -37
- package/api/component/component.mjs +9 -249
- package/api/component/component.type.d.mts +0 -25
- package/api/component/component.type.mjs +0 -44
- package/api/discover/_adapter.d.mts +6 -114
- package/api/discover/_adapter.mjs +17 -372
- package/api/discover/detail/detail.d.mts +6 -18
- package/api/discover/detail/detail.mjs +13 -67
- package/api/discover/detail/detail.test.mjs +0 -85
- package/api/discover/discover.d.mts +9 -3
- package/api/discover/discover.doc.mjs +18 -61
- package/api/discover/discover.mjs +36 -220
- package/api/discover/discover.test.mjs +2 -11
- package/api/discover/discover.type.d.mts +8 -147
- package/api/discover/discover.type.mjs +12 -102
- package/api/discover/list/list.d.mts +6 -20
- package/api/discover/list/list.mjs +12 -45
- package/api/discover/list/list.test.mjs +0 -46
- package/api/discover/search/search.d.mts +16 -18
- package/api/discover/search/search.mjs +56 -102
- package/api/discover/search/search.test.mjs +10 -144
- package/api/docs/_adapter.d.mts +3 -8
- package/api/docs/_adapter.mjs +6 -14
- package/api/docs/docOverlays.test.mjs +1 -27
- package/api/docs/docs.doc.mjs +2 -2
- package/api/docs/docs.test.mjs +243 -0
- package/api/docs/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +314 -0
- package/api/doctor/doctor.d.mts +3 -8
- package/api/doctor/doctor.doc.mjs +8 -17
- package/api/doctor/doctor.mjs +9 -90
- package/api/doctor/doctor.test.mjs +10 -122
- package/api/doctor/doctor.type.d.mts +1 -1
- package/api/doctor/doctor.type.mjs +1 -1
- package/api/gap-report/gap-report.doc.mjs +10 -19
- package/api/hook/hook.doc.mjs +3 -6
- package/api/index.d.mts +2 -1
- package/api/index.mjs +5 -5
- package/api/init/init.doc.mjs +12 -17
- package/api/integration/add-helpers.d.mts +2 -5
- package/api/integration/add-helpers.mjs +9 -36
- package/api/integration/add-theme.mjs +1 -22
- package/api/integration/add-theme.test.mjs +0 -34
- package/api/integration/authoring-checks.mjs +2 -2
- package/api/integration/integrationPackCheck.doc.mjs +3 -3
- package/api/integration/pack-check.mjs +9 -82
- package/api/integration/pack-check.test.mjs +0 -90
- package/api/integration/pack-check.type.mjs +1 -1
- package/api/json/assertResponse.doc.mjs +1 -1
- package/api/json/index.ts +1 -0
- package/api/json/isError.doc.mjs +1 -1
- package/api/layout/_adapter.d.mts +34 -0
- package/api/layout/_adapter.mjs +148 -0
- package/api/layout/check/check.d.mts +16 -0
- package/api/layout/check/check.mjs +40 -0
- package/api/layout/expand/expand.d.mts +22 -0
- package/api/layout/expand/expand.mjs +155 -0
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.d.mts +13 -0
- package/api/layout/grammar/grammar.mjs +87 -0
- package/api/layout/layout.d.mts +6 -0
- package/api/layout/layout.mjs +17 -0
- package/api/layout/layout.test.mjs +297 -0
- package/api/layout/layout.type.d.mts +89 -0
- package/api/layout/layout.type.mjs +103 -0
- package/api/layout/layoutCheck.doc.d.mts +11 -0
- package/api/layout/layoutCheck.doc.mjs +85 -0
- package/api/layout/layoutExpand.doc.d.mts +11 -0
- package/api/layout/layoutExpand.doc.mjs +107 -0
- package/api/layout/layoutGrammar.doc.d.mts +11 -0
- package/api/layout/layoutGrammar.doc.mjs +57 -0
- package/api/search/search.d.mts +1 -27
- package/api/search/search.doc.mjs +2 -2
- package/api/search/search.mjs +16 -228
- package/api/search/search.test.mjs +512 -0
- package/api/swizzle/swizzle.doc.mjs +5 -7
- package/api/template/copy/copy.mjs +1 -1
- package/api/template/copy/copy.test.mjs +0 -9
- package/api/template/template-integration.test.mjs +65 -1
- package/api/template/template.doc.mjs +1 -2
- package/api/template/template.mjs +1 -1
- package/api/theme/add/add.mjs +25 -17
- package/api/theme/add/add.staging.test.mjs +23 -40
- package/api/theme/build/build.family.test.mjs +12 -7
- package/api/theme/build/build.mjs +18 -8
- package/api/theme/generateTonalPalette.doc.mjs +2 -1
- package/api/theme/listThemes.doc.mjs +1 -1
- package/api/theme/themeAdd.doc.mjs +10 -9
- package/api/theme/themeBuild.doc.mjs +13 -13
- package/api/theme/themeList.doc.mjs +1 -1
- package/api/theme/themeListAvailable.doc.mjs +1 -2
- package/api/theme/themePaletteGenerate.doc.mjs +8 -15
- package/api/theme/themeTargets.doc.mjs +2 -3
- package/api/theme/themeTemplate.doc.mjs +1 -2
- package/api/upgrade/run/run.mjs +4 -6
- package/api/upgrade/upgrade.doc.mjs +22 -24
- package/api/upgrade/upgrade.type.mjs +2 -2
- package/assets/codemods/__tests__/runner.test.mjs +1 -3
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +4 -5
- package/assets/docs/README.md +2 -4
- package/assets/docs/browser-support.doc.mjs +11 -11
- package/assets/docs/color.doc.mjs +2 -8
- package/assets/docs/elevation.doc.mjs +4 -6
- package/assets/docs/getting-started.doc.mjs +16 -5
- package/assets/docs/icons.doc.mjs +21 -2
- package/assets/docs/illustrations.doc.mjs +15 -7
- package/assets/docs/internationalization.doc.mjs +5 -7
- package/assets/docs/layout.doc.dense.mjs +82 -130
- package/assets/docs/layout.doc.mjs +77 -133
- package/assets/docs/migration.doc.mjs +21 -19
- package/assets/docs/motion.doc.mjs +3 -16
- package/assets/docs/principles.doc.dense.mjs +5 -5
- package/assets/docs/principles.doc.mjs +0 -8
- package/assets/docs/principles.doc.zh.mjs +6 -6
- package/assets/docs/shape.doc.mjs +3 -8
- package/assets/docs/spacing.doc.mjs +2 -7
- package/assets/docs/styling-libraries.doc.mjs +2 -6
- package/assets/docs/styling.doc.mjs +23 -19
- package/assets/docs/theme.doc.dense.mjs +18 -58
- package/assets/docs/theme.doc.mjs +46 -56
- package/assets/docs/theme.doc.zh.mjs +8 -9
- package/assets/docs/tokens.doc.dense.mjs +2 -2
- package/assets/docs/tokens.doc.mjs +8 -389
- package/assets/docs/tokens.doc.zh.mjs +2 -2
- package/assets/docs/tree/integrations.doc.mjs +451 -25
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/typography.doc.mjs +4 -24
- package/assets/docs/working-with-ai.doc.mjs +22 -30
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/authoring/config/config.doc.mjs +2 -10
- package/authoring/config/parse.d.mts +0 -2
- package/authoring/config/parse.mjs +0 -19
- package/authoring/config/parse.test.mjs +0 -8
- package/authoring/config/type.ts +2 -13
- package/authoring/doctypes/_schema.d.mts +2 -3
- package/authoring/doctypes/_schema.mjs +0 -6
- package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
- package/authoring/doctypes/base/type.ts +2 -4
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +1 -1
- package/authoring/doctypes/component/component.doc.mjs +0 -6
- package/authoring/doctypes/component/type.ts +0 -8
- package/authoring/doctypes/reference/reference.doc.mjs +0 -7
- package/authoring/doctypes/reference/type.ts +0 -5
- package/authoring/doctypes/schema/schema.doc.mjs +2 -2
- package/authoring/doctypes/template/template.doc.mjs +1 -1
- package/authoring/doctypes/template/type.ts +2 -2
- package/authoring/index.d.mts +0 -1
- package/authoring/index.d.ts +0 -10
- package/authoring/index.mjs +0 -1
- package/authoring/integration/integration.doc.mjs +10 -12
- package/clients/cli/command-result-coverage.test.mjs +7 -7
- package/clients/cli/commands/component/index.mjs +55 -152
- package/clients/cli/commands/component-ownership.test.mjs +0 -89
- package/clients/cli/commands/component.doc.mjs +9 -27
- package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
- package/clients/cli/commands/discover.doc.mjs +9 -53
- package/clients/cli/commands/discover.mjs +118 -393
- package/clients/cli/commands/docs.doc.mjs +1 -1
- package/clients/cli/commands/docs.mjs +17 -60
- package/clients/cli/commands/docs.test.mjs +294 -0
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +2 -3
- package/clients/cli/commands/doctor-integration.test.mjs +0 -53
- package/clients/cli/commands/doctor.doc.mjs +1 -3
- package/clients/cli/commands/doctor.mjs +5 -49
- package/clients/cli/commands/gap-report.doc.mjs +9 -10
- package/clients/cli/commands/init.doc.mjs +6 -9
- package/clients/cli/commands/integration-add.doc.mjs +9 -9
- package/clients/cli/commands/integration-authoring.test.mjs +10 -61
- package/clients/cli/commands/integration-pack.doc.mjs +9 -5
- package/clients/cli/commands/integration-real-world.test.mjs +1 -1
- package/clients/cli/commands/integration.doc.mjs +4 -4
- package/clients/cli/commands/integration.mjs +43 -74
- package/clients/cli/commands/layout-check.doc.mjs +65 -0
- package/clients/cli/commands/layout-expand.doc.mjs +83 -0
- package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
- package/clients/cli/commands/layout.doc.mjs +34 -0
- package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
- package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
- package/clients/cli/commands/layout.mjs +275 -0
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +3 -10
- package/clients/cli/commands/search.mjs +2 -21
- package/clients/cli/commands/search.test.mjs +4 -21
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.doc.mjs +1 -1
- package/clients/cli/commands/text-json-parity.test.mjs +16 -5
- package/clients/cli/commands/theme-add.doc.mjs +1 -1
- package/clients/cli/commands/theme-palette-generate.doc.mjs +2 -3
- package/clients/cli/commands/theme-palette.doc.mjs +2 -1
- package/clients/cli/commands/theme-targets.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +1 -2
- package/clients/cli/commands/upgrade.doc.mjs +3 -62
- package/clients/cli/index.mjs +10 -28
- package/clients/cli/lib/define-command.mjs +4 -28
- package/clients/cli/lib/define-command.test.mjs +0 -54
- package/clients/cli/lib/exit-codes.test.mjs +9 -18
- package/clients/cli/lib/json-shim.mjs +14 -24
- package/clients/cli/lib/json-shim.test.mjs +20 -6
- package/clients/cli/lib/manifest.mjs +13 -18
- package/clients/cli/lib/manifest.test.mjs +2 -5
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/agent-docs/agent-docs.test.mjs +1159 -0
- package/foundation/discovery/authoring-self-docs.mjs +0 -1
- package/foundation/discovery/authoring-self-docs.test.mjs +2 -6
- package/foundation/discovery/cli-self-docs.mjs +2 -16
- package/foundation/discovery/cli-self-docs.test.mjs +0 -20
- package/foundation/discovery/docs-discovery.mjs +1 -5
- package/foundation/discovery/docs-discovery.test.mjs +0 -21
- package/foundation/discovery/docs-section-key.d.mts +1 -1
- package/foundation/discovery/docs-section-key.mjs +1 -1
- package/foundation/discovery/template-adapter.mjs +1 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +14 -3
- package/foundation/doc-compiler/tree.d.mts +0 -4
- package/foundation/doc-compiler/tree.mjs +1 -6
- package/foundation/doc-compiler/tree.test.mjs +598 -0
- package/foundation/integrations/cli-requirement.d.mts +6 -26
- package/foundation/integrations/cli-requirement.mjs +11 -46
- package/foundation/integrations/cli-requirement.test.mjs +2 -7
- package/foundation/integrations/contribution-inventory.mjs +1 -1
- package/foundation/integrations/integrations.d.mts +1 -14
- package/foundation/integrations/integrations.mjs +1 -41
- package/foundation/integrations/integrations.test.mjs +0 -31
- package/foundation/response/error-codes.doc.mjs +8 -6
- package/foundation/response/error-codes.test.mjs +5 -30
- package/foundation/response/response-types.doc.d.mts +3 -4
- package/foundation/response/response-types.doc.mjs +27 -40
- package/foundation/response/response-types.doc.test.mjs +0 -23
- package/foundation/response/response.doc.mjs +10 -11
- package/foundation/xle/browser.d.mts +3 -3
- package/foundation/xle/browser.mjs +3 -3
- package/foundation/xle/expand.mjs +2 -2
- package/foundation/xle/parse.mjs +1 -1
- package/foundation/xle/print.mjs +2 -2
- package/foundation/xle/splice.mjs +1 -1
- package/package.json +9 -9
- package/api/discover/_adapter.test.mjs +0 -215
- package/api/discover/_catalog-view.d.mts +0 -115
- package/api/discover/_catalog-view.mjs +0 -203
- package/api/discover/_catalog-view.test.mjs +0 -128
- package/api/discover/detail/item/item.d.mts +0 -26
- package/api/discover/detail/item/item.mjs +0 -78
- package/api/discover/detail/item/item.test.mjs +0 -73
- package/api/integration/pack-check.lifecycle-output.test.mjs +0 -107
- package/api/theme/add/add.rollback.test.mjs +0 -158
- package/api/theme/build/build.rollback.test.mjs +0 -148
- package/api/upgrade/run/files-changed.test.mjs +0 -111
- package/assets/codemods/file-count.test.mjs +0 -163
- package/assets/docs/tree/add-a-component.doc.mjs +0 -75
- package/assets/docs/tree/add-a-theme.doc.mjs +0 -85
- package/assets/docs/tree/add-a-topic.doc.mjs +0 -144
- package/assets/docs/tree/agent-guidance.doc.mjs +0 -138
- package/assets/docs/tree/block-template.doc.mjs +0 -130
- package/assets/docs/tree/build-the-template.doc.mjs +0 -28
- package/assets/docs/tree/building-blocks.doc.mjs +0 -46
- package/assets/docs/tree/check-your-docs.doc.mjs +0 -137
- package/assets/docs/tree/checks.doc.mjs +0 -119
- package/assets/docs/tree/codemods.doc.mjs +0 -147
- package/assets/docs/tree/component-family.doc.mjs +0 -113
- package/assets/docs/tree/component-imports.doc.mjs +0 -69
- package/assets/docs/tree/component-lookups.doc.mjs +0 -149
- package/assets/docs/tree/components.doc.mjs +0 -23
- package/assets/docs/tree/configuration.doc.mjs +0 -23
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +0 -182
- package/assets/docs/tree/define-the-theme.doc.mjs +0 -118
- package/assets/docs/tree/describe-the-component.doc.mjs +0 -57
- package/assets/docs/tree/docs.doc.mjs +0 -21
- package/assets/docs/tree/document-the-template.doc.mjs +0 -28
- package/assets/docs/tree/document-the-theme.doc.mjs +0 -68
- package/assets/docs/tree/export-template-assets.doc.mjs +0 -147
- package/assets/docs/tree/extend-or-replace.doc.mjs +0 -103
- package/assets/docs/tree/fonts-and-assets.doc.mjs +0 -106
- package/assets/docs/tree/generate-a-palette.doc.mjs +0 -66
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +0 -105
- package/assets/docs/tree/help.doc.mjs +0 -16
- package/assets/docs/tree/links.doc.mjs +0 -98
- package/assets/docs/tree/package-and-test.doc.mjs +0 -32
- package/assets/docs/tree/page-template.doc.mjs +0 -71
- package/assets/docs/tree/publishing.doc.mjs +0 -111
- package/assets/docs/tree/quick-start.doc.mjs +0 -272
- package/assets/docs/tree/replace-a-core-component.doc.mjs +0 -104
- package/assets/docs/tree/replace-a-core-template.doc.mjs +0 -172
- package/assets/docs/tree/sections-and-placement.doc.mjs +0 -108
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +0 -59
- package/assets/docs/tree/ship.doc.mjs +0 -16
- package/assets/docs/tree/short-and-findable.doc.mjs +0 -108
- package/assets/docs/tree/single-component.doc.mjs +0 -165
- package/assets/docs/tree/start-a-template.doc.mjs +0 -143
- package/assets/docs/tree/subcomponent.doc.mjs +0 -115
- package/assets/docs/tree/template-assets.doc.mjs +0 -64
- package/assets/docs/tree/template-doc-overview.doc.mjs +0 -109
- package/assets/docs/tree/template-fonts.doc.mjs +0 -102
- package/assets/docs/tree/template-grading-rubric.doc.mjs +0 -452
- package/assets/docs/tree/template-icons.doc.mjs +0 -97
- package/assets/docs/tree/template-images-media.doc.mjs +0 -127
- package/assets/docs/tree/template-styles.doc.mjs +0 -93
- package/assets/docs/tree/templates.doc.mjs +0 -34
- package/assets/docs/tree/test-in-an-app.doc.mjs +0 -115
- package/assets/docs/tree/test-template-in-app.doc.mjs +0 -128
- package/assets/docs/tree/themes.doc.mjs +0 -39
- package/assets/docs/tree/troubleshooting.doc.mjs +0 -149
- package/assets/docs/tree/upgrading.doc.mjs +0 -103
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +0 -51
- package/assets/docs/tree/verify-packed-template.doc.mjs +0 -77
- package/assets/docs/tree/versioning.doc.mjs +0 -161
- package/assets/docs/tree/write-good-templates.doc.mjs +0 -64
- package/assets/docs/tree/write-the-template-file.doc.mjs +0 -154
- package/authoring/discover/discover.doc.d.mts +0 -13
- package/authoring/discover/discover.doc.mjs +0 -138
- package/authoring/discover/parse.d.mts +0 -24
- package/authoring/discover/parse.mjs +0 -128
- package/authoring/discover/parse.test.mjs +0 -124
- package/authoring/discover/type.ts +0 -87
- package/clients/cli/commands/component-batch.test.mjs +0 -341
- package/clients/cli/commands/discover.sources.test.mjs +0 -267
- package/clients/cli/commands/integration-verify.doc.mjs +0 -22
- package/clients/cli/lib/parse-error-format.test.mjs +0 -81
- package/foundation/response/batch.type.d.mts +0 -33
- package/foundation/response/batch.type.mjs +0 -34
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
* open one section by its key, and `--full` prints the whole topic. `--json`
|
|
8
8
|
* keeps the docs() contract: a topic returns its whole doc, and `--index` its
|
|
9
9
|
* sections.
|
|
10
|
-
* Supports --detail (full|compact|brief) and --lang (en|zh|dense).
|
|
11
|
-
* block's label prints above its fence, and table cells escape their pipes.
|
|
10
|
+
* Supports --detail (full|compact|brief) and --lang (en|zh|dense).
|
|
12
11
|
*
|
|
13
12
|
* Usage:
|
|
14
13
|
* astryx docs List available topics
|
|
@@ -43,35 +42,21 @@ import {doc as docsFn} from '../../../api/docs/docs.doc.mjs';
|
|
|
43
42
|
|
|
44
43
|
// ─── Formatting ──────────────────────────────────────────────────────────────
|
|
45
44
|
|
|
46
|
-
/**
|
|
47
|
-
* A table cell with its pipes escaped. Columns are separated by ` | `, and a
|
|
48
|
-
* union type such as `'light' | 'dark'` is spelled with the same character,
|
|
49
|
-
* so an unescaped cell reads as extra columns. `astryx component` escapes its
|
|
50
|
-
* prop tables the same way.
|
|
51
|
-
* @param {string | undefined} cell
|
|
52
|
-
* @returns {string}
|
|
53
|
-
*/
|
|
54
|
-
function tableCell(cell) {
|
|
55
|
-
return (cell || '').replaceAll('|', '\\|');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
45
|
/**
|
|
59
46
|
* @param {string[]} headers
|
|
60
47
|
* @param {string[][]} rows
|
|
61
48
|
* @returns {string}
|
|
62
49
|
*/
|
|
63
50
|
function formatTable(headers, rows) {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
const widths = head.map((h, i) =>
|
|
67
|
-
Math.max(h.length, ...cells.map(r => (r[i] || '').length)),
|
|
51
|
+
const widths = headers.map((h, i) =>
|
|
52
|
+
Math.max(h.length, ...rows.map(r => (r[i] || '').length)),
|
|
68
53
|
);
|
|
69
54
|
const sep = widths.map(w => '-'.repeat(w)).join(' | ');
|
|
70
|
-
const
|
|
71
|
-
const body =
|
|
72
|
-
.map(r => r.map((c, i) => c.padEnd(widths[i])).join(' | '))
|
|
55
|
+
const head = headers.map((h, i) => h.padEnd(widths[i])).join(' | ');
|
|
56
|
+
const body = rows
|
|
57
|
+
.map(r => r.map((c, i) => (c || '').padEnd(widths[i])).join(' | '))
|
|
73
58
|
.join('\n');
|
|
74
|
-
return `${
|
|
59
|
+
return `${head}\n${sep}\n${body}`;
|
|
75
60
|
}
|
|
76
61
|
|
|
77
62
|
/**
|
|
@@ -80,19 +65,15 @@ function formatTable(headers, rows) {
|
|
|
80
65
|
* @returns {string}
|
|
81
66
|
*/
|
|
82
67
|
function formatTableCompact(headers, rows) {
|
|
83
|
-
|
|
84
|
-
return rows
|
|
85
|
-
.map(r => r.filter(cell => String(cell ?? '').trim() !== '').join(' = '))
|
|
86
|
-
.join('\n');
|
|
68
|
+
return rows.map(r => r.join(' = ')).join('\n');
|
|
87
69
|
}
|
|
88
70
|
|
|
89
71
|
/**
|
|
90
|
-
* One content block as text. Exported for its tests.
|
|
91
72
|
* @param {import('@astryxdesign/cli/authoring').ReferenceContentBlock} block
|
|
92
73
|
* @param {'full' | 'compact' | 'brief'} detail
|
|
93
74
|
* @returns {string | null}
|
|
94
75
|
*/
|
|
95
|
-
|
|
76
|
+
function formatBlock(block, detail) {
|
|
96
77
|
switch (block.type) {
|
|
97
78
|
case 'prose':
|
|
98
79
|
return block.text;
|
|
@@ -103,20 +84,13 @@ export function formatBlock(block, detail) {
|
|
|
103
84
|
case 'code':
|
|
104
85
|
if (detail === 'compact' || detail === 'brief') return null;
|
|
105
86
|
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// HTML, and a reader who copies the block would copy it too.
|
|
109
|
-
const label = block.label
|
|
110
|
-
? `${block.label.replace(/:\s*$/, '')}:\n`
|
|
111
|
-
: '';
|
|
112
|
-
return `${label}\`\`\`${block.lang}\n${block.code}\n\`\`\``;
|
|
87
|
+
const label = block.label ? `// ${block.label}\n` : '';
|
|
88
|
+
return `\`\`\`${block.lang}\n${label}${block.code}\n\`\`\``;
|
|
113
89
|
}
|
|
114
90
|
|
|
115
91
|
case 'table':
|
|
116
92
|
if (detail === 'brief') {
|
|
117
|
-
return block.rows
|
|
118
|
-
.map(r => r.slice(0, 2).map(tableCell).join('='))
|
|
119
|
-
.join(' | ');
|
|
93
|
+
return block.rows.map(r => r.slice(0, 2).join('=')).join(' | ');
|
|
120
94
|
}
|
|
121
95
|
if (detail === 'compact') {
|
|
122
96
|
return formatTableCompact(block.headers, block.rows);
|
|
@@ -228,23 +202,16 @@ function emitIndex(index, run) {
|
|
|
228
202
|
}
|
|
229
203
|
|
|
230
204
|
/**
|
|
231
|
-
* One child row of a namespace
|
|
232
|
-
*
|
|
233
|
-
* template`). Typed docs keep a distinct title when it carries the real symbol
|
|
234
|
-
* name (`assert-response assertResponse()`).
|
|
205
|
+
* One child row of a namespace: its route name, then the doc's own title when
|
|
206
|
+
* it is not the route name (`assertResponse()`, `search()`), then its summary.
|
|
235
207
|
* @param {import('../../../api/docs/docs.type.mjs').DocsNodeChild} child
|
|
236
208
|
* @returns {{name: string, summary: string}}
|
|
237
209
|
*/
|
|
238
210
|
function childRow(child) {
|
|
239
|
-
const titleAddsIdentity =
|
|
240
|
-
child.kind !== 'namespace' &&
|
|
241
|
-
child.kind !== 'generic' &&
|
|
242
|
-
child.title !== child.name;
|
|
243
211
|
return {
|
|
244
212
|
name: child.name,
|
|
245
|
-
summary:
|
|
246
|
-
? `${child.title}: ${child.summary}
|
|
247
|
-
: child.summary,
|
|
213
|
+
summary:
|
|
214
|
+
child.title === child.name ? child.summary : `${child.title}: ${child.summary}`,
|
|
248
215
|
};
|
|
249
216
|
}
|
|
250
217
|
|
|
@@ -260,17 +227,6 @@ function emitNode(node, detail, run) {
|
|
|
260
227
|
if (node.kind === 'namespace') {
|
|
261
228
|
emit(
|
|
262
229
|
section(node.title, wrapText(node.summary)),
|
|
263
|
-
// A namespace may author intro `blocks`; they render above its children.
|
|
264
|
-
...(node.content?.length
|
|
265
|
-
? [
|
|
266
|
-
text(
|
|
267
|
-
node.content
|
|
268
|
-
.map(b => formatBlock(b, detail))
|
|
269
|
-
.filter(Boolean)
|
|
270
|
-
.join('\n\n'),
|
|
271
|
-
),
|
|
272
|
-
]
|
|
273
|
-
: []),
|
|
274
230
|
...node.slots.flatMap(slot => [
|
|
275
231
|
// A namespace with one slot titled like itself needs no second heading.
|
|
276
232
|
...(node.slots.length === 1 && slot.title === node.title
|
|
@@ -279,6 +235,7 @@ function emitNode(node, detail, run) {
|
|
|
279
235
|
records(slot.children.map(childRow), {
|
|
280
236
|
fields: ['name', 'summary'],
|
|
281
237
|
layout: 'inline',
|
|
238
|
+
overflow: 'truncate',
|
|
282
239
|
}),
|
|
283
240
|
]),
|
|
284
241
|
text(
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import * as os from 'node:os';
|
|
7
|
+
import {Command} from 'commander';
|
|
8
|
+
import {registerDocs} from './docs.mjs';
|
|
9
|
+
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
10
|
+
import {displayWidth} from '../formatters/index.mjs';
|
|
11
|
+
|
|
12
|
+
let tmpDir;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-docs-test-'));
|
|
16
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
17
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
22
|
+
vi.restoreAllMocks();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
function createProgram() {
|
|
26
|
+
const program = new Command();
|
|
27
|
+
program.exitOverride(); // Throw instead of calling process.exit
|
|
28
|
+
registerDocs(program);
|
|
29
|
+
return program;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('registerDocs', () => {
|
|
33
|
+
it('lists available topics when no topic given', async () => {
|
|
34
|
+
const program = createProgram();
|
|
35
|
+
await program.parseAsync(['node', 'astryx', 'docs']);
|
|
36
|
+
|
|
37
|
+
const output = console.log.mock.calls.map(c => c[0]).join('\n');
|
|
38
|
+
expect(output).toContain('principles');
|
|
39
|
+
expect(output).toContain('tokens');
|
|
40
|
+
expect(output).not.toContain('shadcn-compatibility');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('errors for unknown topic', async () => {
|
|
44
|
+
const program = createProgram();
|
|
45
|
+
vi.spyOn(process, 'exit').mockImplementation(code => {
|
|
46
|
+
throw new Error(`exit ${code}`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await expect(
|
|
50
|
+
program.parseAsync(['node', 'astryx', 'docs', 'nonexistent']),
|
|
51
|
+
).rejects.toThrow('exit 1');
|
|
52
|
+
|
|
53
|
+
const errorOutput = console.error.mock.calls.map(c => c[0]).join('\n');
|
|
54
|
+
expect(errorOutput).toContain('Unknown topic');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('hyphenated doc filenames', () => {
|
|
59
|
+
it('lists hyphenated topics like getting-started', async () => {
|
|
60
|
+
const program = createProgram();
|
|
61
|
+
await program.parseAsync(['node', 'astryx', 'docs']);
|
|
62
|
+
|
|
63
|
+
const output = console.log.mock.calls.map(c => c[0]).join('\n');
|
|
64
|
+
expect(output).toContain('getting-started');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('loads a hyphenated topic by name', async () => {
|
|
68
|
+
const program = createProgram();
|
|
69
|
+
await program.parseAsync(['node', 'astryx', 'docs', 'getting-started']);
|
|
70
|
+
|
|
71
|
+
const output = console.log.mock.calls.map(c => c[0]).join('\n');
|
|
72
|
+
expect(output.length).toBeGreaterThan(0);
|
|
73
|
+
expect(console.error).not.toHaveBeenCalled();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('returns docs.detail via API for hyphenated topic', async () => {
|
|
77
|
+
const {docs: docsApi} = await import('../../../api/docs/docs.mjs');
|
|
78
|
+
const result = await docsApi('getting-started', undefined, {full: true});
|
|
79
|
+
expect(result.type).toBe('docs.detail');
|
|
80
|
+
expect(result.data).toBeDefined();
|
|
81
|
+
expect(result.data.description).toBeDefined();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('migration docs', () => {
|
|
86
|
+
it('lists the migration topic', async () => {
|
|
87
|
+
const program = createProgram();
|
|
88
|
+
await program.parseAsync(['node', 'astryx', 'docs']);
|
|
89
|
+
|
|
90
|
+
const output = console.log.mock.calls.map(c => c[0]).join('\n');
|
|
91
|
+
expect(output).toContain('migration');
|
|
92
|
+
expect(output).toContain('Tailwind');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('loads migration docs by topic name', async () => {
|
|
96
|
+
const program = createProgram();
|
|
97
|
+
await program.parseAsync(['node', 'astryx', 'docs', 'migration']);
|
|
98
|
+
|
|
99
|
+
const output = console.log.mock.calls.map(c => c[0]).join('\n');
|
|
100
|
+
expect(output).toContain('Migration Guide');
|
|
101
|
+
expect(output).toContain('Recommended Order');
|
|
102
|
+
expect(output).toContain('Map shadcn and Radix Primitives');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('progressive reads', () => {
|
|
107
|
+
const SLOW = 60_000;
|
|
108
|
+
/** @param {string} out */
|
|
109
|
+
const widest = out => Math.max(...out.split('\n').map(line => line.length));
|
|
110
|
+
|
|
111
|
+
it('lists every topic on one line each', async () => {
|
|
112
|
+
const {status, stdout} = await runCli(['docs']);
|
|
113
|
+
expect(status).toBe(0);
|
|
114
|
+
expect(stdout).toMatch(/^principles +\S/m);
|
|
115
|
+
expect(widest(stdout)).toBeLessThanOrEqual(120);
|
|
116
|
+
expect(stdout).toContain('Usage: pnpm exec astryx docs <topic>');
|
|
117
|
+
}, SLOW);
|
|
118
|
+
|
|
119
|
+
it("prints a topic's section index with the keys to read by", async () => {
|
|
120
|
+
const {status, stdout} = await runCli(['docs', 'theme', '--index']);
|
|
121
|
+
expect(status).toBe(0);
|
|
122
|
+
expect(stdout).toMatch(/^quick-start +Quick Start/m);
|
|
123
|
+
expect(stdout).toContain('docs theme <section>');
|
|
124
|
+
expect(stdout).toMatch(/Read everything: +\S.* docs theme --full$/m);
|
|
125
|
+
expect(widest(stdout)).toBeLessThanOrEqual(120);
|
|
126
|
+
}, SLOW);
|
|
127
|
+
|
|
128
|
+
it('prints one section by its key', async () => {
|
|
129
|
+
const {status, stdout} = await runCli(['docs', 'theme', 'quick-start']);
|
|
130
|
+
expect(status).toBe(0);
|
|
131
|
+
expect(stdout).toMatch(/^## Quick Start/m);
|
|
132
|
+
}, SLOW);
|
|
133
|
+
|
|
134
|
+
it("lists a topic's sections by default; --full prints the whole topic", async () => {
|
|
135
|
+
const index = await runCli(['docs', 'theme', '--index']);
|
|
136
|
+
expect((await runCli(['docs', 'theme'])).stdout).toBe(index.stdout);
|
|
137
|
+
const full = await runCli(['docs', 'theme', '--full']);
|
|
138
|
+
expect(full.status).toBe(0);
|
|
139
|
+
expect(full.stdout).toMatch(/^## Quick Start/m);
|
|
140
|
+
expect(full.stdout.length).toBeGreaterThan(index.stdout.length * 3);
|
|
141
|
+
expect((await runCli(['--detail', 'full', 'docs', 'theme', '--full'])).stdout).toBe(
|
|
142
|
+
full.stdout,
|
|
143
|
+
);
|
|
144
|
+
}, SLOW);
|
|
145
|
+
|
|
146
|
+
it('returns the matching envelopes as JSON', async () => {
|
|
147
|
+
const envelope = async args => JSON.parse((await runCli([...args, '--json'])).stdout);
|
|
148
|
+
// JSON keeps docs(): a bare read is the whole topic.
|
|
149
|
+
expect((await envelope(['docs', 'theme'])).type).toBe('docs.detail');
|
|
150
|
+
expect((await envelope(['docs', 'theme', '--full'])).type).toBe('docs.detail');
|
|
151
|
+
expect((await envelope(['docs', 'theme', '--index'])).type).toBe(
|
|
152
|
+
'docs.index',
|
|
153
|
+
);
|
|
154
|
+
expect((await envelope(['docs', 'theme', 'quick-start'])).type).toBe(
|
|
155
|
+
'docs.detail.section',
|
|
156
|
+
);
|
|
157
|
+
}, SLOW);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('the docs tree, one level at a time', () => {
|
|
161
|
+
const SLOW = 60_000;
|
|
162
|
+
/** @param {string} out */
|
|
163
|
+
const widest = out => Math.max(...out.split('\n').map(line => line.length));
|
|
164
|
+
|
|
165
|
+
it('lists the docs tree first, under its own heading, then the topics', async () => {
|
|
166
|
+
const {status, stdout} = await runCli(['docs']);
|
|
167
|
+
expect(status).toBe(0);
|
|
168
|
+
expect(stdout).toMatch(/^Docs tree$/m);
|
|
169
|
+
expect(stdout).toMatch(/^Topics$/m);
|
|
170
|
+
expect(stdout).toMatch(/^cli +Commands, programmatic APIs/m);
|
|
171
|
+
expect(stdout.indexOf('\ncli ')).toBeLessThan(stdout.indexOf('\nprinciples '));
|
|
172
|
+
expect(stdout).not.toMatch(/^cli-integrations /m);
|
|
173
|
+
}, SLOW);
|
|
174
|
+
|
|
175
|
+
it("shows a child's own name when its route name does not spell it", async () => {
|
|
176
|
+
const {status, stdout} = await runCli(['docs', 'cli/api/functions']);
|
|
177
|
+
expect(status).toBe(0);
|
|
178
|
+
expect(stdout).toMatch(/^assert-response +assertResponse\(\): \S/m);
|
|
179
|
+
expect(stdout).toMatch(/^search +search\(\): Unified ranked search/m);
|
|
180
|
+
}, SLOW);
|
|
181
|
+
|
|
182
|
+
it('prints a namespace: each slot, its children, and how to go down and up', async () => {
|
|
183
|
+
const {status, stdout} = await runCli(['docs', 'cli/api']);
|
|
184
|
+
expect(status).toBe(0);
|
|
185
|
+
expect(stdout).toMatch(/^API$/m);
|
|
186
|
+
expect(stdout).toMatch(/^Reference$/m);
|
|
187
|
+
expect(stdout).toMatch(/^functions +Functions: Every function/m);
|
|
188
|
+
expect(stdout).toMatch(/^schemas +/m);
|
|
189
|
+
expect(stdout).toMatch(/^enums +/m);
|
|
190
|
+
// One level only: no function is listed on the api page.
|
|
191
|
+
expect(stdout).not.toMatch(/^search +/m);
|
|
192
|
+
expect(stdout).toMatch(/Open one: .*docs cli\/api\/<name>$/m);
|
|
193
|
+
expect(stdout).toMatch(/Up: .*docs cli$/m);
|
|
194
|
+
expect(widest(stdout)).toBeLessThanOrEqual(120);
|
|
195
|
+
}, SLOW);
|
|
196
|
+
|
|
197
|
+
it('prints a typed doc and the way back up', async () => {
|
|
198
|
+
const {status, stdout} = await runCli(['docs', 'cli/api/functions/search']);
|
|
199
|
+
expect(status).toBe(0);
|
|
200
|
+
expect(stdout).toMatch(/^## search\(\)/m);
|
|
201
|
+
expect(stdout).toContain('Read it with `astryx docs cli/commands/search`.');
|
|
202
|
+
expect(stdout).toMatch(/Up: .*docs cli\/api\/functions$/m);
|
|
203
|
+
}, SLOW);
|
|
204
|
+
|
|
205
|
+
it('reads the dense variant whole, asked with --dense or with --lang dense', async () => {
|
|
206
|
+
for (const args of [['--dense', 'docs', 'theme'], ['--lang', 'dense', 'docs', 'theme']]) {
|
|
207
|
+
const {status, stdout} = await runCli(args);
|
|
208
|
+
expect(status).toBe(0);
|
|
209
|
+
expect(stdout).not.toContain('Read one section:');
|
|
210
|
+
}
|
|
211
|
+
}, SLOW);
|
|
212
|
+
|
|
213
|
+
it('refuses --index with --full', async () => {
|
|
214
|
+
const both = await runCli(['docs', 'theme', '--index', '--full', '--json']);
|
|
215
|
+
expect(both.status).toBe(1);
|
|
216
|
+
expect(JSON.parse(both.stdout).code).toBe('ERR_INVALID_ARGUMENT');
|
|
217
|
+
}, SLOW);
|
|
218
|
+
|
|
219
|
+
it('reads the integration guide by its route, and not by its old name', async () => {
|
|
220
|
+
const guide = await runCli(['docs', 'cli/integrations', '--index']);
|
|
221
|
+
expect(guide.status).toBe(0);
|
|
222
|
+
expect(guide.stdout).toMatch(/Read one section: .*docs cli\/integrations <section>/);
|
|
223
|
+
// A bare read is one level too: the sections, and how to read it all.
|
|
224
|
+
const bare = await runCli(['docs', 'cli/integrations']);
|
|
225
|
+
expect(bare.status).toBe(0);
|
|
226
|
+
expect(bare.stdout).toBe(guide.stdout);
|
|
227
|
+
expect(bare.stdout).toMatch(/Read everything: +.*docs cli\/integrations --full$/m);
|
|
228
|
+
const one = await runCli(['docs', 'cli/integrations', 'codemods']);
|
|
229
|
+
expect(one.status).toBe(0);
|
|
230
|
+
expect(one.stdout).toMatch(/^## Codemods$/m);
|
|
231
|
+
expect(one.stdout).toMatch(/^Up: .*docs cli\/integrations --index$/m);
|
|
232
|
+
expect(one.stdout).toMatch(/^Previous: .*docs cli\/integrations agent-docs$/m);
|
|
233
|
+
expect(one.stdout).toMatch(/^Next: .*docs cli\/integrations recording-runs$/m);
|
|
234
|
+
const full = await runCli(['docs', 'cli/integrations', '--full']);
|
|
235
|
+
expect(full.status).toBe(0);
|
|
236
|
+
expect(full.stdout).toMatch(/^## Overview$/m);
|
|
237
|
+
expect(full.stdout.length).toBeGreaterThan(bare.stdout.length * 4);
|
|
238
|
+
const old = await runCli(['docs', 'cli-integrations']);
|
|
239
|
+
expect(old.status).toBe(1);
|
|
240
|
+
expect(old.stderr).toContain('Unknown topic "cli-integrations"');
|
|
241
|
+
}, SLOW);
|
|
242
|
+
|
|
243
|
+
it('returns docs.node as JSON, and fails a section of a namespace', async () => {
|
|
244
|
+
const node = JSON.parse((await runCli(['docs', 'cli', '--json'])).stdout);
|
|
245
|
+
expect(node).toMatchObject({
|
|
246
|
+
type: 'docs.node',
|
|
247
|
+
data: {route: 'cli', kind: 'namespace', breadcrumb: []},
|
|
248
|
+
});
|
|
249
|
+
expect(node.data.slots.map(slot => slot.name)).toEqual(['guides', 'reference']);
|
|
250
|
+
const section = await runCli(['docs', 'cli', 'commands', '--json']);
|
|
251
|
+
expect(section.status).toBe(1);
|
|
252
|
+
const error = JSON.parse(section.stdout);
|
|
253
|
+
expect(error).toMatchObject({code: 'ERR_UNKNOWN_SECTION'});
|
|
254
|
+
expect(error.suggestions.map(s => s.name)).toEqual([
|
|
255
|
+
'cli/integrations',
|
|
256
|
+
'cli/writing-docs',
|
|
257
|
+
'cli/commands',
|
|
258
|
+
'cli/api',
|
|
259
|
+
]);
|
|
260
|
+
}, SLOW);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
describe('text width in every language', () => {
|
|
264
|
+
const SLOW = 60_000;
|
|
265
|
+
/** Widest line outside code blocks, in terminal columns. */
|
|
266
|
+
const widest = out => {
|
|
267
|
+
let inCode = false;
|
|
268
|
+
let max = 0;
|
|
269
|
+
for (const line of out.split('\n')) {
|
|
270
|
+
if (/^\s*```/.test(line)) {
|
|
271
|
+
inCode = !inCode;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
// A single unbreakable token (a long URL) cannot wrap without breaking it.
|
|
275
|
+
const oneToken = !/\s/.test(line.trim());
|
|
276
|
+
if (!inCode && !line.startsWith('#') && !oneToken) {
|
|
277
|
+
max = Math.max(max, displayWidth(line));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return max;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
it('keeps the additive section index within 120 columns', async () => {
|
|
284
|
+
const {status, stdout} = await runCli([
|
|
285
|
+
'docs',
|
|
286
|
+
'theme',
|
|
287
|
+
'--index',
|
|
288
|
+
'--lang',
|
|
289
|
+
'zh',
|
|
290
|
+
]);
|
|
291
|
+
expect(status).toBe(0);
|
|
292
|
+
expect(widest(stdout)).toBeLessThanOrEqual(120);
|
|
293
|
+
}, SLOW);
|
|
294
|
+
});
|
|
@@ -10,10 +10,9 @@ export const doc = {
|
|
|
10
10
|
namespace: 'cli/commands',
|
|
11
11
|
summary: 'Check an integration\'s docs: the docs tree they add, every link, and overlaps with Core topics',
|
|
12
12
|
description:
|
|
13
|
-
'
|
|
14
|
-
'Intentional replacements and extensions are information. ' +
|
|
13
|
+
'Reports intentional Core topic replacements and extensions as information. ' +
|
|
15
14
|
'A same-name topic without `replaces` or `extends` is an accidental conflict ' +
|
|
16
|
-
'and fails until the author declares the relationship or renames it.
|
|
15
|
+
'and fails until the author declares the relationship or renames it.',
|
|
17
16
|
fn: 'integrationDocConflicts',
|
|
18
17
|
args: [
|
|
19
18
|
{
|
|
@@ -13,7 +13,6 @@ vi.mock('../../../foundation/discovery/template-conflict-release.mjs', () => ({
|
|
|
13
13
|
expandedTemplateConflictSchemaActive: () => true,
|
|
14
14
|
}));
|
|
15
15
|
import * as fs from 'node:fs';
|
|
16
|
-
import * as os from 'node:os';
|
|
17
16
|
import * as path from 'node:path';
|
|
18
17
|
import {Command} from 'commander';
|
|
19
18
|
import {discoverCoreTemplates} from '../../../foundation/discovery/template-adapter.mjs';
|
|
@@ -345,35 +344,6 @@ describe('doctor integration — command', () => {
|
|
|
345
344
|
expect(process.exitCode).toBe(1);
|
|
346
345
|
});
|
|
347
346
|
|
|
348
|
-
it('components exits 1 when Core is missing, with no [ok] after the failure', async () => {
|
|
349
|
-
// Outside the repo, so nothing above the package resolves Core.
|
|
350
|
-
const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-no-core-'));
|
|
351
|
-
const previousTmp = tmpDir;
|
|
352
|
-
tmpDir = outside;
|
|
353
|
-
try {
|
|
354
|
-
writeComponentIntegration('AcmeCarousel');
|
|
355
|
-
expect(findCoreDir(outside)).toBeNull();
|
|
356
|
-
process.chdir(outside);
|
|
357
|
-
|
|
358
|
-
await createProgram().parseAsync([
|
|
359
|
-
'node',
|
|
360
|
-
'astryx',
|
|
361
|
-
'doctor',
|
|
362
|
-
'integration',
|
|
363
|
-
'components',
|
|
364
|
-
]);
|
|
365
|
-
|
|
366
|
-
const printed = logCalls.join('\n');
|
|
367
|
-
expect(printed).toContain('core_not_found');
|
|
368
|
-
expect(printed).not.toContain('[ok]');
|
|
369
|
-
expect(process.exitCode).toBe(1);
|
|
370
|
-
} finally {
|
|
371
|
-
tmpDir = previousTmp;
|
|
372
|
-
process.chdir(previousCwd);
|
|
373
|
-
fs.rmSync(outside, {recursive: true, force: true});
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
|
|
377
347
|
it('components warns with the exact package-qualified command', async () => {
|
|
378
348
|
const coreDir = findCoreDir(tmpDir);
|
|
379
349
|
expect(coreDir).not.toBeNull();
|
|
@@ -463,29 +433,6 @@ describe('doctor integration — command', () => {
|
|
|
463
433
|
expect(process.exitCode).toBeUndefined();
|
|
464
434
|
});
|
|
465
435
|
|
|
466
|
-
it('docs exits 1 for a placement that hides a guide, with no [ok] after the failure', async () => {
|
|
467
|
-
writeDocIntegration({name: 'deploying'});
|
|
468
|
-
fs.writeFileSync(
|
|
469
|
-
path.join(tmpDir, 'docs', 'deploying.doc.mjs'),
|
|
470
|
-
"export default {type: 'generic', name: 'deploying', title: 'Deploying', description: 'Deploy.', placement: {parent: 'namespace:nope', slot: 'guides'}, sections: [{title: 'Deploy', content: [{type: 'prose', text: 'Deploy.'}]}]};\n",
|
|
471
|
-
);
|
|
472
|
-
process.chdir(tmpDir);
|
|
473
|
-
|
|
474
|
-
await createProgram().parseAsync([
|
|
475
|
-
'node',
|
|
476
|
-
'astryx',
|
|
477
|
-
'doctor',
|
|
478
|
-
'integration',
|
|
479
|
-
'docs',
|
|
480
|
-
]);
|
|
481
|
-
|
|
482
|
-
const printed = logCalls.join('\n');
|
|
483
|
-
expect(printed).toContain('[fail]');
|
|
484
|
-
expect(printed).toContain('invalid_doc_graph');
|
|
485
|
-
expect(printed).not.toContain('[ok]');
|
|
486
|
-
expect(process.exitCode).toBe(1);
|
|
487
|
-
});
|
|
488
|
-
|
|
489
436
|
it('docs exits 1 for an accidental same-name Core topic', async () => {
|
|
490
437
|
const [coreTopic] = Object.keys(discoverBuiltinTopics());
|
|
491
438
|
writeDocIntegration({name: coreTopic});
|
|
@@ -16,9 +16,7 @@ export const doc = {
|
|
|
16
16
|
summary: 'Diagnose Astryx projects and integration packages',
|
|
17
17
|
description:
|
|
18
18
|
'Runs read-only project health diagnostics by default: Node version, @astryxdesign/core ' +
|
|
19
|
-
'install and version alignment, themes, config,
|
|
20
|
-
'provider identity, contribution issues), agent docs, core peer dependencies, package manager, ' +
|
|
21
|
-
"and the docs the CLI reads. It writes nothing, but loading astryx.config runs that file's code. " +
|
|
19
|
+
'install and version alignment, themes, config, agent docs, and package manager. ' +
|
|
22
20
|
'The `integration` subcommands provide authoring checks for one integration package.',
|
|
23
21
|
fn: 'doctor',
|
|
24
22
|
subcommands: ['integration'],
|
|
@@ -36,8 +36,6 @@ import {doc as integrationTemplateConflictsFn} from '../../../api/integration/in
|
|
|
36
36
|
import {doc as integrationComponentConflictsFn} from '../../../api/integration/integrationComponentConflicts.doc.mjs';
|
|
37
37
|
import {doc as integrationDocConflictsFn} from '../../../api/integration/integrationDocConflicts.doc.mjs';
|
|
38
38
|
import {NO_RESULT_SET} from '../../../foundation/debug/index.mjs';
|
|
39
|
-
import {cliError} from '../lib/cli-error.mjs';
|
|
40
|
-
import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
|
|
41
39
|
|
|
42
40
|
const STATUS = {
|
|
43
41
|
pass: '[ok]',
|
|
@@ -183,10 +181,8 @@ function printComponentConflicts(data) {
|
|
|
183
181
|
),
|
|
184
182
|
...issueBlocks(data.issues),
|
|
185
183
|
];
|
|
186
|
-
// An [ok] after a failed check reads as a pass: say nothing it could not check.
|
|
187
|
-
const failed = data.issues.some(issue => issue.severity === 'error');
|
|
188
184
|
if (data.conflicts.length === 0) {
|
|
189
|
-
|
|
185
|
+
output.push(text('[ok] No component names conflict with Core.'));
|
|
190
186
|
} else {
|
|
191
187
|
output.push(
|
|
192
188
|
records(data.conflicts, {
|
|
@@ -228,9 +224,7 @@ function printDocConflicts(data) {
|
|
|
228
224
|
);
|
|
229
225
|
}
|
|
230
226
|
if (data.findings.length === 0) {
|
|
231
|
-
|
|
232
|
-
output.push(text('[ok] No doc topics overlap with Core.'));
|
|
233
|
-
}
|
|
227
|
+
output.push(text('[ok] No doc topics overlap with Core.'));
|
|
234
228
|
} else {
|
|
235
229
|
output.push(
|
|
236
230
|
records(data.findings, {
|
|
@@ -321,57 +315,19 @@ async function runAuthoringCheck(program, pkg, kind) {
|
|
|
321
315
|
return NO_RESULT_SET;
|
|
322
316
|
}
|
|
323
317
|
|
|
324
|
-
/**
|
|
325
|
-
* The first word after a command group, which names a subcommand it does not
|
|
326
|
-
* have, or null.
|
|
327
|
-
* @param {import('commander').Command | undefined} invoked
|
|
328
|
-
* @returns {string | null}
|
|
329
|
-
*/
|
|
330
|
-
function unknownWord(invoked) {
|
|
331
|
-
const word = (invoked?.args ?? []).find(arg => !String(arg).startsWith('-'));
|
|
332
|
-
return word == null ? null : String(word);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
/**
|
|
336
|
-
* Report an unknown subcommand, in text as in JSON, with the ones the group has.
|
|
337
|
-
* @param {import('commander').Command} group
|
|
338
|
-
* @param {string} label the group's full name
|
|
339
|
-
* @param {string} word
|
|
340
|
-
*/
|
|
341
|
-
function unknownSubcommand(group, label, word) {
|
|
342
|
-
return cliError(`unknown subcommand '${label} ${word}'`, {
|
|
343
|
-
suggestions: group.commands.map(child => ({
|
|
344
|
-
name: child.name(),
|
|
345
|
-
reason: 'available subcommand',
|
|
346
|
-
})),
|
|
347
|
-
code: ERROR_CODES.ERR_UNKNOWN_SUBCOMMAND,
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
|
|
351
318
|
/**
|
|
352
319
|
* Register `astryx doctor` and its integration-authoring leaves.
|
|
353
320
|
* @param {import('commander').Command} program
|
|
354
321
|
*/
|
|
355
322
|
export function registerDoctor(program) {
|
|
356
|
-
|
|
357
|
-
let doctorCmd;
|
|
358
|
-
doctorCmd = defineCommand(program, doctorCommand, {
|
|
323
|
+
const doctorCmd = defineCommand(program, doctorCommand, {
|
|
359
324
|
fn: doctorFn,
|
|
360
|
-
|
|
361
|
-
action: async (options, invoked) => {
|
|
362
|
-
const word = unknownWord(invoked);
|
|
363
|
-
if (word != null) return unknownSubcommand(doctorCmd, 'doctor', word);
|
|
364
|
-
return runProjectDoctor(program);
|
|
365
|
-
},
|
|
325
|
+
action: async () => runProjectDoctor(program),
|
|
366
326
|
});
|
|
367
327
|
/** @type {import('commander').Command} */
|
|
368
328
|
let integrationCmd;
|
|
369
329
|
integrationCmd = defineCommand(doctorCmd, doctorIntegrationGroup, {
|
|
370
|
-
action: (
|
|
371
|
-
const word = unknownWord(invoked);
|
|
372
|
-
if (word != null) {
|
|
373
|
-
return unknownSubcommand(integrationCmd, 'doctor integration', word);
|
|
374
|
-
}
|
|
330
|
+
action: () => {
|
|
375
331
|
integrationCmd.outputHelp();
|
|
376
332
|
return NO_RESULT_SET;
|
|
377
333
|
},
|