@astryxdesign/cli 0.6.4 → 0.6.5-canary.031021b
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/CHANGELOG.md +56 -0
- package/README.md +97 -90
- 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/_adapter.d.mts +25 -0
- package/api/component/_adapter.mjs +59 -5
- package/api/component/component.d.mts +6 -3
- package/api/component/component.doc.mjs +37 -17
- package/api/component/component.mjs +249 -9
- package/api/component/component.type.d.mts +25 -0
- package/api/component/component.type.mjs +44 -0
- package/api/discover/_adapter.d.mts +114 -6
- package/api/discover/_adapter.mjs +372 -17
- package/api/discover/_adapter.test.mjs +215 -0
- package/api/discover/_catalog-view.d.mts +115 -0
- package/api/discover/_catalog-view.mjs +203 -0
- package/api/discover/_catalog-view.test.mjs +128 -0
- package/api/discover/detail/detail.d.mts +18 -6
- package/api/discover/detail/detail.mjs +67 -13
- package/api/discover/detail/detail.test.mjs +85 -0
- package/api/discover/detail/item/item.d.mts +26 -0
- package/api/discover/detail/item/item.mjs +78 -0
- package/api/discover/detail/item/item.test.mjs +73 -0
- package/api/discover/discover.d.mts +3 -9
- package/api/discover/discover.doc.mjs +61 -18
- package/api/discover/discover.mjs +220 -36
- package/api/discover/discover.test.mjs +11 -2
- package/api/discover/discover.type.d.mts +147 -8
- package/api/discover/discover.type.mjs +102 -12
- package/api/discover/list/list.d.mts +20 -6
- package/api/discover/list/list.mjs +45 -12
- package/api/discover/list/list.test.mjs +46 -0
- package/api/discover/search/search.d.mts +18 -16
- package/api/discover/search/search.mjs +102 -56
- package/api/discover/search/search.test.mjs +144 -10
- 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.d.mts +8 -3
- package/api/doctor/doctor.doc.mjs +17 -8
- package/api/doctor/doctor.mjs +90 -9
- package/api/doctor/doctor.test.mjs +122 -10
- 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 +1 -0
- package/api/index.mjs +5 -3
- package/api/init/init.doc.mjs +17 -12
- package/api/integration/add-helpers.d.mts +5 -2
- package/api/integration/add-helpers.mjs +36 -9
- 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 +107 -0
- package/api/integration/pack-check.mjs +82 -9
- 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/isError.doc.mjs +1 -1
- 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.doc.mjs +2 -1
- package/api/theme/add/add.mjs +17 -25
- package/api/theme/add/add.rollback.test.mjs +158 -0
- package/api/theme/add/add.staging.test.mjs +40 -23
- package/api/theme/build/build.family.test.mjs +7 -12
- package/api/theme/build/build.mjs +8 -18
- package/api/theme/build/build.rollback.test.mjs +148 -0
- 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/files-changed.test.mjs +111 -0
- package/api/upgrade/run/run.mjs +5 -3
- package/api/upgrade/upgrade.doc.mjs +24 -22
- package/api/upgrade/upgrade.type.mjs +2 -2
- package/assets/codemods/__tests__/runner.test.mjs +3 -1
- package/assets/codemods/file-count.test.mjs +163 -0
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +5 -4
- 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/internationalization.doc.mjs +7 -5
- 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 +57 -47
- 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/component-lookups.doc.mjs +149 -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 -451
- 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/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/authoring/config/config.doc.mjs +9 -1
- package/authoring/config/parse.d.mts +2 -0
- package/authoring/config/parse.mjs +19 -0
- package/authoring/config/parse.test.mjs +8 -0
- package/authoring/config/type.ts +11 -0
- package/authoring/discover/discover.doc.d.mts +13 -0
- package/authoring/discover/discover.doc.mjs +138 -0
- package/authoring/discover/parse.d.mts +24 -0
- package/authoring/discover/parse.mjs +128 -0
- package/authoring/discover/parse.test.mjs +124 -0
- package/authoring/discover/type.ts +87 -0
- 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/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/index.d.mts +1 -0
- package/authoring/index.d.ts +10 -0
- package/authoring/index.mjs +1 -0
- package/authoring/integration/integration.doc.mjs +12 -10
- package/clients/cli/commands/component/index.mjs +152 -55
- package/clients/cli/commands/component-batch.test.mjs +341 -0
- package/clients/cli/commands/component-ownership.test.mjs +89 -0
- package/clients/cli/commands/component.doc.mjs +27 -9
- package/clients/cli/commands/discover.doc.mjs +53 -9
- package/clients/cli/commands/discover.mjs +393 -118
- package/clients/cli/commands/discover.sources.test.mjs +267 -0
- 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/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 +7 -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 +28 -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 +17 -1
- package/clients/cli/lib/json-shim.mjs +24 -14
- package/clients/cli/lib/manifest.mjs +18 -5
- package/clients/cli/lib/manifest.test.mjs +5 -2
- package/clients/cli/lib/parse-error-format.test.mjs +81 -0
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/discovery/authoring-self-docs.mjs +1 -0
- 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/doc-compiler/doc-loads.test.mjs +3 -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/integrations/integrations.d.mts +14 -1
- package/foundation/integrations/integrations.mjs +41 -1
- package/foundation/integrations/integrations.test.mjs +31 -0
- package/foundation/response/batch.type.d.mts +33 -0
- package/foundation/response/batch.type.mjs +34 -0
- 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 +40 -10
- package/foundation/response/response-types.doc.test.mjs +23 -0
- package/foundation/response/response.doc.mjs +11 -10
- package/package.json +9 -9
- package/api/docs/docs.test.mjs +0 -243
- package/api/docs/integration-tree.test.mjs +0 -555
- package/api/docs/integrationDocs.test.mjs +0 -314
- package/api/search/search.test.mjs +0 -512
- 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 -294
- package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
- package/foundation/doc-compiler/tree.test.mjs +0 -598
|
@@ -35,12 +35,13 @@ export const doc = {
|
|
|
35
35
|
{
|
|
36
36
|
flag: '--preview <path>',
|
|
37
37
|
param: 'options.preview',
|
|
38
|
-
description: 'Write a
|
|
38
|
+
description: 'Write a self-contained HTML preview page; the path must end in .html',
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
flag: '-f, --overwrite',
|
|
42
42
|
param: 'options.overwrite',
|
|
43
|
-
description:
|
|
43
|
+
description:
|
|
44
|
+
'Replace existing candidate, receipt, and preview files. Without it, if any of them exists, nothing is written',
|
|
44
45
|
},
|
|
45
46
|
],
|
|
46
47
|
examples: [
|
|
@@ -8,8 +8,7 @@ export const doc = {
|
|
|
8
8
|
namespace: 'cli/commands',
|
|
9
9
|
summary: 'Create and work with theme-owned color palettes',
|
|
10
10
|
description:
|
|
11
|
-
'Palette authoring tools.
|
|
12
|
-
'Palette inspection and diagnostic commands are intentionally deferred to follow-up work.',
|
|
11
|
+
'Palette authoring tools. generate writes a palette candidate for you to review before a theme uses it.',
|
|
13
12
|
subcommands: ['generate'],
|
|
14
13
|
examples: [
|
|
15
14
|
{
|
|
@@ -20,8 +20,8 @@ export const doc = {
|
|
|
20
20
|
'the component that declares it, and the props and states that are legal override keys ' +
|
|
21
21
|
'under it. This is the whole themeable surface in one command: what auditing a theme, or ' +
|
|
22
22
|
'answering "which key paints this pixel?", used to need one `astryx component <Name>` per ' +
|
|
23
|
-
'component to assemble. Pass a component name to scope it; pass any
|
|
24
|
-
'keys. `--json` for a list a repo can lint its own theme against.',
|
|
23
|
+
'component to assemble. Pass a component name to scope it; pass any other text to search ' +
|
|
24
|
+
'target keys, classes, and components. `--json` for a list a repo can lint its own theme against.',
|
|
25
25
|
fn: 'themeTargets',
|
|
26
26
|
args: [{name: 'filter', param: 'filter', required: false}],
|
|
27
27
|
examples: [
|
|
@@ -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',
|
|
@@ -342,16 +343,24 @@ export async function createProgram() {
|
|
|
342
343
|
.name('astryx')
|
|
343
344
|
.description('Design system CLI — components, themes, and tooling')
|
|
344
345
|
.version(pkg.version)
|
|
345
|
-
|
|
346
|
-
|
|
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')
|
|
347
353
|
.addOption(
|
|
348
354
|
new Option(
|
|
349
355
|
'--lang <locale>',
|
|
350
|
-
'
|
|
356
|
+
'Language or format for component and docs reads: en (default), zh (as --zh), or dense (as --dense)',
|
|
351
357
|
).choices(['en', 'zh', 'dense']),
|
|
352
358
|
)
|
|
353
359
|
.addOption(
|
|
354
|
-
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
|
+
)
|
|
355
364
|
.choices(['full', 'compact', 'brief'])
|
|
356
365
|
.default('full'),
|
|
357
366
|
)
|
|
@@ -471,6 +480,19 @@ export async function createProgram() {
|
|
|
471
480
|
const fullName = fullCommandName(actionCommand, program);
|
|
472
481
|
if (JSON_SUPPORTED.has(fullName)) return;
|
|
473
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
|
+
}
|
|
474
496
|
debug.setOutcome('rejected', {
|
|
475
497
|
exitCode: 1,
|
|
476
498
|
code: ERROR_CODES.ERR_INVALID_OPTION,
|
|
@@ -605,7 +627,7 @@ export async function createProgram() {
|
|
|
605
627
|
text(`Run \`${getCliInvocation()} manifest --json\` for the full structured manifest.`),
|
|
606
628
|
);
|
|
607
629
|
});
|
|
608
|
-
|
|
630
|
+
addDocHelp(manifestCommand, manifestDoc);
|
|
609
631
|
markReportsResult(manifestCommand);
|
|
610
632
|
|
|
611
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
|
});
|
|
@@ -41,7 +41,7 @@ describe('command exit codes', () => {
|
|
|
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,6 +51,22 @@ 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
|
|
|
@@ -33,10 +33,12 @@
|
|
|
33
33
|
* shows help because the invocation failed (`help <unknown>`, or a
|
|
34
34
|
* command group with no subcommand), which exits 1.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* Commander writes its own "error: ..." line via configureOutput.writeErr.
|
|
37
|
+
* The shim drops that line in both modes. Under --json the error envelope
|
|
38
|
+
* replaces it; in text mode `handleCommanderError` writes the Astryx line
|
|
39
|
+
* instead (`Error: <message>`, the same message the envelope carries), so
|
|
40
|
+
* a parse failure reads like every other CLI error. Other stderr output,
|
|
41
|
+
* such as help printed as the failure report, still passes through.
|
|
40
42
|
*/
|
|
41
43
|
|
|
42
44
|
import {API_VERSION, isJsonMode, toErrorEnvelope} from '../../../foundation/response/json.mjs';
|
|
@@ -249,11 +251,20 @@ function applyShimRecursively(cmd) {
|
|
|
249
251
|
});
|
|
250
252
|
cmd.configureOutput({
|
|
251
253
|
writeOut: (str) => process.stdout.write(str),
|
|
254
|
+
// Commander's own "error: ..." line never reaches the user. Under --json a
|
|
255
|
+
// consumer parsing both streams must not see it alongside the envelope;
|
|
256
|
+
// in text mode it is Commander's format, not Astryx's, so an invalid
|
|
257
|
+
// global option (`--lang zh-Hans`) printed `error: option '--lang
|
|
258
|
+
// <locale>' argument 'zh-Hans' is invalid…` where every other CLI error
|
|
259
|
+
// prints `Error: …`. handleCommanderError writes the Astryx line below,
|
|
260
|
+
// for both modes, from the same message.
|
|
261
|
+
//
|
|
262
|
+
// ONLY that line. Commander also writes HELP through this channel when it
|
|
263
|
+
// shows help because the invocation failed (a command group with no
|
|
264
|
+
// subcommand), and that output is still wanted in text mode.
|
|
252
265
|
writeErr: (str) => {
|
|
253
|
-
// Suppress Commander's "error: ..." stderr line when --json is
|
|
254
|
-
// active, so a JSON consumer parsing both streams doesn't see
|
|
255
|
-
// it alongside the envelope. Non-JSON callers are unaffected.
|
|
256
266
|
if (jsonActive()) return;
|
|
267
|
+
if (/^error:\s/i.test(str)) return;
|
|
257
268
|
process.stderr.write(str);
|
|
258
269
|
},
|
|
259
270
|
});
|
|
@@ -432,16 +443,15 @@ export function handleCommanderError(err) {
|
|
|
432
443
|
code: commanderCodeToErrorCode(code, message),
|
|
433
444
|
});
|
|
434
445
|
|
|
435
|
-
// Real error paths.
|
|
446
|
+
// Real error paths. Strip Commander's "error: " prefix once: in the envelope
|
|
447
|
+
// the key is already `error`, and in text mode the Astryx prefix replaces it.
|
|
448
|
+
const cleaned = message.replace(/^error:\s*/i, '');
|
|
436
449
|
if (jsonActive()) {
|
|
437
|
-
// Strip Commander's "error: " prefix — the envelope key is `error`
|
|
438
|
-
// already, doubled "error" is noise.
|
|
439
|
-
const cleaned = message.replace(/^error:\s*/i, '');
|
|
440
450
|
emitJsonError(cleaned, undefined, commanderCodeToErrorCode(code, cleaned));
|
|
441
451
|
} else {
|
|
442
|
-
//
|
|
443
|
-
//
|
|
444
|
-
|
|
452
|
+
// Commander's own line was suppressed above, so a parse failure reads the
|
|
453
|
+
// same as every other CLI error — the `Error: …` line cliError prints.
|
|
454
|
+
process.stderr.write(`Error: ${cleaned}\n`);
|
|
445
455
|
}
|
|
446
456
|
process.exit(exitCode || 1);
|
|
447
457
|
}
|
|
@@ -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
|
|
@@ -51,6 +52,7 @@ export const RESPONSE_TYPES = {
|
|
|
51
52
|
init: ['init.run', 'init.remove'],
|
|
52
53
|
component: [
|
|
53
54
|
'component.list',
|
|
55
|
+
'component.batch',
|
|
54
56
|
'component.detail',
|
|
55
57
|
'component.detail.props',
|
|
56
58
|
'component.detail.source',
|
|
@@ -69,6 +71,7 @@ export const RESPONSE_TYPES = {
|
|
|
69
71
|
'discover.list',
|
|
70
72
|
'discover.detail',
|
|
71
73
|
'discover.detail.doc',
|
|
74
|
+
'discover.item',
|
|
72
75
|
'discover.search',
|
|
73
76
|
],
|
|
74
77
|
search: ['search'],
|
|
@@ -90,8 +93,9 @@ export const RESPONSE_TYPES = {
|
|
|
90
93
|
'theme targets': ['theme.targets'],
|
|
91
94
|
'theme palette generate': ['theme.palette.generate'],
|
|
92
95
|
'integration add': ['integration.add'],
|
|
96
|
+
'integration verify': ['integration.pack-check'],
|
|
93
97
|
'integration pack': ['integration.pack-check'],
|
|
94
|
-
upgrade: ['upgrade.list', 'upgrade.status', 'upgrade.run'],
|
|
98
|
+
upgrade: ['upgrade.list', 'upgrade.registry', 'upgrade.status', 'upgrade.run'],
|
|
95
99
|
manifest: ['manifest'],
|
|
96
100
|
doctor: ['doctor'],
|
|
97
101
|
'doctor integration validate': ['integration.validate'],
|
|
@@ -103,6 +107,14 @@ export const RESPONSE_TYPES = {
|
|
|
103
107
|
'layout grammar': ['layout.grammar'],
|
|
104
108
|
};
|
|
105
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
|
+
|
|
106
118
|
/**
|
|
107
119
|
* Example invocations per fully-qualified command. Optional, agent-facing.
|
|
108
120
|
* @type {Record<string, string[]>}
|
|
@@ -111,6 +123,7 @@ const EXAMPLES = {
|
|
|
111
123
|
component: [
|
|
112
124
|
'astryx component',
|
|
113
125
|
'astryx component XDSButton',
|
|
126
|
+
'astryx component Button Badge Text',
|
|
114
127
|
'astryx component XDSButton --props --json',
|
|
115
128
|
],
|
|
116
129
|
docs: [
|
|
@@ -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'],
|
|
@@ -156,7 +156,10 @@ describe('manifest: shape', () => {
|
|
|
156
156
|
|
|
157
157
|
it('derives arguments from Commander metadata', () => {
|
|
158
158
|
const component = allEntries.find((c) => c.name === 'component');
|
|
159
|
-
|
|
159
|
+
const names = component.arguments.find((a) => a.name === 'names');
|
|
160
|
+
expect(names.required).toBe(false);
|
|
161
|
+
expect(names.variadic).toBe(true);
|
|
162
|
+
expect(names.description).toContain('Two or more return one ordered batch');
|
|
160
163
|
const themeBuild = allEntries.find((c) => c.name === 'theme build');
|
|
161
164
|
expect(themeBuild.arguments.map((a) => a.name)).toContain('files');
|
|
162
165
|
const files = themeBuild.arguments.find((a) => a.name === 'files');
|
|
@@ -193,7 +196,7 @@ describe('manifest: e2e', () => {
|
|
|
193
196
|
// Enriched: the full structured manifest is embedded.
|
|
194
197
|
expect(parsed.data.manifest).toBeDefined();
|
|
195
198
|
expect(parsed.data.manifest.commands.find((c) => c.name === 'component').responseTypes)
|
|
196
|
-
.
|
|
199
|
+
.toEqual(expect.arrayContaining(['component.list', 'component.batch']));
|
|
197
200
|
});
|
|
198
201
|
});
|
|
199
202
|
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file A parse failure reads like every other Astryx error in text mode.
|
|
5
|
+
*
|
|
6
|
+
* `astryx theme list --lang zh-Hans` printed Commander's own line —
|
|
7
|
+
* `error: option '--lang <locale>' argument 'zh-Hans' is invalid…` — while
|
|
8
|
+
* every other CLI error prints `Error: …`. `--json` was already correct
|
|
9
|
+
* (ERR_INVALID_LANG), so text and JSON disagreed on everything except the exit
|
|
10
|
+
* code. Commander writes that line before any Astryx code runs, so the shim is
|
|
11
|
+
* the only place that can speak for it.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {describe, it, expect} from 'vitest';
|
|
15
|
+
import {Command} from 'commander';
|
|
16
|
+
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
17
|
+
import {installJsonShim} from './json-shim.mjs';
|
|
18
|
+
|
|
19
|
+
describe('a parse error prints the Astryx error format', () => {
|
|
20
|
+
it.each([
|
|
21
|
+
['invalid --lang value', ['theme', 'list', '--lang', 'zh-Hans'], 'ERR_INVALID_LANG'],
|
|
22
|
+
['invalid --detail value', ['theme', 'list', '--detail', 'nope'], 'ERR_INVALID_DETAIL'],
|
|
23
|
+
['unknown option', ['component', '--bogus-flag'], 'ERR_INVALID_OPTION'],
|
|
24
|
+
['missing argument', ['theme', 'build'], 'ERR_MISSING_ARGUMENT'],
|
|
25
|
+
])('%s', async (_label, args, code) => {
|
|
26
|
+
const human = await runCli(args);
|
|
27
|
+
|
|
28
|
+
expect(human.status).toBe(1);
|
|
29
|
+
expect(human.stderr).toContain('Error: ');
|
|
30
|
+
// Commander's own lowercase line must not reach the user.
|
|
31
|
+
expect(human.stderr).not.toMatch(/(^|\n)error: /);
|
|
32
|
+
|
|
33
|
+
// --json is unchanged, and the two modes agree on the exit code.
|
|
34
|
+
const json = await runCli(['--json', ...args]);
|
|
35
|
+
expect(json.status).toBe(human.status);
|
|
36
|
+
expect(JSON.parse(json.stdout).code).toBe(code);
|
|
37
|
+
expect(json.stderr).toBe('');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('carries Commander\'s explanation, not just a generic line', async () => {
|
|
41
|
+
const {stderr} = await runCli(['theme', 'list', '--lang', 'zh-Hans']);
|
|
42
|
+
expect(stderr).toContain("'zh-Hans' is invalid");
|
|
43
|
+
expect(stderr).toContain('en, zh, dense');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('leaves --help and --version at exit 0 with nothing on stderr', async () => {
|
|
47
|
+
for (const args of [['--help'], ['theme', '--help']]) {
|
|
48
|
+
const r = await runCli(args);
|
|
49
|
+
expect(r.status).toBe(0);
|
|
50
|
+
expect(r.stderr).toBe('');
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Commander writes help through the same stderr channel when it shows help
|
|
55
|
+
// BECAUSE the invocation failed, so suppressing that channel wholesale would
|
|
56
|
+
// have taken the help with it. Driven on a throwaway program rather than a
|
|
57
|
+
// real command, so the guard outlives whichever command happens to have a
|
|
58
|
+
// subcommand group today.
|
|
59
|
+
it('still lets help reach stderr when help IS the failure report', () => {
|
|
60
|
+
const program = new Command('probe');
|
|
61
|
+
const group = program.command('group');
|
|
62
|
+
group.command('leaf').action(() => {});
|
|
63
|
+
installJsonShim(program);
|
|
64
|
+
|
|
65
|
+
/** @type {string[]} */
|
|
66
|
+
const written = [];
|
|
67
|
+
const original = process.stderr.write;
|
|
68
|
+
// @ts-expect-error test double for the write signature
|
|
69
|
+
process.stderr.write = str => {
|
|
70
|
+
written.push(String(str));
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
try {
|
|
74
|
+
group.outputHelp({error: true});
|
|
75
|
+
} finally {
|
|
76
|
+
process.stderr.write = original;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
expect(written.join('')).toMatch(/Usage: probe group/);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -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,
|