@astryxdesign/cli 0.6.4 → 0.6.5-canary.00f1ed9
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 +103 -95
- package/api/build/_adapter.d.mts +36 -2
- package/api/build/_adapter.mjs +41 -10
- package/api/build/build.doc.mjs +8 -3
- package/api/build/build.test.mjs +60 -2
- package/api/build/kit/kit.mjs +109 -26
- package/api/build/kit/rank.d.mts +24 -8
- package/api/build/kit/rank.mjs +277 -97
- package/api/build/kit/rank.test.mjs +231 -48
- 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/docs/docs.test.mjs +54 -18
- package/api/docs/integration-tree.test.mjs +17 -0
- package/api/docs/integrationDocs.test.mjs +27 -5
- 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/error.d.mts +22 -0
- package/api/error.mjs +42 -0
- 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-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +7 -3
- package/api/integration/add-contribution.test.mjs +3 -3
- package/api/integration/add-helpers.d.mts +5 -2
- package/api/integration/add-helpers.mjs +36 -9
- package/api/integration/add-theme.mjs +266 -23
- package/api/integration/add-theme.test.mjs +247 -0
- package/api/integration/authoring-checks.mjs +12 -9
- package/api/integration/authoring-checks.type.d.mts +8 -0
- package/api/integration/authoring-checks.type.mjs +7 -3
- package/api/integration/integration-authoring.type.d.mts +4 -1
- package/api/integration/integration-authoring.type.mjs +6 -1
- package/api/integration/integrationAdd.doc.mjs +6 -0
- package/api/integration/integrationAddTheme.doc.mjs +10 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -1
- package/api/integration/integrationDocConflicts.doc.mjs +1 -1
- package/api/integration/integrationPackCheck.doc.mjs +3 -3
- package/api/integration/integrationTemplateConflicts.doc.mjs +1 -1
- package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
- package/api/integration/pack-check.mjs +92 -10
- package/api/integration/pack-check.test.mjs +140 -1
- package/api/integration/pack-check.type.mjs +1 -1
- package/api/integration/validate-integration.d.mts +4 -2
- package/api/integration/validate-integration.mjs +7 -2
- package/api/integration/validate-integration.test.mjs +55 -0
- package/api/integration/validate-integration.type.d.mts +5 -0
- package/api/integration/validate-integration.type.mjs +5 -1
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/json/assertResponse.doc.mjs +1 -1
- package/api/json/isError.doc.mjs +1 -1
- package/api/layout/expand/expand.mjs +12 -7
- package/api/layout/expand/expand.receipt.test.mjs +74 -0
- package/api/layout/layout.type.d.mts +1 -0
- package/api/layout/layout.type.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -1
- package/api/search/search.d.mts +51 -1
- package/api/search/search.doc.mjs +2 -2
- package/api/search/search.mjs +299 -17
- package/api/search/search.test.mjs +216 -18
- package/api/swizzle/copy/copy.mjs +66 -3
- package/api/swizzle/swizzle.doc.mjs +11 -5
- package/api/template/copy/copy.mjs +15 -9
- package/api/template/copy/copy.receipt.test.mjs +77 -0
- package/api/template/copy/copy.test.mjs +9 -0
- package/api/template/show/show.mjs +15 -4
- package/api/template/show/show.test.mjs +76 -0
- package/api/template/template-integration.test.mjs +14 -0
- package/api/template/template.d.mts +1 -1
- package/api/template/template.doc.mjs +8 -3
- package/api/template/template.mjs +1 -0
- package/api/template/template.type.d.mts +2 -0
- package/api/template/template.type.mjs +2 -0
- 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.d.mts +24 -0
- package/api/theme/build/build.family.test.mjs +7 -12
- package/api/theme/build/build.mjs +243 -26
- package/api/theme/build/build.project-core.test.mjs +165 -0
- 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 +25 -6
- package/api/upgrade/run/run.test.mjs +45 -1
- package/api/upgrade/upgrade.doc.mjs +24 -22
- package/api/upgrade/upgrade.type.d.mts +1 -0
- package/api/upgrade/upgrade.type.mjs +3 -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 +3 -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 +121 -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 +153 -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 +162 -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/assets/templates/pages/ai-chat/template.doc.mjs +16 -1
- package/assets/templates/pages/ai-chat-landing/template.doc.mjs +9 -1
- package/assets/templates/pages/blank/template.doc.mjs +3 -1
- package/assets/templates/pages/canvas-editor/template.doc.mjs +9 -1
- package/assets/templates/pages/centered-hero/template.doc.mjs +3 -1
- package/assets/templates/pages/checkout-wizard/template.doc.mjs +1 -0
- package/assets/templates/pages/classic-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/contact-form/template.doc.mjs +16 -1
- package/assets/templates/pages/dashboard/template.doc.mjs +15 -1
- package/assets/templates/pages/dashboard-alert-rail/template.doc.mjs +23 -1
- package/assets/templates/pages/dashboard-cohort-funnel/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-comparison/template.doc.mjs +9 -1
- package/assets/templates/pages/dashboard-composition/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-progress/template.doc.mjs +17 -1
- package/assets/templates/pages/dashboard-scorecard/template.doc.mjs +2 -1
- package/assets/templates/pages/detail-page/template.doc.mjs +8 -0
- package/assets/templates/pages/documentation/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-design/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-technical/template.doc.mjs +10 -1
- package/assets/templates/pages/editor/template.doc.mjs +9 -1
- package/assets/templates/pages/file-explorer/template.doc.mjs +3 -1
- package/assets/templates/pages/form-two-column/template.doc.mjs +17 -1
- package/assets/templates/pages/form-wizard/template.doc.mjs +14 -1
- package/assets/templates/pages/form-wizard-dialog/template.doc.mjs +1 -0
- package/assets/templates/pages/gallery-hero/template.doc.mjs +10 -1
- package/assets/templates/pages/ide/template.doc.mjs +9 -1
- package/assets/templates/pages/incident-console/template.doc.mjs +10 -1
- package/assets/templates/pages/kanban-board/template.doc.mjs +11 -1
- package/assets/templates/pages/library/template.doc.mjs +15 -1
- package/assets/templates/pages/login/template.doc.mjs +10 -1
- package/assets/templates/pages/login-card/template.doc.mjs +11 -1
- package/assets/templates/pages/login-split/template.doc.mjs +10 -1
- package/assets/templates/pages/login-sso/template.doc.mjs +10 -1
- package/assets/templates/pages/messaging-shell/template.doc.mjs +11 -1
- package/assets/templates/pages/mixed-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/payment-form/template.doc.mjs +3 -1
- package/assets/templates/pages/product-detail/template.doc.mjs +9 -1
- package/assets/templates/pages/product-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/settings/template.doc.mjs +3 -1
- package/assets/templates/pages/settings-dialog/template.doc.mjs +9 -1
- package/assets/templates/pages/settings-sidebar/template.doc.mjs +9 -1
- package/assets/templates/pages/shell-nav/template.doc.mjs +10 -1
- package/assets/templates/pages/shell-side-nav/template.doc.mjs +14 -1
- package/assets/templates/pages/shell-top-nav/template.doc.mjs +11 -1
- package/assets/templates/pages/side-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/table/template.doc.mjs +11 -1
- package/assets/templates/pages/table-filter/template.doc.mjs +21 -1
- package/assets/templates/pages/table-grouped/template.doc.mjs +15 -1
- package/assets/templates/pages/table-inbox/template.doc.mjs +18 -6
- package/assets/templates/pages/table-page/template.doc.mjs +20 -1
- package/assets/templates/pages/table-tree/template.doc.mjs +14 -1
- package/assets/templates/pages/theme-showcase/template.doc.mjs +10 -1
- package/assets/templates/pages/work-item-detail/template.doc.mjs +10 -0
- package/assets/templates/themes/butter/icons.tsx +2 -0
- package/assets/templates/themes/chocolate/icons.tsx +2 -0
- package/assets/templates/themes/gothic/icons.tsx +2 -0
- package/assets/templates/themes/matcha/icons.tsx +2 -0
- package/assets/templates/themes/neutral/icons.tsx +2 -0
- package/assets/templates/themes/stone/icons.tsx +2 -0
- package/assets/templates/themes/y2k/icons.tsx +2 -0
- 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/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +1 -0
- package/authoring/doctypes/template/parse.test.mjs +21 -0
- package/authoring/doctypes/template/template.doc.mjs +7 -1
- package/authoring/doctypes/template/type.ts +12 -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/docs.test.mjs +113 -24
- 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 +53 -9
- 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 +17 -7
- package/clients/cli/commands/integration-authoring.test.mjs +70 -9
- 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 +76 -43
- package/clients/cli/commands/layout-expand.doc.mjs +3 -1
- package/clients/cli/commands/layout.expand-receipt.test.mjs +94 -0
- package/clients/cli/commands/layout.mjs +16 -0
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +10 -3
- package/clients/cli/commands/search.mjs +21 -2
- package/clients/cli/commands/search.test.mjs +21 -4
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.copy-receipt.test.mjs +60 -0
- package/clients/cli/commands/template.doc.mjs +1 -1
- package/clients/cli/commands/template.mjs +19 -5
- package/clients/cli/commands/template.show-media.test.mjs +62 -0
- 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.ascii-output.test.mjs +14 -0
- package/clients/cli/commands/upgrade.doc.mjs +62 -3
- package/clients/cli/commands/write-failure.test.mjs +175 -0
- 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 +48 -156
- package/clients/cli/lib/manifest.test.mjs +103 -9
- package/clients/cli/lib/parse-error-format.test.mjs +81 -0
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/agent-docs/agent-docs.test.mjs +3 -2
- 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/discovery/template-adapter.d.mts +14 -0
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +37 -1
- package/foundation/discovery/template-adapter.mjs +21 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +5 -4
- 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/doc-compiler/tree.test.mjs +65 -14
- package/foundation/integrations/cli-requirement.d.mts +75 -11
- package/foundation/integrations/cli-requirement.mjs +120 -23
- package/foundation/integrations/cli-requirement.test.mjs +141 -9
- 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 +5 -4
- package/foundation/response/response-types.doc.mjs +49 -19
- package/foundation/response/response-types.doc.test.mjs +23 -0
- package/foundation/response/response.doc.mjs +11 -10
- package/package.json +9 -9
- package/assets/docs/tree/integrations.test.mjs +0 -62
- package/assets/docs/tree/writing-docs.doc.mjs +0 -286
|
@@ -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: 'Pick the owner when two packages ship the same slug',
|
|
45
45
|
cli: 'astryx theme add ocean --package @acme/themes',
|
|
46
46
|
},
|
|
47
47
|
],
|
|
@@ -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), ' +
|
|
@@ -84,4 +84,18 @@ describe('upgrade human output is ASCII', () => {
|
|
|
84
84
|
'minSize: 200',
|
|
85
85
|
);
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
// Every fixture above has a real src/, so the completion line for a project
|
|
89
|
+
// whose source is somewhere else was never reached — this suite was green on
|
|
90
|
+
// that path by luck, not by coverage.
|
|
91
|
+
it('reports a source directory that does not exist', async () => {
|
|
92
|
+
fs.rmSync(path.join(tmpDir, 'src'), {recursive: true, force: true});
|
|
93
|
+
fs.mkdirSync(path.join(tmpDir, 'app'), {recursive: true});
|
|
94
|
+
write('app/panel.tsx', 'export const x = 1;\n');
|
|
95
|
+
|
|
96
|
+
expect(await nonAsciiLines(['upgrade', '--from', '0.5.0'])).toEqual([]);
|
|
97
|
+
expect(
|
|
98
|
+
await nonAsciiLines(['upgrade', '--from', '0.5.0', '--apply']),
|
|
99
|
+
).toEqual([]);
|
|
100
|
+
});
|
|
87
101
|
});
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file A write that fails on the filesystem reports ERR_WRITE_FAILED.
|
|
5
|
+
*
|
|
6
|
+
* An unwritable target produced `{"error": "EACCES: permission denied, open
|
|
7
|
+
* '/abs/host/path/readonly/x.tsx'", "code": "ERR_UNKNOWN"}` — the raw Node
|
|
8
|
+
* errno error, with the wrong code and an absolute host path in the message.
|
|
9
|
+
* ERR_WRITE_FAILED is in the frozen registry for exactly this case, and every
|
|
10
|
+
* other Astryx message names its target relative to the project.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as os from 'node:os';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import {fileURLToPath} from 'node:url';
|
|
18
|
+
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
19
|
+
|
|
20
|
+
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../..');
|
|
21
|
+
|
|
22
|
+
// chmod means nothing to root, so the unwritable directory would be writable
|
|
23
|
+
// and the command would succeed. Skip rather than assert something false.
|
|
24
|
+
const asRoot = typeof process.getuid === 'function' && process.getuid() === 0;
|
|
25
|
+
|
|
26
|
+
let dir;
|
|
27
|
+
let readonlyDir;
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-write-fail-'));
|
|
31
|
+
fs.writeFileSync(
|
|
32
|
+
path.join(dir, 'package.json'),
|
|
33
|
+
JSON.stringify({
|
|
34
|
+
name: 'scratch',
|
|
35
|
+
version: '1.0.0',
|
|
36
|
+
dependencies: {'@astryxdesign/core': '0.6.3'},
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
fs.mkdirSync(path.join(dir, 'node_modules', '@astryxdesign'), {recursive: true});
|
|
40
|
+
fs.symlinkSync(
|
|
41
|
+
path.join(REPO, 'packages', 'core'),
|
|
42
|
+
path.join(dir, 'node_modules', '@astryxdesign', 'core'),
|
|
43
|
+
'dir',
|
|
44
|
+
);
|
|
45
|
+
readonlyDir = path.join(dir, 'readonly');
|
|
46
|
+
fs.mkdirSync(readonlyDir);
|
|
47
|
+
fs.chmodSync(readonlyDir, 0o500);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
try {
|
|
52
|
+
fs.chmodSync(readonlyDir, 0o700);
|
|
53
|
+
} catch {
|
|
54
|
+
// already gone
|
|
55
|
+
}
|
|
56
|
+
fs.rmSync(dir, {recursive: true, force: true});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/** @param {string[]} args */
|
|
60
|
+
const json = async args => {
|
|
61
|
+
const {status, stdout} = await runCli(['--json', ...args], {cwd: dir});
|
|
62
|
+
return {status, body: JSON.parse(stdout)};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
describe.skipIf(asRoot)('a failed write reports ERR_WRITE_FAILED', () => {
|
|
66
|
+
it('template into an unwritable directory', async () => {
|
|
67
|
+
const {status, body} = await json(['template', 'ai-chat', 'readonly/x.tsx']);
|
|
68
|
+
|
|
69
|
+
expect(status).toBe(1);
|
|
70
|
+
expect(body.code).toBe('ERR_WRITE_FAILED');
|
|
71
|
+
expect(body.error).toContain('readonly/x.tsx');
|
|
72
|
+
expect(body.error).toContain('EACCES');
|
|
73
|
+
// The whole point of the relative form: no absolute host path escapes.
|
|
74
|
+
expect(body.error).not.toContain(dir);
|
|
75
|
+
expect(fs.existsSync(path.join(readonlyDir, 'x.tsx'))).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('swizzle into an unwritable directory', async () => {
|
|
79
|
+
const {status, body} = await json([
|
|
80
|
+
'swizzle',
|
|
81
|
+
'Button',
|
|
82
|
+
'--output',
|
|
83
|
+
'readonly/sub',
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
expect(status).toBe(1);
|
|
87
|
+
expect(body.code).toBe('ERR_WRITE_FAILED');
|
|
88
|
+
expect(body.error).toContain('readonly/sub');
|
|
89
|
+
expect(body.error).not.toContain(dir);
|
|
90
|
+
expect(fs.existsSync(path.join(readonlyDir, 'sub'))).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('still writes normally into a writable directory', async () => {
|
|
94
|
+
const {status, body} = await json(['template', 'ai-chat', 'src/page.tsx']);
|
|
95
|
+
|
|
96
|
+
expect(status, JSON.stringify(body).slice(0, 200)).toBe(0);
|
|
97
|
+
expect(body.type).toBe('template.copy');
|
|
98
|
+
expect(fs.existsSync(path.join(dir, 'src', 'page.tsx'))).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe.skipIf(asRoot)('a swizzle that fails part-way undoes what it wrote', () => {
|
|
103
|
+
it('restores replaced files, removes created ones, and says nothing was written', async () => {
|
|
104
|
+
// Learn the component's files, in copy order, from a successful swizzle.
|
|
105
|
+
const probe = await json(['swizzle', 'Button', '--output', 'probe']);
|
|
106
|
+
expect(probe.status, JSON.stringify(probe.body).slice(0, 200)).toBe(0);
|
|
107
|
+
const files = probe.body.data.files;
|
|
108
|
+
expect(files.length).toBeGreaterThan(1);
|
|
109
|
+
const outDir = path.join(dir, 'out', path.basename(probe.body.data.outputDir));
|
|
110
|
+
|
|
111
|
+
// The first file exists (replaceable), the middle ones do not, and the
|
|
112
|
+
// last one is read-only, so --overwrite fails after writing the others.
|
|
113
|
+
const first = files[0];
|
|
114
|
+
const middle = files.slice(1, -1);
|
|
115
|
+
const last = files[files.length - 1];
|
|
116
|
+
fs.mkdirSync(outDir, {recursive: true});
|
|
117
|
+
fs.writeFileSync(path.join(outDir, first), 'before first\n');
|
|
118
|
+
fs.writeFileSync(path.join(outDir, last), 'before last\n');
|
|
119
|
+
fs.chmodSync(path.join(outDir, last), 0o444);
|
|
120
|
+
|
|
121
|
+
const {status, body} = await json([
|
|
122
|
+
'swizzle',
|
|
123
|
+
'Button',
|
|
124
|
+
'--output',
|
|
125
|
+
'out',
|
|
126
|
+
'--overwrite',
|
|
127
|
+
]);
|
|
128
|
+
fs.chmodSync(path.join(outDir, last), 0o644);
|
|
129
|
+
|
|
130
|
+
expect(status).toBe(1);
|
|
131
|
+
expect(body.code).toBe('ERR_WRITE_FAILED');
|
|
132
|
+
expect(body.error).toContain(last);
|
|
133
|
+
expect(body.error).toContain('Nothing was written.');
|
|
134
|
+
expect(body.error).not.toContain(dir);
|
|
135
|
+
expect(fs.readFileSync(path.join(outDir, first), 'utf8')).toBe('before first\n');
|
|
136
|
+
expect(fs.readFileSync(path.join(outDir, last), 'utf8')).toBe('before last\n');
|
|
137
|
+
for (const file of middle) {
|
|
138
|
+
expect(fs.existsSync(path.join(outDir, file))).toBe(false);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('undoes the copy when a destination cannot be read back', async () => {
|
|
143
|
+
const probe = await json(['swizzle', 'Button', '--output', 'probe']);
|
|
144
|
+
expect(probe.status, JSON.stringify(probe.body).slice(0, 200)).toBe(0);
|
|
145
|
+
const files = probe.body.data.files;
|
|
146
|
+
const outDir = path.join(dir, 'out', path.basename(probe.body.data.outputDir));
|
|
147
|
+
|
|
148
|
+
// A directory where the last file goes: the rollback snapshot cannot read
|
|
149
|
+
// it, and the earlier files are already written when the copy reaches it.
|
|
150
|
+
const first = files[0];
|
|
151
|
+
const middle = files.slice(1, -1);
|
|
152
|
+
const last = files[files.length - 1];
|
|
153
|
+
fs.mkdirSync(path.join(outDir, last), {recursive: true});
|
|
154
|
+
fs.writeFileSync(path.join(outDir, first), 'before first\n');
|
|
155
|
+
|
|
156
|
+
const {status, body} = await json([
|
|
157
|
+
'swizzle',
|
|
158
|
+
'Button',
|
|
159
|
+
'--output',
|
|
160
|
+
'out',
|
|
161
|
+
'--overwrite',
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
expect(status).toBe(1);
|
|
165
|
+
expect(body.code).toBe('ERR_WRITE_FAILED');
|
|
166
|
+
expect(body.error).toContain(last);
|
|
167
|
+
expect(body.error).toContain('Nothing was written.');
|
|
168
|
+
expect(body.error).not.toContain(dir);
|
|
169
|
+
expect(fs.readFileSync(path.join(outDir, first), 'utf8')).toBe('before first\n');
|
|
170
|
+
expect(fs.statSync(path.join(outDir, last)).isDirectory()).toBe(true);
|
|
171
|
+
for (const file of middle) {
|
|
172
|
+
expect(fs.existsSync(path.join(outDir, file))).toBe(false);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
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
|
}
|