@astryxdesign/cli 0.6.4-canary.f0355e3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +96 -99
- package/api/build/build.doc.mjs +1 -6
- package/api/build/build.test.mjs +0 -22
- package/api/build/kit/kit.mjs +5 -44
- package/api/component/_adapter.d.mts +0 -25
- package/api/component/_adapter.mjs +5 -59
- package/api/component/component.d.mts +3 -6
- package/api/component/component.doc.mjs +17 -37
- package/api/component/component.mjs +9 -249
- package/api/component/component.type.d.mts +0 -25
- package/api/component/component.type.mjs +0 -44
- package/api/discover/_adapter.d.mts +6 -114
- package/api/discover/_adapter.mjs +17 -372
- package/api/discover/detail/detail.d.mts +6 -18
- package/api/discover/detail/detail.mjs +13 -67
- package/api/discover/detail/detail.test.mjs +0 -85
- package/api/discover/discover.d.mts +9 -3
- package/api/discover/discover.doc.mjs +18 -61
- package/api/discover/discover.mjs +36 -220
- package/api/discover/discover.test.mjs +2 -11
- package/api/discover/discover.type.d.mts +8 -147
- package/api/discover/discover.type.mjs +12 -102
- package/api/discover/list/list.d.mts +6 -20
- package/api/discover/list/list.mjs +12 -45
- package/api/discover/list/list.test.mjs +0 -46
- package/api/discover/search/search.d.mts +16 -18
- package/api/discover/search/search.mjs +56 -102
- package/api/discover/search/search.test.mjs +10 -144
- package/api/docs/_adapter.d.mts +3 -8
- package/api/docs/_adapter.mjs +6 -14
- package/api/docs/docOverlays.test.mjs +1 -27
- package/api/docs/docs.doc.mjs +2 -2
- package/api/docs/docs.test.mjs +243 -0
- package/api/docs/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +314 -0
- package/api/doctor/doctor.d.mts +3 -8
- package/api/doctor/doctor.doc.mjs +8 -17
- package/api/doctor/doctor.mjs +9 -90
- package/api/doctor/doctor.test.mjs +10 -122
- package/api/doctor/doctor.type.d.mts +1 -1
- package/api/doctor/doctor.type.mjs +1 -1
- package/api/gap-report/gap-report.doc.mjs +10 -19
- package/api/hook/hook.doc.mjs +3 -6
- package/api/index.d.mts +2 -1
- package/api/index.mjs +5 -5
- package/api/init/init.doc.mjs +12 -17
- package/api/integration/add-helpers.d.mts +2 -5
- package/api/integration/add-helpers.mjs +9 -36
- package/api/integration/add-theme.mjs +1 -22
- package/api/integration/add-theme.test.mjs +0 -34
- package/api/integration/authoring-checks.mjs +2 -2
- package/api/integration/integrationPackCheck.doc.mjs +3 -3
- package/api/integration/pack-check.mjs +9 -82
- package/api/integration/pack-check.test.mjs +0 -90
- package/api/integration/pack-check.type.mjs +1 -1
- package/api/json/assertResponse.doc.mjs +1 -1
- package/api/json/index.ts +1 -0
- package/api/json/isError.doc.mjs +1 -1
- package/api/layout/_adapter.d.mts +34 -0
- package/api/layout/_adapter.mjs +148 -0
- package/api/layout/check/check.d.mts +16 -0
- package/api/layout/check/check.mjs +40 -0
- package/api/layout/expand/expand.d.mts +22 -0
- package/api/layout/expand/expand.mjs +155 -0
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.d.mts +13 -0
- package/api/layout/grammar/grammar.mjs +87 -0
- package/api/layout/layout.d.mts +6 -0
- package/api/layout/layout.mjs +17 -0
- package/api/layout/layout.test.mjs +297 -0
- package/api/layout/layout.type.d.mts +89 -0
- package/api/layout/layout.type.mjs +103 -0
- package/api/layout/layoutCheck.doc.d.mts +11 -0
- package/api/layout/layoutCheck.doc.mjs +85 -0
- package/api/layout/layoutExpand.doc.d.mts +11 -0
- package/api/layout/layoutExpand.doc.mjs +107 -0
- package/api/layout/layoutGrammar.doc.d.mts +11 -0
- package/api/layout/layoutGrammar.doc.mjs +57 -0
- package/api/search/search.d.mts +1 -27
- package/api/search/search.doc.mjs +2 -2
- package/api/search/search.mjs +16 -228
- package/api/search/search.test.mjs +512 -0
- package/api/swizzle/swizzle.doc.mjs +5 -7
- package/api/template/copy/copy.mjs +1 -1
- package/api/template/copy/copy.test.mjs +0 -9
- package/api/template/template-integration.test.mjs +65 -1
- package/api/template/template.doc.mjs +1 -2
- package/api/template/template.mjs +1 -1
- package/api/theme/add/add.mjs +25 -17
- package/api/theme/add/add.staging.test.mjs +23 -40
- package/api/theme/build/build.family.test.mjs +12 -7
- package/api/theme/build/build.mjs +18 -8
- package/api/theme/generateTonalPalette.doc.mjs +2 -1
- package/api/theme/listThemes.doc.mjs +1 -1
- package/api/theme/themeAdd.doc.mjs +10 -9
- package/api/theme/themeBuild.doc.mjs +13 -13
- package/api/theme/themeList.doc.mjs +1 -1
- package/api/theme/themeListAvailable.doc.mjs +1 -2
- package/api/theme/themePaletteGenerate.doc.mjs +8 -15
- package/api/theme/themeTargets.doc.mjs +2 -3
- package/api/theme/themeTemplate.doc.mjs +1 -2
- package/api/upgrade/run/run.mjs +4 -6
- package/api/upgrade/upgrade.doc.mjs +22 -24
- package/api/upgrade/upgrade.type.mjs +2 -2
- package/assets/codemods/__tests__/runner.test.mjs +1 -3
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +4 -5
- package/assets/docs/README.md +2 -4
- package/assets/docs/browser-support.doc.mjs +11 -11
- package/assets/docs/color.doc.mjs +2 -8
- package/assets/docs/elevation.doc.mjs +4 -6
- package/assets/docs/getting-started.doc.mjs +16 -5
- package/assets/docs/icons.doc.mjs +21 -2
- package/assets/docs/illustrations.doc.mjs +15 -7
- package/assets/docs/internationalization.doc.mjs +5 -7
- package/assets/docs/layout.doc.dense.mjs +82 -130
- package/assets/docs/layout.doc.mjs +77 -133
- package/assets/docs/migration.doc.mjs +21 -19
- package/assets/docs/motion.doc.mjs +3 -16
- package/assets/docs/principles.doc.dense.mjs +5 -5
- package/assets/docs/principles.doc.mjs +0 -8
- package/assets/docs/principles.doc.zh.mjs +6 -6
- package/assets/docs/shape.doc.mjs +3 -8
- package/assets/docs/spacing.doc.mjs +2 -7
- package/assets/docs/styling-libraries.doc.mjs +2 -6
- package/assets/docs/styling.doc.mjs +23 -19
- package/assets/docs/theme.doc.dense.mjs +18 -58
- package/assets/docs/theme.doc.mjs +46 -56
- package/assets/docs/theme.doc.zh.mjs +8 -9
- package/assets/docs/tokens.doc.dense.mjs +2 -2
- package/assets/docs/tokens.doc.mjs +8 -389
- package/assets/docs/tokens.doc.zh.mjs +2 -2
- package/assets/docs/tree/integrations.doc.mjs +451 -25
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/typography.doc.mjs +4 -24
- package/assets/docs/working-with-ai.doc.mjs +22 -30
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/authoring/config/config.doc.mjs +2 -10
- package/authoring/config/parse.d.mts +0 -2
- package/authoring/config/parse.mjs +0 -19
- package/authoring/config/parse.test.mjs +0 -8
- package/authoring/config/type.ts +2 -13
- package/authoring/doctypes/_schema.d.mts +2 -3
- package/authoring/doctypes/_schema.mjs +0 -6
- package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
- package/authoring/doctypes/base/type.ts +2 -4
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +1 -1
- package/authoring/doctypes/component/component.doc.mjs +0 -6
- package/authoring/doctypes/component/type.ts +0 -8
- package/authoring/doctypes/reference/reference.doc.mjs +0 -7
- package/authoring/doctypes/reference/type.ts +0 -5
- package/authoring/doctypes/schema/schema.doc.mjs +2 -2
- package/authoring/doctypes/template/template.doc.mjs +1 -1
- package/authoring/doctypes/template/type.ts +2 -2
- package/authoring/index.d.mts +0 -1
- package/authoring/index.d.ts +0 -10
- package/authoring/index.mjs +0 -1
- package/authoring/integration/integration.doc.mjs +10 -12
- package/clients/cli/command-result-coverage.test.mjs +7 -7
- package/clients/cli/commands/component/index.mjs +55 -152
- package/clients/cli/commands/component-ownership.test.mjs +0 -89
- package/clients/cli/commands/component.doc.mjs +9 -27
- package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
- package/clients/cli/commands/discover.doc.mjs +9 -53
- package/clients/cli/commands/discover.mjs +118 -393
- package/clients/cli/commands/docs.doc.mjs +1 -1
- package/clients/cli/commands/docs.mjs +17 -60
- package/clients/cli/commands/docs.test.mjs +294 -0
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +2 -3
- package/clients/cli/commands/doctor-integration.test.mjs +0 -53
- package/clients/cli/commands/doctor.doc.mjs +1 -3
- package/clients/cli/commands/doctor.mjs +5 -49
- package/clients/cli/commands/gap-report.doc.mjs +9 -10
- package/clients/cli/commands/init.doc.mjs +6 -9
- package/clients/cli/commands/integration-add.doc.mjs +9 -9
- package/clients/cli/commands/integration-authoring.test.mjs +10 -61
- package/clients/cli/commands/integration-pack.doc.mjs +9 -5
- package/clients/cli/commands/integration-real-world.test.mjs +1 -1
- package/clients/cli/commands/integration.doc.mjs +4 -4
- package/clients/cli/commands/integration.mjs +43 -74
- package/clients/cli/commands/layout-check.doc.mjs +65 -0
- package/clients/cli/commands/layout-expand.doc.mjs +83 -0
- package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
- package/clients/cli/commands/layout.doc.mjs +34 -0
- package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
- package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
- package/clients/cli/commands/layout.mjs +275 -0
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +3 -10
- package/clients/cli/commands/search.mjs +2 -21
- package/clients/cli/commands/search.test.mjs +4 -21
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.doc.mjs +1 -1
- package/clients/cli/commands/text-json-parity.test.mjs +16 -5
- package/clients/cli/commands/theme-add.doc.mjs +1 -1
- package/clients/cli/commands/theme-palette-generate.doc.mjs +2 -3
- package/clients/cli/commands/theme-palette.doc.mjs +2 -1
- package/clients/cli/commands/theme-targets.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +1 -2
- package/clients/cli/commands/upgrade.doc.mjs +3 -62
- package/clients/cli/index.mjs +10 -28
- package/clients/cli/lib/define-command.mjs +4 -28
- package/clients/cli/lib/define-command.test.mjs +0 -54
- package/clients/cli/lib/exit-codes.test.mjs +9 -18
- package/clients/cli/lib/json-shim.mjs +14 -24
- package/clients/cli/lib/json-shim.test.mjs +20 -6
- package/clients/cli/lib/manifest.mjs +13 -18
- package/clients/cli/lib/manifest.test.mjs +2 -5
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/agent-docs/agent-docs.test.mjs +1159 -0
- package/foundation/discovery/authoring-self-docs.mjs +0 -1
- package/foundation/discovery/authoring-self-docs.test.mjs +2 -6
- package/foundation/discovery/cli-self-docs.mjs +2 -16
- package/foundation/discovery/cli-self-docs.test.mjs +0 -20
- package/foundation/discovery/docs-discovery.mjs +1 -5
- package/foundation/discovery/docs-discovery.test.mjs +0 -21
- package/foundation/discovery/docs-section-key.d.mts +1 -1
- package/foundation/discovery/docs-section-key.mjs +1 -1
- package/foundation/discovery/template-adapter.mjs +1 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +14 -3
- package/foundation/doc-compiler/tree.d.mts +0 -4
- package/foundation/doc-compiler/tree.mjs +1 -6
- package/foundation/doc-compiler/tree.test.mjs +598 -0
- package/foundation/integrations/cli-requirement.d.mts +6 -26
- package/foundation/integrations/cli-requirement.mjs +11 -46
- package/foundation/integrations/cli-requirement.test.mjs +2 -7
- package/foundation/integrations/contribution-inventory.mjs +1 -1
- package/foundation/integrations/integrations.d.mts +1 -14
- package/foundation/integrations/integrations.mjs +1 -41
- package/foundation/integrations/integrations.test.mjs +0 -31
- package/foundation/response/error-codes.doc.mjs +8 -6
- package/foundation/response/error-codes.test.mjs +5 -30
- package/foundation/response/response-types.doc.d.mts +3 -4
- package/foundation/response/response-types.doc.mjs +27 -40
- package/foundation/response/response-types.doc.test.mjs +0 -23
- package/foundation/response/response.doc.mjs +10 -11
- package/foundation/xle/browser.d.mts +3 -3
- package/foundation/xle/browser.mjs +3 -3
- package/foundation/xle/expand.mjs +2 -2
- package/foundation/xle/parse.mjs +1 -1
- package/foundation/xle/print.mjs +2 -2
- package/foundation/xle/splice.mjs +1 -1
- package/package.json +9 -9
- package/api/discover/_adapter.test.mjs +0 -215
- package/api/discover/_catalog-view.d.mts +0 -115
- package/api/discover/_catalog-view.mjs +0 -203
- package/api/discover/_catalog-view.test.mjs +0 -128
- package/api/discover/detail/item/item.d.mts +0 -26
- package/api/discover/detail/item/item.mjs +0 -78
- package/api/discover/detail/item/item.test.mjs +0 -73
- package/api/integration/pack-check.lifecycle-output.test.mjs +0 -107
- package/api/theme/add/add.rollback.test.mjs +0 -158
- package/api/theme/build/build.rollback.test.mjs +0 -148
- package/api/upgrade/run/files-changed.test.mjs +0 -111
- package/assets/codemods/file-count.test.mjs +0 -163
- package/assets/docs/tree/add-a-component.doc.mjs +0 -75
- package/assets/docs/tree/add-a-theme.doc.mjs +0 -85
- package/assets/docs/tree/add-a-topic.doc.mjs +0 -144
- package/assets/docs/tree/agent-guidance.doc.mjs +0 -138
- package/assets/docs/tree/block-template.doc.mjs +0 -130
- package/assets/docs/tree/build-the-template.doc.mjs +0 -28
- package/assets/docs/tree/building-blocks.doc.mjs +0 -46
- package/assets/docs/tree/check-your-docs.doc.mjs +0 -137
- package/assets/docs/tree/checks.doc.mjs +0 -119
- package/assets/docs/tree/codemods.doc.mjs +0 -147
- package/assets/docs/tree/component-family.doc.mjs +0 -113
- package/assets/docs/tree/component-imports.doc.mjs +0 -69
- package/assets/docs/tree/component-lookups.doc.mjs +0 -149
- package/assets/docs/tree/components.doc.mjs +0 -23
- package/assets/docs/tree/configuration.doc.mjs +0 -23
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +0 -182
- package/assets/docs/tree/define-the-theme.doc.mjs +0 -118
- package/assets/docs/tree/describe-the-component.doc.mjs +0 -57
- package/assets/docs/tree/docs.doc.mjs +0 -21
- package/assets/docs/tree/document-the-template.doc.mjs +0 -28
- package/assets/docs/tree/document-the-theme.doc.mjs +0 -68
- package/assets/docs/tree/export-template-assets.doc.mjs +0 -147
- package/assets/docs/tree/extend-or-replace.doc.mjs +0 -103
- package/assets/docs/tree/fonts-and-assets.doc.mjs +0 -106
- package/assets/docs/tree/generate-a-palette.doc.mjs +0 -66
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +0 -105
- package/assets/docs/tree/help.doc.mjs +0 -16
- package/assets/docs/tree/links.doc.mjs +0 -98
- package/assets/docs/tree/package-and-test.doc.mjs +0 -32
- package/assets/docs/tree/page-template.doc.mjs +0 -71
- package/assets/docs/tree/publishing.doc.mjs +0 -111
- package/assets/docs/tree/quick-start.doc.mjs +0 -272
- package/assets/docs/tree/replace-a-core-component.doc.mjs +0 -104
- package/assets/docs/tree/replace-a-core-template.doc.mjs +0 -172
- package/assets/docs/tree/sections-and-placement.doc.mjs +0 -108
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +0 -59
- package/assets/docs/tree/ship.doc.mjs +0 -16
- package/assets/docs/tree/short-and-findable.doc.mjs +0 -108
- package/assets/docs/tree/single-component.doc.mjs +0 -165
- package/assets/docs/tree/start-a-template.doc.mjs +0 -143
- package/assets/docs/tree/subcomponent.doc.mjs +0 -115
- package/assets/docs/tree/template-assets.doc.mjs +0 -64
- package/assets/docs/tree/template-doc-overview.doc.mjs +0 -109
- package/assets/docs/tree/template-fonts.doc.mjs +0 -102
- package/assets/docs/tree/template-grading-rubric.doc.mjs +0 -452
- package/assets/docs/tree/template-icons.doc.mjs +0 -97
- package/assets/docs/tree/template-images-media.doc.mjs +0 -127
- package/assets/docs/tree/template-styles.doc.mjs +0 -93
- package/assets/docs/tree/templates.doc.mjs +0 -34
- package/assets/docs/tree/test-in-an-app.doc.mjs +0 -115
- package/assets/docs/tree/test-template-in-app.doc.mjs +0 -128
- package/assets/docs/tree/themes.doc.mjs +0 -39
- package/assets/docs/tree/troubleshooting.doc.mjs +0 -149
- package/assets/docs/tree/upgrading.doc.mjs +0 -103
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +0 -51
- package/assets/docs/tree/verify-packed-template.doc.mjs +0 -77
- package/assets/docs/tree/versioning.doc.mjs +0 -161
- package/assets/docs/tree/write-good-templates.doc.mjs +0 -64
- package/assets/docs/tree/write-the-template-file.doc.mjs +0 -154
- package/authoring/discover/discover.doc.d.mts +0 -13
- package/authoring/discover/discover.doc.mjs +0 -138
- package/authoring/discover/parse.d.mts +0 -24
- package/authoring/discover/parse.mjs +0 -128
- package/authoring/discover/parse.test.mjs +0 -124
- package/authoring/discover/type.ts +0 -87
- package/clients/cli/commands/component-batch.test.mjs +0 -341
- package/clients/cli/commands/discover.sources.test.mjs +0 -267
- package/clients/cli/commands/integration-verify.doc.mjs +0 -22
- package/clients/cli/lib/parse-error-format.test.mjs +0 -81
- package/foundation/response/batch.type.d.mts +0 -33
- package/foundation/response/batch.type.mjs +0 -34
|
@@ -191,18 +191,6 @@ describe('search CLI — exit codes + JSON contract', () => {
|
|
|
191
191
|
expect(r.stdout).toContain('No results');
|
|
192
192
|
});
|
|
193
193
|
|
|
194
|
-
it('takes every word after `search` as one query', async () => {
|
|
195
|
-
// Commander took only the first word, so `search dark mode` searched for
|
|
196
|
-
// "dark" and dropped "mode" without a word.
|
|
197
|
-
const json = await runCli(['--json', 'search', 'dark', 'mode', '--type', 'doc'], REPO_ROOT);
|
|
198
|
-
expect(json.status).toBe(0);
|
|
199
|
-
const env = JSON.parse(json.stdout);
|
|
200
|
-
expect(env.data.query).toBe('dark mode');
|
|
201
|
-
expect(env.data.results[0]).toMatchObject({name: 'theme', section: 'light-dark-mode'});
|
|
202
|
-
const text = await runCli(['search', 'dark', 'mode', '--type', 'doc'], REPO_ROOT);
|
|
203
|
-
expect(text.stdout).toContain('Results for "dark mode"');
|
|
204
|
-
}, SCAN_TIMEOUT);
|
|
205
|
-
|
|
206
194
|
it('exits 1 for an invalid --type', async () => {
|
|
207
195
|
const r = await runCli(['search', 'x', '--type', 'bogus'], REPO_ROOT);
|
|
208
196
|
expect(r.status).toBe(1);
|
|
@@ -311,19 +299,14 @@ describe('search CLI — exit codes + JSON contract', () => {
|
|
|
311
299
|
expect(r.stdout).toContain('reason:');
|
|
312
300
|
});
|
|
313
301
|
|
|
314
|
-
it('
|
|
302
|
+
it('exits 1 with ERR_CORE_NOT_FOUND when no @astryxdesign/core is reachable', async () => {
|
|
315
303
|
const empty = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-search-cli-no-core-'));
|
|
316
304
|
try {
|
|
317
|
-
const
|
|
318
|
-
expect(open.status).toBe(0);
|
|
319
|
-
expect(JSON.parse(open.stdout).data.results[0]).toMatchObject({domain: 'doc'});
|
|
320
|
-
// The text says the search covered the docs alone.
|
|
321
|
-
const text = await runCli(['search', 'button'], empty);
|
|
322
|
-
expect(text.status).toBe(0);
|
|
323
|
-
expect(text.stdout).toContain('only the docs were searched');
|
|
324
|
-
const json = await runCli(['--json', 'search', 'button', '--type', 'component'], empty);
|
|
305
|
+
const json = await runCli(['--json', 'search', 'button'], empty);
|
|
325
306
|
expect(json.status).toBe(1);
|
|
326
307
|
expect(JSON.parse(json.stdout)).toMatchObject({code: 'ERR_CORE_NOT_FOUND'});
|
|
308
|
+
const text = await runCli(['search', 'button'], empty);
|
|
309
|
+
expect(text.status).toBe(1);
|
|
327
310
|
} finally {
|
|
328
311
|
fs.rmSync(empty, {recursive: true, force: true});
|
|
329
312
|
}
|
|
@@ -46,7 +46,7 @@ export const doc = {
|
|
|
46
46
|
],
|
|
47
47
|
examples: [
|
|
48
48
|
{label: 'List swizzlable components', cli: 'astryx swizzle --list'},
|
|
49
|
-
{label: 'Eject a component', cli: 'astryx swizzle
|
|
49
|
+
{label: 'Eject a component', cli: 'astryx swizzle XDSButton'},
|
|
50
50
|
],
|
|
51
51
|
exitCodes: [
|
|
52
52
|
{code: 0, when: 'success'},
|
|
@@ -14,7 +14,7 @@ export const doc = {
|
|
|
14
14
|
name: 'template',
|
|
15
15
|
displayName: 'astryx template',
|
|
16
16
|
namespace: 'cli/commands',
|
|
17
|
-
summary: '
|
|
17
|
+
summary: 'Inject a page or block template',
|
|
18
18
|
description:
|
|
19
19
|
'One entry point for the template family: with no name it lists the discovered ' +
|
|
20
20
|
'templates; with a name it shows the source or a layout skeleton, or scaffolds it ' +
|
|
@@ -372,17 +372,28 @@ const CASES = [
|
|
|
372
372
|
skipFieldChecks: true,
|
|
373
373
|
},
|
|
374
374
|
{
|
|
375
|
-
name: 'integration
|
|
376
|
-
args: ['integration', '
|
|
375
|
+
name: 'integration pack',
|
|
376
|
+
args: ['integration', 'pack', '--check'],
|
|
377
377
|
// Needs a packable project; pack itself may exit differently.
|
|
378
378
|
skipFieldChecks: true,
|
|
379
379
|
},
|
|
380
|
+
// ── Layout command: remove these cases when the layout command is deleted ──
|
|
380
381
|
{
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
382
|
+
name: 'layout check',
|
|
383
|
+
args: ['layout', 'check', 'Button'],
|
|
384
|
+
skipFieldChecks: true,
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: 'layout expand',
|
|
388
|
+
args: ['layout', 'expand', 'Button'],
|
|
389
|
+
skipFieldChecks: true,
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: 'layout grammar',
|
|
393
|
+
args: ['layout', 'grammar'],
|
|
384
394
|
skipFieldChecks: true,
|
|
385
395
|
},
|
|
396
|
+
// ── End layout cases ──
|
|
386
397
|
{
|
|
387
398
|
name: 'manifest',
|
|
388
399
|
args: ['manifest'],
|
|
@@ -41,7 +41,7 @@ export const doc = {
|
|
|
41
41
|
examples: [
|
|
42
42
|
{label: 'Scaffold a theme', cli: 'astryx theme add matcha'},
|
|
43
43
|
{
|
|
44
|
-
label: '
|
|
44
|
+
label: 'Select an integration theme',
|
|
45
45
|
cli: 'astryx theme add ocean --package @acme/themes',
|
|
46
46
|
},
|
|
47
47
|
],
|
|
@@ -35,13 +35,12 @@ export const doc = {
|
|
|
35
35
|
{
|
|
36
36
|
flag: '--preview <path>',
|
|
37
37
|
param: 'options.preview',
|
|
38
|
-
description: 'Write a self-contained HTML preview
|
|
38
|
+
description: 'Write a standardized self-contained HTML preview',
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
41
|
flag: '-f, --overwrite',
|
|
42
42
|
param: 'options.overwrite',
|
|
43
|
-
description:
|
|
44
|
-
'Replace existing candidate, receipt, and preview files. Without it, if any of them exists, nothing is written',
|
|
43
|
+
description: 'Replace existing candidate and receipt files',
|
|
45
44
|
},
|
|
46
45
|
],
|
|
47
46
|
examples: [
|
|
@@ -8,7 +8,8 @@ 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.
|
|
11
|
+
'Palette authoring tools. The initial generate command creates reviewable candidates. ' +
|
|
12
|
+
'Palette inspection and diagnostic commands are intentionally deferred to follow-up work.',
|
|
12
13
|
subcommands: ['generate'],
|
|
13
14
|
examples: [
|
|
14
15
|
{
|
|
@@ -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
|
-
'
|
|
23
|
+
'component to assemble. Pass a component name to scope it; pass any substring to search ' +
|
|
24
|
+
'keys. `--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,8 +13,7 @@ export const doc = {
|
|
|
13
13
|
name: 'theme',
|
|
14
14
|
displayName: 'astryx theme',
|
|
15
15
|
namespace: 'cli/commands',
|
|
16
|
-
summary:
|
|
17
|
-
'Create and build themes: add a shipped one, compile to CSS, or list what a theme can override',
|
|
16
|
+
summary: 'Theme tools: build, export, and manage themes',
|
|
18
17
|
description:
|
|
19
18
|
'The theme command group. Running astryx theme with no subcommand prints the ' +
|
|
20
19
|
'subcommand list; the work happens in the subcommands: compile a theme (build), ' +
|
|
@@ -13,8 +13,7 @@ export const doc = {
|
|
|
13
13
|
name: 'upgrade',
|
|
14
14
|
displayName: 'astryx upgrade',
|
|
15
15
|
namespace: 'cli/commands',
|
|
16
|
-
summary:
|
|
17
|
-
'Update your code after upgrading Astryx, and refresh ShadCN-copied components',
|
|
16
|
+
summary: 'Migrate versions and update ShadCN-copied compositions',
|
|
18
17
|
description:
|
|
19
18
|
'Migrates project source from a previous Astryx version to the installed one by ' +
|
|
20
19
|
'running the registered codemods, and refreshes the fully rendered managed ' +
|
|
@@ -34,7 +33,7 @@ export const doc = {
|
|
|
34
33
|
{
|
|
35
34
|
flag: '--apply',
|
|
36
35
|
param: 'options.apply',
|
|
37
|
-
description: 'Write changes to disk
|
|
36
|
+
description: 'Write changes to disk (default: dry-run)',
|
|
38
37
|
default: false,
|
|
39
38
|
},
|
|
40
39
|
{
|
|
@@ -81,8 +80,7 @@ export const doc = {
|
|
|
81
80
|
flag: '--registry',
|
|
82
81
|
param: 'options.registry',
|
|
83
82
|
description:
|
|
84
|
-
'Only
|
|
85
|
-
'edits that do not overlap are merged, and conflicts are left untouched; --from is not required. ' +
|
|
83
|
+
'Only reconcile ShadCN-copied compositions; --from is not required. ' +
|
|
86
84
|
'Combining it with --list, --from, --force, --codemod, --skip-codemod, --integration or --install-deps exits 1 with ERR_INVALID_ARGUMENT',
|
|
87
85
|
default: false,
|
|
88
86
|
},
|
|
@@ -115,61 +113,4 @@ export const doc = {
|
|
|
115
113
|
},
|
|
116
114
|
],
|
|
117
115
|
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
|
-
],
|
|
175
116
|
};
|
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 {addExitCodesHelp, 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,7 +95,6 @@ export const JSON_SUPPORTED = new Set([
|
|
|
95
95
|
'theme targets',
|
|
96
96
|
'theme palette generate',
|
|
97
97
|
'integration add',
|
|
98
|
-
'integration verify',
|
|
99
98
|
'integration pack',
|
|
100
99
|
'upgrade',
|
|
101
100
|
'manifest',
|
|
@@ -104,6 +103,9 @@ export const JSON_SUPPORTED = new Set([
|
|
|
104
103
|
'doctor integration templates',
|
|
105
104
|
'doctor integration components',
|
|
106
105
|
'doctor integration docs',
|
|
106
|
+
'layout expand',
|
|
107
|
+
'layout check',
|
|
108
|
+
'layout grammar',
|
|
107
109
|
]);
|
|
108
110
|
|
|
109
111
|
/**
|
|
@@ -262,6 +264,7 @@ const commands = [
|
|
|
262
264
|
{name: 'gap-report', path: './commands/gap-report.mjs', register: 'registerGapReport'},
|
|
263
265
|
// agent-docs folded into init — functions still importable from agent-docs.mjs
|
|
264
266
|
{name: 'template', path: './commands/template.mjs', register: 'registerTemplate'},
|
|
267
|
+
{name: 'layout', path: './commands/layout.mjs', register: 'registerLayout'},
|
|
265
268
|
{name: 'upgrade', path: './commands/upgrade.mjs', register: 'registerUpgrade'},
|
|
266
269
|
{name: 'theme', path: './commands/build-theme.mjs', register: 'registerTheme'},
|
|
267
270
|
{name: 'integration', path: './commands/integration.mjs', register: 'registerIntegration'},
|
|
@@ -339,24 +342,16 @@ export async function createProgram() {
|
|
|
339
342
|
.name('astryx')
|
|
340
343
|
.description('Design system CLI — components, themes, and tooling')
|
|
341
344
|
.version(pkg.version)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
.option(
|
|
345
|
-
'--zh',
|
|
346
|
-
'Simplified Chinese for component reads and for docs topics that have a translation (English otherwise)',
|
|
347
|
-
)
|
|
348
|
-
.option('--dense', 'Token-efficient dense text for component <Name> and docs <topic> reads')
|
|
345
|
+
.option('--zh', 'Output docs in Chinese Simplified')
|
|
346
|
+
.option('--dense', 'Output docs in compressed dense format (token-efficient)')
|
|
349
347
|
.addOption(
|
|
350
348
|
new Option(
|
|
351
349
|
'--lang <locale>',
|
|
352
|
-
'
|
|
350
|
+
'Output docs in specified language/format (en, zh, dense)',
|
|
353
351
|
).choices(['en', 'zh', 'dense']),
|
|
354
352
|
)
|
|
355
353
|
.addOption(
|
|
356
|
-
new Option(
|
|
357
|
-
'--detail <level>',
|
|
358
|
-
'Detail level for component, hook, and docs tree reads (e.g. docs cli/commands/build). Lists default to brief',
|
|
359
|
-
)
|
|
354
|
+
new Option('--detail <level>', 'Output detail level (full, compact, brief)')
|
|
360
355
|
.choices(['full', 'compact', 'brief'])
|
|
361
356
|
.default('full'),
|
|
362
357
|
)
|
|
@@ -476,19 +471,6 @@ export async function createProgram() {
|
|
|
476
471
|
const fullName = fullCommandName(actionCommand, program);
|
|
477
472
|
if (JSON_SUPPORTED.has(fullName)) return;
|
|
478
473
|
process.__xdsJsonHandled = true;
|
|
479
|
-
// A group given a word it does not have reports an unknown subcommand and
|
|
480
|
-
// lists the ones it has, in JSON as in text, even when a flag follows it.
|
|
481
|
-
const extras = actionCommand.commands.length > 0 ? actionCommand.args : [];
|
|
482
|
-
const unknown = extras.find(arg => !String(arg).startsWith('-'));
|
|
483
|
-
if (unknown != null) {
|
|
484
|
-
cliError(`unknown subcommand '${fullName} ${unknown}'`, {
|
|
485
|
-
code: ERROR_CODES.ERR_UNKNOWN_SUBCOMMAND,
|
|
486
|
-
suggestions: actionCommand.commands.map(child => ({
|
|
487
|
-
name: child.name(),
|
|
488
|
-
reason: 'available subcommand',
|
|
489
|
-
})),
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
474
|
debug.setOutcome('rejected', {
|
|
493
475
|
exitCode: 1,
|
|
494
476
|
code: ERROR_CODES.ERR_INVALID_OPTION,
|
|
@@ -623,7 +605,7 @@ export async function createProgram() {
|
|
|
623
605
|
text(`Run \`${getCliInvocation()} manifest --json\` for the full structured manifest.`),
|
|
624
606
|
);
|
|
625
607
|
});
|
|
626
|
-
|
|
608
|
+
addExitCodesHelp(manifestCommand, manifestDoc.exitCodes);
|
|
627
609
|
markReportsResult(manifestCommand);
|
|
628
610
|
|
|
629
611
|
// Hidden command used by package.json postinstall scripts
|
|
@@ -25,8 +25,6 @@
|
|
|
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';
|
|
30
28
|
import {text} from '../formatters/index.mjs';
|
|
31
29
|
|
|
32
30
|
/**
|
|
@@ -145,11 +143,10 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
|
|
|
145
143
|
cmd.addOption(option);
|
|
146
144
|
}
|
|
147
145
|
|
|
148
|
-
// Help ends with the documented exit codes
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
addDocHelp(cmd, doc);
|
|
146
|
+
// Help ends with the documented exit codes. `choices` stay in the option
|
|
147
|
+
// text: Commander `.choices()` would replace the api layer's
|
|
148
|
+
// ERR_INVALID_ARGUMENT validation.
|
|
149
|
+
addExitCodesHelp(cmd, doc.exitCodes);
|
|
153
150
|
|
|
154
151
|
if (action) {
|
|
155
152
|
// The recording seam. An action's job ends at "here is what I answered
|
|
@@ -168,27 +165,6 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
|
|
|
168
165
|
return cmd;
|
|
169
166
|
}
|
|
170
167
|
|
|
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
|
-
|
|
192
168
|
/**
|
|
193
169
|
* End `cmd`'s help with a CommandDoc's exit codes.
|
|
194
170
|
* @param {import('commander').Command} cmd
|
|
@@ -7,7 +7,6 @@
|
|
|
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';
|
|
11
10
|
import {doc as searchCommand} from '../commands/search.doc.mjs';
|
|
12
11
|
import {doc as searchFn} from '../../../api/search/search.doc.mjs';
|
|
13
12
|
|
|
@@ -47,57 +46,4 @@ describe('defineCommand', () => {
|
|
|
47
46
|
expect(cmd.name()).toBe('build');
|
|
48
47
|
expect(cmd.registeredArguments.map(a => a.name())).toEqual(['file']);
|
|
49
48
|
});
|
|
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
|
-
});
|
|
103
49
|
});
|
|
@@ -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',
|
|
45
45
|
async (name, doc) => {
|
|
46
46
|
const {status, stdout} = await runCli([...name.split(' '), '--help']);
|
|
47
47
|
expect(status).toBe(0);
|
|
@@ -51,25 +51,16 @@ 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);
|
|
70
54
|
},
|
|
71
55
|
);
|
|
72
56
|
|
|
57
|
+
it('bare `astryx layout` exits 1 in both modes, as documented', async () => {
|
|
58
|
+
const doc = commandDocs.find((d) => d.name === 'layout');
|
|
59
|
+
expect(doc.exitCodes.find((e) => e.code === 1)?.when).toMatch(/^no subcommand/);
|
|
60
|
+
expect((await runCli(['layout'])).status).toBe(1);
|
|
61
|
+
expect((await runCli(['layout', '--json'])).status).toBe(1);
|
|
62
|
+
});
|
|
63
|
+
|
|
73
64
|
it('`astryx discover` with a blank query exits 1 only when packages are discovered', async () => {
|
|
74
65
|
const doc = commandDocs.find((d) => d.name === 'discover');
|
|
75
66
|
expect(doc.exitCodes.find((e) => e.code === 1)?.when).toMatch(/blank query when packages are discovered/);
|
|
@@ -33,12 +33,10 @@
|
|
|
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
|
-
*
|
|
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.
|
|
36
|
+
* Non-JSON behavior is preserved exactly: every code path that printed
|
|
37
|
+
* to stderr before still prints to stderr. Commander writes its
|
|
38
|
+
* "error: ..." line via configureOutput.writeErr, which we pass
|
|
39
|
+
* through verbatim outside of --json mode.
|
|
42
40
|
*/
|
|
43
41
|
|
|
44
42
|
import {API_VERSION, isJsonMode, toErrorEnvelope} from '../../../foundation/response/json.mjs';
|
|
@@ -251,20 +249,11 @@ function applyShimRecursively(cmd) {
|
|
|
251
249
|
});
|
|
252
250
|
cmd.configureOutput({
|
|
253
251
|
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.
|
|
265
252
|
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.
|
|
266
256
|
if (jsonActive()) return;
|
|
267
|
-
if (/^error:\s/i.test(str)) return;
|
|
268
257
|
process.stderr.write(str);
|
|
269
258
|
},
|
|
270
259
|
});
|
|
@@ -443,15 +432,16 @@ export function handleCommanderError(err) {
|
|
|
443
432
|
code: commanderCodeToErrorCode(code, message),
|
|
444
433
|
});
|
|
445
434
|
|
|
446
|
-
// Real error paths.
|
|
447
|
-
// the key is already `error`, and in text mode the Astryx prefix replaces it.
|
|
448
|
-
const cleaned = message.replace(/^error:\s*/i, '');
|
|
435
|
+
// Real error paths.
|
|
449
436
|
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, '');
|
|
450
440
|
emitJsonError(cleaned, undefined, commanderCodeToErrorCode(code, cleaned));
|
|
451
441
|
} else {
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
|
|
442
|
+
// Non-JSON mode: Commander already wrote the "error: ..." line
|
|
443
|
+
// to stderr via configureOutput.writeErr before throwing the
|
|
444
|
+
// CommanderError. Nothing to do — exit with the original code.
|
|
455
445
|
}
|
|
456
446
|
process.exit(exitCode || 1);
|
|
457
447
|
}
|
|
@@ -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
|
});
|