@astryxdesign/cli 0.6.4-canary.06c8fa3 → 0.6.4-canary.0e1fbdb
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 +49 -40
- package/api/build/build.doc.mjs +6 -1
- package/api/build/build.test.mjs +22 -0
- package/api/build/kit/kit.mjs +44 -5
- package/api/component/component.doc.mjs +14 -7
- package/api/docs/_adapter.d.mts +8 -3
- package/api/docs/_adapter.mjs +14 -6
- package/api/docs/docOverlays.test.mjs +27 -1
- package/api/docs/docs.doc.mjs +2 -2
- package/api/doctor/doctor.doc.mjs +17 -8
- 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 +19 -10
- package/api/hook/hook.doc.mjs +6 -3
- package/api/index.d.mts +2 -0
- package/api/index.mjs +3 -1
- package/api/init/init.doc.mjs +17 -12
- package/api/integration/add-theme.mjs +22 -1
- package/api/integration/add-theme.test.mjs +34 -0
- package/api/integration/authoring-checks.mjs +2 -2
- package/api/integration/integrationPackCheck.doc.mjs +3 -3
- package/api/integration/pack-check.lifecycle-output.test.mjs +2 -0
- package/api/integration/pack-check.mjs +54 -6
- package/api/integration/pack-check.test.mjs +90 -0
- 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 +27 -1
- package/api/search/search.doc.mjs +2 -2
- package/api/search/search.mjs +228 -16
- package/api/swizzle/swizzle.doc.mjs +7 -5
- package/api/template/copy/copy.mjs +1 -1
- package/api/template/copy/copy.test.mjs +9 -0
- package/api/template/template-integration.test.mjs +65 -1
- package/api/template/template.doc.mjs +2 -1
- package/api/template/template.mjs +1 -1
- package/api/theme/generateTonalPalette.doc.mjs +1 -2
- package/api/theme/listThemes.doc.mjs +1 -1
- package/api/theme/themeAdd.doc.mjs +9 -10
- package/api/theme/themeBuild.doc.mjs +13 -13
- package/api/theme/themeList.doc.mjs +1 -1
- package/api/theme/themeListAvailable.doc.mjs +2 -1
- package/api/theme/themePaletteGenerate.doc.mjs +15 -8
- package/api/theme/themeTargets.doc.mjs +3 -2
- package/api/theme/themeTemplate.doc.mjs +2 -1
- package/api/upgrade/run/run.mjs +1 -1
- package/api/upgrade/upgrade.doc.mjs +24 -22
- package/assets/docs/README.md +4 -2
- package/assets/docs/browser-support.doc.mjs +11 -11
- package/assets/docs/color.doc.mjs +8 -2
- package/assets/docs/elevation.doc.mjs +6 -4
- package/assets/docs/getting-started.doc.mjs +5 -16
- package/assets/docs/icons.doc.mjs +2 -21
- package/assets/docs/illustrations.doc.mjs +7 -15
- package/assets/docs/layout.doc.dense.mjs +130 -82
- package/assets/docs/layout.doc.mjs +133 -77
- package/assets/docs/migration.doc.mjs +19 -21
- package/assets/docs/motion.doc.mjs +16 -3
- package/assets/docs/principles.doc.dense.mjs +5 -5
- package/assets/docs/principles.doc.mjs +8 -0
- package/assets/docs/principles.doc.zh.mjs +6 -6
- package/assets/docs/shape.doc.mjs +8 -3
- package/assets/docs/spacing.doc.mjs +7 -2
- package/assets/docs/styling-libraries.doc.mjs +6 -2
- package/assets/docs/styling.doc.mjs +19 -23
- package/assets/docs/theme.doc.dense.mjs +58 -18
- package/assets/docs/theme.doc.mjs +56 -46
- package/assets/docs/theme.doc.zh.mjs +9 -8
- package/assets/docs/tokens.doc.dense.mjs +2 -2
- package/assets/docs/tokens.doc.mjs +389 -8
- package/assets/docs/tokens.doc.zh.mjs +2 -2
- package/assets/docs/tree/add-a-component.doc.mjs +75 -0
- package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
- package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
- package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
- package/assets/docs/tree/block-template.doc.mjs +130 -0
- package/assets/docs/tree/build-the-template.doc.mjs +28 -0
- package/assets/docs/tree/building-blocks.doc.mjs +46 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
- package/assets/docs/tree/checks.doc.mjs +119 -0
- package/assets/docs/tree/codemods.doc.mjs +147 -0
- package/assets/docs/tree/component-family.doc.mjs +113 -0
- package/assets/docs/tree/component-imports.doc.mjs +69 -0
- package/assets/docs/tree/components.doc.mjs +23 -0
- package/assets/docs/tree/configuration.doc.mjs +23 -0
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
- package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
- package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
- package/assets/docs/tree/docs.doc.mjs +21 -0
- package/assets/docs/tree/document-the-template.doc.mjs +28 -0
- package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
- package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
- package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
- package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
- package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
- package/assets/docs/tree/help.doc.mjs +16 -0
- package/assets/docs/tree/integrations.doc.mjs +25 -470
- package/assets/docs/tree/links.doc.mjs +98 -0
- package/assets/docs/tree/package-and-test.doc.mjs +32 -0
- package/assets/docs/tree/page-template.doc.mjs +71 -0
- package/assets/docs/tree/publishing.doc.mjs +111 -0
- package/assets/docs/tree/quick-start.doc.mjs +272 -0
- package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
- package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
- package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
- package/assets/docs/tree/ship.doc.mjs +16 -0
- package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
- package/assets/docs/tree/single-component.doc.mjs +165 -0
- package/assets/docs/tree/start-a-template.doc.mjs +143 -0
- package/assets/docs/tree/subcomponent.doc.mjs +115 -0
- package/assets/docs/tree/template-assets.doc.mjs +64 -0
- package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
- package/assets/docs/tree/template-fonts.doc.mjs +102 -0
- package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
- package/assets/docs/tree/template-icons.doc.mjs +97 -0
- package/assets/docs/tree/template-images-media.doc.mjs +127 -0
- package/assets/docs/tree/template-styles.doc.mjs +93 -0
- package/assets/docs/tree/templates.doc.mjs +34 -0
- package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
- package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
- package/assets/docs/tree/themes.doc.mjs +39 -0
- package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
- package/assets/docs/tree/upgrading.doc.mjs +103 -0
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
- package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
- package/assets/docs/tree/versioning.doc.mjs +161 -0
- package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
- package/assets/docs/typography.doc.mjs +24 -4
- package/assets/docs/working-with-ai.doc.mjs +30 -22
- package/authoring/config/config.doc.mjs +2 -2
- package/authoring/config/type.ts +2 -2
- package/authoring/doctypes/_schema.d.mts +3 -2
- package/authoring/doctypes/_schema.mjs +6 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
- package/authoring/doctypes/base/type.ts +4 -2
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +1 -1
- package/authoring/doctypes/component/component.doc.mjs +6 -0
- package/authoring/doctypes/component/type.ts +8 -0
- package/authoring/doctypes/reference/reference.doc.mjs +7 -0
- package/authoring/doctypes/reference/type.ts +5 -0
- 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/integration/integration.doc.mjs +12 -10
- package/clients/cli/command-result-coverage.test.mjs +7 -7
- package/clients/cli/commands/component.doc.mjs +4 -3
- package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
- package/clients/cli/commands/docs.doc.mjs +1 -1
- package/clients/cli/commands/docs.mjs +60 -17
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
- package/clients/cli/commands/doctor-integration.test.mjs +53 -0
- package/clients/cli/commands/doctor.doc.mjs +3 -1
- package/clients/cli/commands/doctor.mjs +49 -5
- package/clients/cli/commands/gap-report.doc.mjs +10 -9
- package/clients/cli/commands/init.doc.mjs +9 -6
- package/clients/cli/commands/integration-add.doc.mjs +9 -9
- package/clients/cli/commands/integration-authoring.test.mjs +61 -10
- package/clients/cli/commands/integration-pack.doc.mjs +5 -9
- package/clients/cli/commands/integration-real-world.test.mjs +1 -1
- package/clients/cli/commands/integration-verify.doc.mjs +22 -0
- package/clients/cli/commands/integration.doc.mjs +4 -4
- package/clients/cli/commands/integration.mjs +74 -43
- 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 +10 -3
- package/clients/cli/commands/search.mjs +21 -2
- package/clients/cli/commands/search.test.mjs +21 -4
- 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 +24 -1
- package/clients/cli/commands/theme-add.doc.mjs +1 -1
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
- package/clients/cli/commands/theme-palette.doc.mjs +1 -2
- package/clients/cli/commands/theme-targets.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +2 -1
- package/clients/cli/commands/upgrade.doc.mjs +62 -3
- package/clients/cli/index.mjs +32 -6
- package/clients/cli/lib/define-command.mjs +28 -4
- package/clients/cli/lib/define-command.test.mjs +54 -0
- package/clients/cli/lib/exit-codes.test.mjs +25 -2
- package/clients/cli/lib/json-shim.test.mjs +20 -6
- package/clients/cli/lib/manifest.mjs +23 -5
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
- package/foundation/discovery/cli-self-docs.mjs +16 -2
- package/foundation/discovery/cli-self-docs.test.mjs +20 -0
- package/foundation/discovery/docs-discovery.mjs +5 -1
- package/foundation/discovery/docs-discovery.test.mjs +21 -0
- 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 +15 -2
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/tree.d.mts +4 -0
- package/foundation/doc-compiler/tree.mjs +6 -1
- package/foundation/integrations/cli-requirement.d.mts +26 -6
- package/foundation/integrations/cli-requirement.mjs +46 -11
- package/foundation/integrations/cli-requirement.test.mjs +7 -2
- package/foundation/integrations/contribution-inventory.mjs +1 -1
- package/foundation/response/error-codes.doc.mjs +6 -8
- package/foundation/response/error-codes.test.mjs +30 -5
- package/foundation/response/response-types.doc.d.mts +4 -3
- package/foundation/response/response-types.doc.mjs +42 -6
- package/foundation/response/response.doc.mjs +11 -10
- 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/docs/docs.test.mjs +0 -245
- package/api/docs/integration-tree.test.mjs +0 -555
- package/api/docs/integrationDocs.test.mjs +0 -314
- package/api/search/search.test.mjs +0 -530
- package/assets/docs/tree/integrations.test.mjs +0 -62
- package/assets/docs/tree/writing-docs.doc.mjs +0 -286
- package/clients/cli/commands/docs.test.mjs +0 -323
- package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
- package/foundation/doc-compiler/tree.test.mjs +0 -606
|
@@ -13,7 +13,8 @@ export const doc = {
|
|
|
13
13
|
name: 'theme',
|
|
14
14
|
displayName: 'astryx theme',
|
|
15
15
|
namespace: 'cli/commands',
|
|
16
|
-
summary:
|
|
16
|
+
summary:
|
|
17
|
+
'Create and build themes: add a shipped one, compile to CSS, or list what a theme can override',
|
|
17
18
|
description:
|
|
18
19
|
'The theme command group. Running astryx theme with no subcommand prints the ' +
|
|
19
20
|
'subcommand list; the work happens in the subcommands: compile a theme (build), ' +
|
|
@@ -13,7 +13,8 @@ export const doc = {
|
|
|
13
13
|
name: 'upgrade',
|
|
14
14
|
displayName: 'astryx upgrade',
|
|
15
15
|
namespace: 'cli/commands',
|
|
16
|
-
summary:
|
|
16
|
+
summary:
|
|
17
|
+
'Update your code after upgrading Astryx, and refresh ShadCN-copied components',
|
|
17
18
|
description:
|
|
18
19
|
'Migrates project source from a previous Astryx version to the installed one by ' +
|
|
19
20
|
'running the registered codemods, and refreshes the fully rendered managed ' +
|
|
@@ -33,7 +34,7 @@ export const doc = {
|
|
|
33
34
|
{
|
|
34
35
|
flag: '--apply',
|
|
35
36
|
param: 'options.apply',
|
|
36
|
-
description: 'Write changes to disk
|
|
37
|
+
description: 'Write changes to disk; without it, the run is a dry run',
|
|
37
38
|
default: false,
|
|
38
39
|
},
|
|
39
40
|
{
|
|
@@ -80,7 +81,8 @@ export const doc = {
|
|
|
80
81
|
flag: '--registry',
|
|
81
82
|
param: 'options.registry',
|
|
82
83
|
description:
|
|
83
|
-
'Only
|
|
84
|
+
'Only update ShadCN-copied compositions from their install receipts: unchanged files are updated, ' +
|
|
85
|
+
'edits that do not overlap are merged, and conflicts are left untouched; --from is not required. ' +
|
|
84
86
|
'Combining it with --list, --from, --force, --codemod, --skip-codemod, --integration or --install-deps exits 1 with ERR_INVALID_ARGUMENT',
|
|
85
87
|
default: false,
|
|
86
88
|
},
|
|
@@ -113,4 +115,61 @@ export const doc = {
|
|
|
113
115
|
},
|
|
114
116
|
],
|
|
115
117
|
related: ['init', 'doctor'],
|
|
118
|
+
notes: [
|
|
119
|
+
{type: 'heading', level: 3, text: 'Protected files'},
|
|
120
|
+
{
|
|
121
|
+
type: 'prose',
|
|
122
|
+
text:
|
|
123
|
+
'Codemods never write to a file your project marks as generated, vendored, or ignored. ' +
|
|
124
|
+
'upgrade reads these marks from the files on disk, so the answer is the same with any version control, or none. ' +
|
|
125
|
+
'A file is protected when:',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: 'list',
|
|
129
|
+
style: 'unordered',
|
|
130
|
+
items: [
|
|
131
|
+
'a `.gitattributes` file marks it `linguist-generated` or `linguist-vendored`',
|
|
132
|
+
'its leading comment says `@generated`, `@partially-generated`, or `Code generated ... DO NOT EDIT.`',
|
|
133
|
+
'a `.gitignore`, or the `.hgignore` at the project root, excludes it',
|
|
134
|
+
'it is an installed dependency (such as anything in `node_modules`), is inside `.git`, `.hg`, or `.sl`, is a symbolic link, or is outside the project',
|
|
135
|
+
],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
type: 'prose',
|
|
139
|
+
text:
|
|
140
|
+
'Rules work as they do in Git: a later rule wins, so `linguist-generated=false` or a `!` line in an ignore file ' +
|
|
141
|
+
'returns a file to normal handling. A folder name such as `dist` or `generated` protects nothing by itself. ' +
|
|
142
|
+
'If a protection file cannot be read or parsed, upgrade stops with ERR_CODEMOD_PROTECTION_SOURCE before it writes anything.',
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'code',
|
|
146
|
+
lang: 'text',
|
|
147
|
+
code:
|
|
148
|
+
'# .gitattributes\n' +
|
|
149
|
+
'generated/** linguist-generated=true\n' +
|
|
150
|
+
'vendor/** linguist-vendored=true\n' +
|
|
151
|
+
'\n' +
|
|
152
|
+
'# A later rule returns one authored file to normal handling\n' +
|
|
153
|
+
'generated/hand-authored.ts linguist-generated=false',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'prose',
|
|
157
|
+
text:
|
|
158
|
+
'When a codemod would change a protected file, upgrade makes the change only in memory and leaves the file as it is. ' +
|
|
159
|
+
'The rest of the upgrade goes ahead. With --apply, your other files are written, then upgrade runs the ' +
|
|
160
|
+
'`hooks.postCodemod` commands from astryx.config (see `astryx docs authoring config`) and checks the protected ' +
|
|
161
|
+
'files again. If one still needs the change, the run is incomplete: it exits 1, prints ' +
|
|
162
|
+
'ERR_CODEMOD_PROTECTED with each file and the rule that protects it, and does not refresh the agent docs. ' +
|
|
163
|
+
'Regenerate or edit those files, then run the same upgrade again. A dry run reports the same files and writes nothing.',
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: 'prose',
|
|
167
|
+
text:
|
|
168
|
+
'With --json, the receipt says `complete: false` and `errorCode: "ERR_CODEMOD_PROTECTED"`. `modifiedFiles` lists the files ' +
|
|
169
|
+
'upgrade changed (or would change), and `protectedFiles` lists each blocked file with its `reasons`, `declarations` ' +
|
|
170
|
+
'(the rules that protect it), `codemods`, and `commands`. A generated file can name the command that rebuilds it on a ' +
|
|
171
|
+
'`Command:` line in its header, such as `// Command: pnpm run gen:panel`; upgrade prints it as ' +
|
|
172
|
+
'`Regenerate with: <command>` and lists it in `commands`.',
|
|
173
|
+
},
|
|
174
|
+
],
|
|
116
175
|
};
|
package/clients/cli/index.mjs
CHANGED
|
@@ -24,7 +24,7 @@ import {emit, section, text, records} from './formatters/index.mjs';
|
|
|
24
24
|
import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
|
|
25
25
|
import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
|
|
26
26
|
import {installJsonShim} from './lib/json-shim.mjs';
|
|
27
|
-
import {
|
|
27
|
+
import {addDocHelp, markReportsResult} from './lib/define-command.mjs';
|
|
28
28
|
import {doc as manifestDoc} from './commands/manifest.doc.mjs';
|
|
29
29
|
import {isAstryxInitialized} from '../../foundation/agent-docs/agent-docs.mjs';
|
|
30
30
|
import * as debug from '../../foundation/debug/index.mjs';
|
|
@@ -95,6 +95,7 @@ export const JSON_SUPPORTED = new Set([
|
|
|
95
95
|
'theme targets',
|
|
96
96
|
'theme palette generate',
|
|
97
97
|
'integration add',
|
|
98
|
+
'integration verify',
|
|
98
99
|
'integration pack',
|
|
99
100
|
'upgrade',
|
|
100
101
|
'manifest',
|
|
@@ -103,6 +104,9 @@ export const JSON_SUPPORTED = new Set([
|
|
|
103
104
|
'doctor integration templates',
|
|
104
105
|
'doctor integration components',
|
|
105
106
|
'doctor integration docs',
|
|
107
|
+
'layout expand',
|
|
108
|
+
'layout check',
|
|
109
|
+
'layout grammar',
|
|
106
110
|
]);
|
|
107
111
|
|
|
108
112
|
/**
|
|
@@ -261,6 +265,7 @@ const commands = [
|
|
|
261
265
|
{name: 'gap-report', path: './commands/gap-report.mjs', register: 'registerGapReport'},
|
|
262
266
|
// agent-docs folded into init — functions still importable from agent-docs.mjs
|
|
263
267
|
{name: 'template', path: './commands/template.mjs', register: 'registerTemplate'},
|
|
268
|
+
{name: 'layout', path: './commands/layout.mjs', register: 'registerLayout'},
|
|
264
269
|
{name: 'upgrade', path: './commands/upgrade.mjs', register: 'registerUpgrade'},
|
|
265
270
|
{name: 'theme', path: './commands/build-theme.mjs', register: 'registerTheme'},
|
|
266
271
|
{name: 'integration', path: './commands/integration.mjs', register: 'registerIntegration'},
|
|
@@ -338,16 +343,24 @@ export async function createProgram() {
|
|
|
338
343
|
.name('astryx')
|
|
339
344
|
.description('Design system CLI — components, themes, and tooling')
|
|
340
345
|
.version(pkg.version)
|
|
341
|
-
|
|
342
|
-
|
|
346
|
+
// These four change only the reads named in their text; every other
|
|
347
|
+
// command ignores them.
|
|
348
|
+
.option(
|
|
349
|
+
'--zh',
|
|
350
|
+
'Simplified Chinese for component reads and for docs topics that have a translation (English otherwise)',
|
|
351
|
+
)
|
|
352
|
+
.option('--dense', 'Token-efficient dense text for component <Name> and docs <topic> reads')
|
|
343
353
|
.addOption(
|
|
344
354
|
new Option(
|
|
345
355
|
'--lang <locale>',
|
|
346
|
-
'
|
|
356
|
+
'Language or format for component and docs reads: en (default), zh (as --zh), or dense (as --dense)',
|
|
347
357
|
).choices(['en', 'zh', 'dense']),
|
|
348
358
|
)
|
|
349
359
|
.addOption(
|
|
350
|
-
new Option(
|
|
360
|
+
new Option(
|
|
361
|
+
'--detail <level>',
|
|
362
|
+
'Detail level for component, hook, and docs tree reads (e.g. docs cli/commands/build). Lists default to brief',
|
|
363
|
+
)
|
|
351
364
|
.choices(['full', 'compact', 'brief'])
|
|
352
365
|
.default('full'),
|
|
353
366
|
)
|
|
@@ -467,6 +480,19 @@ export async function createProgram() {
|
|
|
467
480
|
const fullName = fullCommandName(actionCommand, program);
|
|
468
481
|
if (JSON_SUPPORTED.has(fullName)) return;
|
|
469
482
|
process.__xdsJsonHandled = true;
|
|
483
|
+
// A group given a word it does not have reports an unknown subcommand and
|
|
484
|
+
// lists the ones it has, in JSON as in text, even when a flag follows it.
|
|
485
|
+
const extras = actionCommand.commands.length > 0 ? actionCommand.args : [];
|
|
486
|
+
const unknown = extras.find(arg => !String(arg).startsWith('-'));
|
|
487
|
+
if (unknown != null) {
|
|
488
|
+
cliError(`unknown subcommand '${fullName} ${unknown}'`, {
|
|
489
|
+
code: ERROR_CODES.ERR_UNKNOWN_SUBCOMMAND,
|
|
490
|
+
suggestions: actionCommand.commands.map(child => ({
|
|
491
|
+
name: child.name(),
|
|
492
|
+
reason: 'available subcommand',
|
|
493
|
+
})),
|
|
494
|
+
});
|
|
495
|
+
}
|
|
470
496
|
debug.setOutcome('rejected', {
|
|
471
497
|
exitCode: 1,
|
|
472
498
|
code: ERROR_CODES.ERR_INVALID_OPTION,
|
|
@@ -601,7 +627,7 @@ export async function createProgram() {
|
|
|
601
627
|
text(`Run \`${getCliInvocation()} manifest --json\` for the full structured manifest.`),
|
|
602
628
|
);
|
|
603
629
|
});
|
|
604
|
-
|
|
630
|
+
addDocHelp(manifestCommand, manifestDoc);
|
|
605
631
|
markReportsResult(manifestCommand);
|
|
606
632
|
|
|
607
633
|
// Hidden command used by package.json postinstall scripts
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
import {recordCommandResult} from '../../../foundation/debug/index.mjs';
|
|
28
|
+
import {routeSegment} from '../../../foundation/discovery/docs-section-key.mjs';
|
|
29
|
+
import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
|
|
28
30
|
import {text} from '../formatters/index.mjs';
|
|
29
31
|
|
|
30
32
|
/**
|
|
@@ -143,10 +145,11 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
|
|
|
143
145
|
cmd.addOption(option);
|
|
144
146
|
}
|
|
145
147
|
|
|
146
|
-
// Help ends with the documented exit codes
|
|
147
|
-
//
|
|
148
|
-
// ERR_INVALID_ARGUMENT
|
|
149
|
-
|
|
148
|
+
// Help ends with the documented exit codes, the examples, and the docs
|
|
149
|
+
// route that reads the whole command. `choices` stay in the option text:
|
|
150
|
+
// Commander `.choices()` would replace the api layer's ERR_INVALID_ARGUMENT
|
|
151
|
+
// validation.
|
|
152
|
+
addDocHelp(cmd, doc);
|
|
150
153
|
|
|
151
154
|
if (action) {
|
|
152
155
|
// The recording seam. An action's job ends at "here is what I answered
|
|
@@ -165,6 +168,27 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
|
|
|
165
168
|
return cmd;
|
|
166
169
|
}
|
|
167
170
|
|
|
171
|
+
/**
|
|
172
|
+
* End `cmd`'s help with what its CommandDoc says: the exit codes, then the
|
|
173
|
+
* examples, then `More:`, the `astryx docs` route that reads the whole command.
|
|
174
|
+
* @param {import('commander').Command} cmd
|
|
175
|
+
* @param {import('@astryxdesign/cli/authoring').CommandDoc} doc
|
|
176
|
+
*/
|
|
177
|
+
export function addDocHelp(cmd, doc) {
|
|
178
|
+
addExitCodesHelp(cmd, doc.exitCodes);
|
|
179
|
+
// Rendered when help is shown, so the run prefix (npx astryx, pnpm astryx,
|
|
180
|
+
// ...) is looked up then, not on every start.
|
|
181
|
+
cmd.addHelpText('after', () => {
|
|
182
|
+
const examples = (doc.examples ?? []).flatMap(({label, cli}) => [
|
|
183
|
+
...(label ? [` # ${label}`] : []),
|
|
184
|
+
` ${formatCliCommand(cli)}`,
|
|
185
|
+
]);
|
|
186
|
+
const more = `More: ${formatCliCommand(`docs cli/commands/${routeSegment(doc.name)}`)}`;
|
|
187
|
+
const blocks = examples.length > 0 ? [['Examples:', ...examples].join('\n'), more] : [more];
|
|
188
|
+
return `\n${text(blocks.join('\n\n')).toString()}`;
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
168
192
|
/**
|
|
169
193
|
* End `cmd`'s help with a CommandDoc's exit codes.
|
|
170
194
|
* @param {import('commander').Command} cmd
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import {Command} from 'commander';
|
|
8
8
|
import {describe, it, expect} from 'vitest';
|
|
9
9
|
import {defineCommand} from './define-command.mjs';
|
|
10
|
+
import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
|
|
10
11
|
import {doc as searchCommand} from '../commands/search.doc.mjs';
|
|
11
12
|
import {doc as searchFn} from '../../../api/search/search.doc.mjs';
|
|
12
13
|
|
|
@@ -46,4 +47,57 @@ describe('defineCommand', () => {
|
|
|
46
47
|
expect(cmd.name()).toBe('build');
|
|
47
48
|
expect(cmd.registeredArguments.map(a => a.name())).toEqual(['file']);
|
|
48
49
|
});
|
|
50
|
+
|
|
51
|
+
it('ends help with the exit codes, the examples, and the docs route', () => {
|
|
52
|
+
const program = new Command();
|
|
53
|
+
const group = program.command('grp');
|
|
54
|
+
const cmd = defineCommand(
|
|
55
|
+
group,
|
|
56
|
+
{
|
|
57
|
+
type: 'command',
|
|
58
|
+
name: 'grp sub',
|
|
59
|
+
displayName: 'astryx grp sub',
|
|
60
|
+
summary: 'Sub.',
|
|
61
|
+
examples: [
|
|
62
|
+
{label: 'Run it', cli: 'astryx grp sub x'},
|
|
63
|
+
{cli: 'astryx grp sub y --json'},
|
|
64
|
+
],
|
|
65
|
+
exitCodes: [{code: 0, when: 'it works'}],
|
|
66
|
+
},
|
|
67
|
+
{action: () => {}},
|
|
68
|
+
);
|
|
69
|
+
let out = '';
|
|
70
|
+
cmd.configureOutput({writeOut: s => (out += s)});
|
|
71
|
+
cmd.outputHelp();
|
|
72
|
+
const stem = formatCliCommand('');
|
|
73
|
+
expect(out.slice(out.indexOf('\nExit codes:\n'))).toBe(
|
|
74
|
+
[
|
|
75
|
+
'',
|
|
76
|
+
'Exit codes:',
|
|
77
|
+
' 0 it works',
|
|
78
|
+
'',
|
|
79
|
+
'Examples:',
|
|
80
|
+
' # Run it',
|
|
81
|
+
` ${stem} grp sub x`,
|
|
82
|
+
` ${stem} grp sub y --json`,
|
|
83
|
+
'',
|
|
84
|
+
`More: ${stem} docs cli/commands/grp-sub`,
|
|
85
|
+
'',
|
|
86
|
+
].join('\n'),
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('still names the docs route when a command has no examples', () => {
|
|
91
|
+
const program = new Command();
|
|
92
|
+
const cmd = defineCommand(
|
|
93
|
+
program,
|
|
94
|
+
{type: 'command', name: 'solo', summary: 'Solo.', exitCodes: [{code: 0, when: 'ok'}]},
|
|
95
|
+
{action: () => {}},
|
|
96
|
+
);
|
|
97
|
+
let out = '';
|
|
98
|
+
cmd.configureOutput({writeOut: s => (out += s)});
|
|
99
|
+
cmd.outputHelp();
|
|
100
|
+
expect(out).not.toContain('Examples:');
|
|
101
|
+
expect(out.endsWith(`\n\nMore: ${formatCliCommand('docs cli/commands/solo')}\n`)).toBe(true);
|
|
102
|
+
});
|
|
49
103
|
});
|
|
@@ -34,14 +34,14 @@ manifest.commands.forEach(walk);
|
|
|
34
34
|
|
|
35
35
|
describe('command exit codes', () => {
|
|
36
36
|
it('every CommandDoc documents its exit codes', () => {
|
|
37
|
-
expect(commandDocs.length).toBeGreaterThan(
|
|
37
|
+
expect(commandDocs.length).toBeGreaterThan(30);
|
|
38
38
|
for (const doc of commandDocs) {
|
|
39
39
|
expect(doc.exitCodes?.length, doc.name).toBeGreaterThan(0);
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
it.each(commandDocs.map((d) => [d.name, d]))(
|
|
44
|
-
'`astryx %s --help` lists the documented exit codes',
|
|
44
|
+
'`astryx %s --help` lists the documented exit codes, then the examples and the docs route',
|
|
45
45
|
async (name, doc) => {
|
|
46
46
|
const {status, stdout} = await runCli([...name.split(' '), '--help']);
|
|
47
47
|
expect(status).toBe(0);
|
|
@@ -51,9 +51,32 @@ describe('command exit codes', () => {
|
|
|
51
51
|
for (const {code, when} of doc.exitCodes) {
|
|
52
52
|
expect(section).toContain(`\n ${code} ${when}\n`);
|
|
53
53
|
}
|
|
54
|
+
// Examples follow the exit codes, each under its label, and a `More:`
|
|
55
|
+
// line names the route that reads the whole command.
|
|
56
|
+
const examples = section.indexOf('\nExamples:\n');
|
|
57
|
+
expect(examples > 0, stdout).toBe((doc.examples ?? []).length > 0);
|
|
58
|
+
for (const {label, cli} of doc.examples ?? []) {
|
|
59
|
+
const line = ` ${cli.replace(/^astryx\s+/, '')}\n`;
|
|
60
|
+
expect(section.slice(examples), stdout).toContain(
|
|
61
|
+
label ? `\n # ${label}\n` : line,
|
|
62
|
+
);
|
|
63
|
+
expect(section.slice(examples)).toContain(line);
|
|
64
|
+
}
|
|
65
|
+
const route = `docs cli/commands/${name.replace(/ /g, '-')}`;
|
|
66
|
+
expect(section, stdout).toMatch(
|
|
67
|
+
new RegExp(`\\n\\nMore: \\S.* ${route}\\n`),
|
|
68
|
+
);
|
|
69
|
+
expect(section.indexOf('\nMore: ')).toBeGreaterThan(examples);
|
|
54
70
|
},
|
|
55
71
|
);
|
|
56
72
|
|
|
73
|
+
it('bare `astryx layout` exits 1 in both modes, as documented', async () => {
|
|
74
|
+
const doc = commandDocs.find((d) => d.name === 'layout');
|
|
75
|
+
expect(doc.exitCodes.find((e) => e.code === 1)?.when).toMatch(/^no subcommand/);
|
|
76
|
+
expect((await runCli(['layout'])).status).toBe(1);
|
|
77
|
+
expect((await runCli(['layout', '--json'])).status).toBe(1);
|
|
78
|
+
});
|
|
79
|
+
|
|
57
80
|
it('`astryx discover` with a blank query exits 1 only when packages are discovered', async () => {
|
|
58
81
|
const doc = commandDocs.find((d) => d.name === 'discover');
|
|
59
82
|
expect(doc.exitCodes.find((e) => e.code === 1)?.when).toMatch(/blank query when packages are discovered/);
|
|
@@ -225,6 +225,23 @@ describe('--json shim: help shown for a failed invocation is an error envelope',
|
|
|
225
225
|
expect(parsed.error).toMatch(/bogus/);
|
|
226
226
|
});
|
|
227
227
|
|
|
228
|
+
it('astryx layout --json (group, no subcommand) emits ERR_MISSING_ARGUMENT, exit 1', async () => {
|
|
229
|
+
const {status, stdout, stderr} = await runCli(['layout', '--json']);
|
|
230
|
+
expect(status).toBe(1);
|
|
231
|
+
expect(stderr).toBe('');
|
|
232
|
+
const parsed = parseJson(stdout);
|
|
233
|
+
expect(parsed).not.toHaveProperty('type');
|
|
234
|
+
expect(parsed.code).toBe('ERR_MISSING_ARGUMENT');
|
|
235
|
+
expect(parsed.suggestions.map((s) => s.name)).toContain('layout expand');
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('astryx layout (no --json) still prints help to stderr, exit 1', async () => {
|
|
239
|
+
const {status, stdout, stderr} = await runCli(['layout']);
|
|
240
|
+
expect(status).toBe(1);
|
|
241
|
+
expect(stdout).toBe('');
|
|
242
|
+
expect(stderr).toMatch(/Usage: astryx layout/);
|
|
243
|
+
});
|
|
244
|
+
|
|
228
245
|
it('the real binary emits the same envelope for help bogus --json', () => {
|
|
229
246
|
// One program per process, so the root's own outputHelp patch is used here.
|
|
230
247
|
const bin = fileURLToPath(new URL('../bin/astryx.mjs', import.meta.url));
|
|
@@ -240,9 +257,6 @@ describe('--json shim: help shown for a failed invocation is an error envelope',
|
|
|
240
257
|
});
|
|
241
258
|
|
|
242
259
|
it('a group added after install gets the same error envelope', () => {
|
|
243
|
-
// This is the only coverage of the no-subcommand path: every group the CLI
|
|
244
|
-
// ships has an action of its own (see command-result-coverage.test.mjs), so
|
|
245
|
-
// a group without one has to be built here.
|
|
246
260
|
const program = new Command('astryx');
|
|
247
261
|
installJsonShim(program);
|
|
248
262
|
// Added later, so only the prototype-level patch covers its outputHelp.
|
|
@@ -268,11 +282,11 @@ describe('--json shim: help shown for a failed invocation is an error envelope',
|
|
|
268
282
|
expect(parsed.suggestions).toEqual([{name: 'late child', reason: 'available subcommand'}]);
|
|
269
283
|
});
|
|
270
284
|
|
|
271
|
-
it('astryx help
|
|
272
|
-
const {status, stdout} = await runCli(['help', '
|
|
285
|
+
it('astryx help layout --json still emits the help envelope, exit 0', async () => {
|
|
286
|
+
const {status, stdout} = await runCli(['help', 'layout', '--json']);
|
|
273
287
|
expect(status).toBe(0);
|
|
274
288
|
const parsed = parseJson(stdout);
|
|
275
289
|
expect(parsed.type).toBe('help');
|
|
276
|
-
expect(parsed.data.command).toBe('astryx
|
|
290
|
+
expect(parsed.data.command).toBe('astryx layout');
|
|
277
291
|
});
|
|
278
292
|
});
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* `type` discriminators it can emit — are layered on from:
|
|
15
15
|
*
|
|
16
16
|
* - JSON_SUPPORTED (the allowlist in index.mjs), and
|
|
17
|
-
* - RESPONSE_TYPES (the declarative map below
|
|
17
|
+
* - RESPONSE_TYPES (the declarative map below; ROOT_RESPONSE_TYPES names the
|
|
18
|
+
* two, help and version, that no single command owns).
|
|
18
19
|
*
|
|
19
20
|
* A drift-guard test (manifest.test.mjs) asserts every registered command
|
|
20
21
|
* appears in the manifest and every JSON-supported command has a response-type
|
|
@@ -92,16 +93,28 @@ export const RESPONSE_TYPES = {
|
|
|
92
93
|
'theme targets': ['theme.targets'],
|
|
93
94
|
'theme palette generate': ['theme.palette.generate'],
|
|
94
95
|
'integration add': ['integration.add'],
|
|
96
|
+
'integration verify': ['integration.pack-check'],
|
|
95
97
|
'integration pack': ['integration.pack-check'],
|
|
96
|
-
upgrade: ['upgrade.list', 'upgrade.status', 'upgrade.run'],
|
|
98
|
+
upgrade: ['upgrade.list', 'upgrade.registry', 'upgrade.status', 'upgrade.run'],
|
|
97
99
|
manifest: ['manifest'],
|
|
98
100
|
doctor: ['doctor'],
|
|
99
101
|
'doctor integration validate': ['integration.validate'],
|
|
100
102
|
'doctor integration templates': ['integration.template-conflicts'],
|
|
101
103
|
'doctor integration components': ['integration.component-conflicts'],
|
|
102
104
|
'doctor integration docs': ['integration.doc-conflicts'],
|
|
105
|
+
'layout expand': ['layout.expand'],
|
|
106
|
+
'layout check': ['layout.check'],
|
|
107
|
+
'layout grammar': ['layout.grammar'],
|
|
103
108
|
};
|
|
104
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Response types no single command owns: `help` (a bare `astryx --json`, and
|
|
112
|
+
* `--help --json` on any command) and `version` (`astryx --version --json`).
|
|
113
|
+
* The response-types enum lists them beside {@link RESPONSE_TYPES}.
|
|
114
|
+
* @type {readonly string[]}
|
|
115
|
+
*/
|
|
116
|
+
export const ROOT_RESPONSE_TYPES = Object.freeze(['help', 'version']);
|
|
117
|
+
|
|
105
118
|
/**
|
|
106
119
|
* Example invocations per fully-qualified command. Optional, agent-facing.
|
|
107
120
|
* @type {Record<string, string[]>}
|
|
@@ -118,7 +131,7 @@ const EXAMPLES = {
|
|
|
118
131
|
'astryx docs spacing --json',
|
|
119
132
|
'astryx docs theme',
|
|
120
133
|
'astryx docs theme quick-start',
|
|
121
|
-
'astryx docs cli/integrations --full',
|
|
134
|
+
'astryx docs cli/integrations/quick-start --full',
|
|
122
135
|
],
|
|
123
136
|
discover: ['astryx discover --json'],
|
|
124
137
|
search: [
|
|
@@ -126,7 +139,7 @@ const EXAMPLES = {
|
|
|
126
139
|
'astryx search button --type component --json',
|
|
127
140
|
],
|
|
128
141
|
build: ['astryx build', 'astryx build "analytics dashboard" --json'],
|
|
129
|
-
swizzle: ['astryx swizzle
|
|
142
|
+
swizzle: ['astryx swizzle Button'],
|
|
130
143
|
'gap-report': [
|
|
131
144
|
'astryx gap-report --list-categories',
|
|
132
145
|
"astryx gap-report Button --category docs_gap --reason 'Missing keyboard example'",
|
|
@@ -159,7 +172,7 @@ const EXAMPLES = {
|
|
|
159
172
|
'astryx integration add component AcmeWidget',
|
|
160
173
|
'astryx integration add doc deploying --dry-run --json',
|
|
161
174
|
],
|
|
162
|
-
'integration
|
|
175
|
+
'integration verify': ['astryx integration verify --json'],
|
|
163
176
|
upgrade: ['astryx upgrade --json'],
|
|
164
177
|
manifest: ['astryx manifest --json', 'astryx --json'],
|
|
165
178
|
doctor: ['astryx doctor', 'astryx doctor --json'],
|
|
@@ -180,6 +193,11 @@ const EXAMPLES = {
|
|
|
180
193
|
'astryx doctor integration docs @acme/widgets --json',
|
|
181
194
|
],
|
|
182
195
|
init: ['astryx init', 'astryx init --all --json'],
|
|
196
|
+
'layout expand': [
|
|
197
|
+
`astryx layout expand 'V[g6] > C{card-callout}*4' ./src/Page.tsx`,
|
|
198
|
+
],
|
|
199
|
+
'layout check': [`astryx layout check 'A[cp6] > L > LC > S[p6]' --json`],
|
|
200
|
+
'layout grammar': ['astryx layout grammar'],
|
|
183
201
|
};
|
|
184
202
|
|
|
185
203
|
/**
|
|
@@ -491,7 +491,7 @@ export function generateCompressedIndex(
|
|
|
491
491
|
}
|
|
492
492
|
lines.push(' docs cli commands, API reference, integration authoring (one level at a time)');
|
|
493
493
|
lines.push(' swizzle <Name> eject component source for deep customization');
|
|
494
|
-
lines.push(' upgrade --apply
|
|
494
|
+
lines.push(' upgrade --from <old version> --apply run after any Astryx or integration dependency bump');
|
|
495
495
|
const appendCount = agentDocs.reduce(
|
|
496
496
|
(count, contribution) => count + contribution.append.length,
|
|
497
497
|
0,
|
|
@@ -63,7 +63,9 @@ describe('authoring self-docs', () => {
|
|
|
63
63
|
describe('what the authoring docs say about the docs tree', () => {
|
|
64
64
|
it('marks exactly the fields the docs tree does not read yet', () => {
|
|
65
65
|
const notReadYet = graphFieldsDoc.fields
|
|
66
|
-
.filter(field =>
|
|
66
|
+
.filter(field =>
|
|
67
|
+
/^Reserved: .*Nothing reads it today/.test(field.description),
|
|
68
|
+
)
|
|
67
69
|
.map(field => field.name);
|
|
68
70
|
// The tree reads `placement` for every guide (spec:AST-046); the other
|
|
69
71
|
// graph fields are still refused by every topic reader.
|
|
@@ -79,7 +81,9 @@ describe('what the authoring docs say about the docs tree', () => {
|
|
|
79
81
|
expect(placement.description).toMatch(
|
|
80
82
|
/Read for every guide, the CLI's and each integration's/,
|
|
81
83
|
);
|
|
82
|
-
expect(graphFieldsDoc.description).toMatch(
|
|
84
|
+
expect(graphFieldsDoc.description).toMatch(
|
|
85
|
+
/Nothing reads `aliases` or `audience` today/,
|
|
86
|
+
);
|
|
83
87
|
});
|
|
84
88
|
|
|
85
89
|
it('keeps graph blocks behind the separate GraphContentBlock type, and a section takes a reference block', () => {
|
|
@@ -315,12 +315,20 @@ function functionSection(fn, index) {
|
|
|
315
315
|
}
|
|
316
316
|
const params = fn.params ?? [];
|
|
317
317
|
if (params.length > 0) {
|
|
318
|
+
// A Default column only when some parameter declares a default, so a
|
|
319
|
+
// function with none keeps a three-column table.
|
|
320
|
+
const defaults = params.some(
|
|
321
|
+
(/** @type {any} */ p) => typeof p.default === 'string' && p.default !== '',
|
|
322
|
+
);
|
|
318
323
|
content.push({
|
|
319
324
|
type: 'table',
|
|
320
|
-
headers:
|
|
325
|
+
headers: defaults
|
|
326
|
+
? ['Parameter', 'Type', 'Default', 'Description']
|
|
327
|
+
: ['Parameter', 'Type', 'Description'],
|
|
321
328
|
rows: params.map((/** @type {any} */ p) => [
|
|
322
|
-
`\`${p.name}
|
|
329
|
+
`\`${p.name}\`${p.required ? ' (required)' : ''}`,
|
|
323
330
|
`\`${p.type ?? ''}\``,
|
|
331
|
+
...(defaults ? [p.default ?? ''] : []),
|
|
324
332
|
p.description ?? '',
|
|
325
333
|
]),
|
|
326
334
|
});
|
|
@@ -405,6 +413,12 @@ export function cliDocSection(doc, index) {
|
|
|
405
413
|
if (doc.type === 'command') return commandSection(doc, index);
|
|
406
414
|
if (doc.type === 'function') return functionSection(doc, index);
|
|
407
415
|
if (doc.type === 'enum') return enumSection(doc);
|
|
416
|
+
if (doc.type === 'namespace')
|
|
417
|
+
return {
|
|
418
|
+
id: routeSegment(doc.name),
|
|
419
|
+
title: doc.title,
|
|
420
|
+
content: doc.blocks ?? [],
|
|
421
|
+
};
|
|
408
422
|
return {...selfDocSection(doc), id: routeSegment(doc.name)};
|
|
409
423
|
}
|
|
410
424
|
|
|
@@ -315,6 +315,26 @@ describe('cliDocSection', () => {
|
|
|
315
315
|
]);
|
|
316
316
|
});
|
|
317
317
|
|
|
318
|
+
it('marks required parameters and adds a Default column when a parameter has a default', () => {
|
|
319
|
+
const beta = fn('beta', {
|
|
320
|
+
params: [
|
|
321
|
+
{name: 'slug', type: 'string', description: 'The slug.', required: true},
|
|
322
|
+
{name: 'options.limit', type: 'number', description: 'How many.', default: '20'},
|
|
323
|
+
],
|
|
324
|
+
});
|
|
325
|
+
const table = cliDocSection(beta, indexOf([beta])).content.find(
|
|
326
|
+
(/** @type {any} */ block) => block.type === 'table',
|
|
327
|
+
);
|
|
328
|
+
expect(table).toEqual({
|
|
329
|
+
type: 'table',
|
|
330
|
+
headers: ['Parameter', 'Type', 'Default', 'Description'],
|
|
331
|
+
rows: [
|
|
332
|
+
['`slug` (required)', '`string`', '', 'The slug.'],
|
|
333
|
+
['`options.limit`', '`number`', '20', 'How many.'],
|
|
334
|
+
],
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
|
|
318
338
|
it('renders an API function, an enum, and a schema', () => {
|
|
319
339
|
const alpha = fn('alpha', {
|
|
320
340
|
description: 'Longer.',
|
|
@@ -626,7 +626,11 @@ function findMergeTarget(sections, section) {
|
|
|
626
626
|
const sameTitle = sections.findIndex(
|
|
627
627
|
candidate => sourceTitle(candidate) === title,
|
|
628
628
|
);
|
|
629
|
-
return sameTitle;
|
|
629
|
+
if (sameTitle !== -1) return sameTitle;
|
|
630
|
+
// A base section retitled later keeps its old key as its `id`, so an
|
|
631
|
+
// extension that still names it by the old title finds it by that id. A
|
|
632
|
+
// title variant of a section with no id stays a separate section.
|
|
633
|
+
return sections.findIndex(candidate => candidate.id === key);
|
|
630
634
|
}
|
|
631
635
|
|
|
632
636
|
/**
|
|
@@ -509,6 +509,27 @@ describe('mergeTopic', () => {
|
|
|
509
509
|
expect(base.sections[0].content[0].text).toBe('npm i');
|
|
510
510
|
});
|
|
511
511
|
|
|
512
|
+
it('finds a retitled base section by the key its old title derives', () => {
|
|
513
|
+
// A base section retitled later keeps its old key as its `id`; an
|
|
514
|
+
// extension that still names it by the old title replaces it.
|
|
515
|
+
const merged = mergeTopic(
|
|
516
|
+
{
|
|
517
|
+
...base,
|
|
518
|
+
sections: [
|
|
519
|
+
{id: 'quick-start', title: 'Wrap your app in a theme', content: []},
|
|
520
|
+
{id: 'tokens', title: 'Tokens', content: []},
|
|
521
|
+
],
|
|
522
|
+
},
|
|
523
|
+
{sections: [{title: 'Quick Start', content: []}]},
|
|
524
|
+
);
|
|
525
|
+
expect(merged.sections.map(section => [section.id, section.title])).toEqual(
|
|
526
|
+
[
|
|
527
|
+
['quick-start', 'Quick Start'],
|
|
528
|
+
['tokens', 'Tokens'],
|
|
529
|
+
],
|
|
530
|
+
);
|
|
531
|
+
});
|
|
532
|
+
|
|
512
533
|
it('replaces a section by stable ID even when its title changes', () => {
|
|
513
534
|
const merged = mergeTopic(
|
|
514
535
|
{
|
|
@@ -32,7 +32,7 @@ export function sectionTitleKey(title: unknown): string;
|
|
|
32
32
|
export function sectionKey(section: any): string;
|
|
33
33
|
/**
|
|
34
34
|
* A name as a docs-tree route segment: lowercase words joined by hyphens, so
|
|
35
|
-
* `integrationPackCheck` and `integration
|
|
35
|
+
* `integrationPackCheck` and `integration verify` both read naturally.
|
|
36
36
|
* @param {string} name
|
|
37
37
|
* @returns {string}
|
|
38
38
|
*/
|
|
@@ -95,7 +95,7 @@ function legacyTitleMatchIndex(sections, query) {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* A name as a docs-tree route segment: lowercase words joined by hyphens, so
|
|
98
|
-
* `integrationPackCheck` and `integration
|
|
98
|
+
* `integrationPackCheck` and `integration verify` both read naturally.
|
|
99
99
|
* @param {string} name
|
|
100
100
|
* @returns {string}
|
|
101
101
|
*/
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @file Shared template discovery + IO.
|
|
5
5
|
*
|
|
6
6
|
* Owns everything the template leaves (list/show/skeleton/copy) AND other
|
|
7
|
-
* commands (component, search, init, discover, Doctor integration)
|
|
7
|
+
* commands (component, layout, search, init, discover, Doctor integration)
|
|
8
8
|
* share: template discovery across core/external/integration sources, the
|
|
9
9
|
* template-spec loaders, and the cross-command helpers (stripTemplateAssetRefs,
|
|
10
10
|
* findShowcase, findRelatedBlocks, extractComponents, listTemplates). The
|