@astryxdesign/cli 0.6.3 → 0.6.4-canary.10dd683
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 +194 -0
- package/README.md +117 -78
- package/api/blog/blog.doc.mjs +1 -0
- package/api/build/_adapter.d.mts +50 -0
- package/api/build/_adapter.mjs +60 -0
- package/api/build/build.doc.mjs +16 -9
- package/api/build/build.test.mjs +197 -8
- package/api/build/build.type.d.mts +91 -2
- package/api/build/build.type.mjs +52 -8
- package/api/build/help/help.d.mts +12 -5
- package/api/build/help/help.mjs +69 -6
- package/api/build/kit/kit.d.mts +4 -1
- package/api/build/kit/kit.mjs +165 -49
- package/api/build/kit/rank.d.mts +44 -0
- package/api/build/kit/rank.mjs +432 -0
- package/api/build/kit/rank.test.mjs +196 -0
- package/api/component/_adapter.d.mts +6 -12
- package/api/component/_adapter.mjs +20 -10
- package/api/component/component.doc.mjs +13 -3
- package/api/component/component.mjs +91 -14
- package/api/component/component.test.mjs +38 -0
- package/api/component/component.type.d.mts +22 -11
- package/api/component/component.type.mjs +32 -24
- package/api/component/detail/blocks/blocks.d.mts +2 -1
- package/api/component/detail/blocks/blocks.mjs +4 -3
- package/api/component/list/list.d.mts +0 -5
- package/api/component/list/list.mjs +40 -11
- 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 +62 -18
- package/api/discover/discover.mjs +220 -36
- package/api/discover/discover.test.mjs +11 -2
- package/api/discover/discover.type.d.mts +150 -11
- package/api/discover/discover.type.mjs +107 -17
- 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 +272 -41
- package/api/docs/_adapter.mjs +985 -108
- package/api/docs/compiled-topics.test.mjs +78 -0
- package/api/docs/detail/detail.mjs +22 -63
- package/api/docs/detail/section/section.d.mts +1 -1
- package/api/docs/detail/section/section.mjs +54 -19
- package/api/docs/detail/section/section.test.mjs +50 -0
- package/api/docs/docs.d.mts +10 -3
- package/api/docs/docs.doc.mjs +55 -16
- package/api/docs/docs.mjs +53 -10
- package/api/docs/docs.test.mjs +164 -4
- package/api/docs/docs.type.d.mts +221 -5
- package/api/docs/docs.type.mjs +153 -11
- package/api/docs/index/index.d.mts +18 -0
- package/api/docs/index/index.mjs +40 -0
- package/api/docs/index/index.test.mjs +62 -0
- package/api/docs/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +114 -8
- package/api/docs/list/list.mjs +28 -12
- package/api/docs/node/node.d.mts +43 -0
- package/api/docs/node/node.mjs +192 -0
- package/api/docs/reference-blocks.test.mjs +406 -0
- package/api/doctor/doctor.d.mts +99 -1
- package/api/doctor/doctor.doc.mjs +1 -0
- package/api/doctor/doctor.mjs +548 -1
- package/api/doctor/doctor.test.mjs +610 -1
- package/api/gap-report/gap-report.doc.mjs +8 -4
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +1 -0
- package/api/hook/hook.type.d.mts +3 -3
- package/api/hook/hook.type.mjs +11 -11
- package/api/hook/list/list.d.mts +2 -2
- package/api/hook/list/list.mjs +69 -17
- package/api/index.d.mts +1 -1
- package/api/index.mjs +1 -0
- package/api/init/init.doc.mjs +6 -1
- package/api/init/init.test.mjs +41 -1
- package/api/init/remove/remove.mjs +1 -1
- package/api/init/run/run.mjs +20 -10
- package/api/integration/add-contribution.component-names.test.mjs +120 -0
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +130 -15
- package/api/integration/add-contribution.test.mjs +258 -7
- package/api/integration/add-helpers.d.mts +5 -2
- package/api/integration/add-helpers.mjs +36 -9
- package/api/integration/add-theme.mjs +34 -64
- package/api/integration/add-theme.test.mjs +105 -21
- package/api/integration/authoring-checks.mjs +138 -28
- package/api/integration/authoring-checks.test.mjs +179 -7
- package/api/integration/authoring-checks.type.mjs +6 -1
- package/api/integration/integration-authoring.type.d.mts +3 -1
- package/api/integration/integration-authoring.type.mjs +2 -0
- package/api/integration/integration-block-exports.test.mjs +10 -6
- package/api/integration/integrationAdd.doc.mjs +14 -4
- package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
- package/api/integration/integrationAddCodemod.doc.mjs +1 -0
- package/api/integration/integrationAddComponent.doc.mjs +2 -1
- package/api/integration/integrationAddDoc.doc.mjs +8 -1
- package/api/integration/integrationAddTemplate.doc.mjs +1 -0
- package/api/integration/integrationAddTheme.doc.mjs +6 -5
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
- package/api/integration/integrationDocConflicts.doc.mjs +2 -1
- package/api/integration/integrationPackCheck.doc.mjs +2 -1
- package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
- package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
- package/api/integration/pack-check.mjs +83 -7
- package/api/integration/pack-check.test.mjs +387 -47
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +14 -1
- package/api/integration/summarizeIssues.doc.mjs +1 -0
- package/api/integration/template-conflict-compatibility.test.mjs +73 -0
- package/api/integration/validate-integration-fixes.test.mjs +1389 -0
- package/api/integration/validate-integration.mjs +52 -102
- package/api/integration/validate-integration.test.mjs +179 -26
- package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
- package/api/integration/validateIntegration.doc.mjs +3 -2
- package/api/json/assertResponse.doc.mjs +1 -0
- package/api/json/envelope-types.test.mjs +76 -0
- package/api/json/index.ts +2 -0
- package/api/json/isError.doc.mjs +1 -0
- package/api/json/parseResponse.doc.mjs +3 -2
- package/api/layout/_adapter.mjs +20 -5
- package/api/layout/expand/expand.mjs +7 -5
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.mjs +2 -1
- package/api/layout/layoutCheck.doc.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +2 -1
- package/api/layout/layoutGrammar.doc.mjs +1 -0
- package/api/search/search-return-type.test.mjs +54 -0
- package/api/search/search.d.mts +62 -11
- package/api/search/search.doc.mjs +8 -2
- package/api/search/search.mjs +471 -83
- package/api/search/search.test.mjs +124 -1
- package/api/search/search.type.d.mts +15 -3
- package/api/search/search.type.mjs +5 -2
- package/api/swizzle/copy/copy.mjs +28 -11
- package/api/swizzle/swizzle.doc.mjs +2 -1
- package/api/swizzle/swizzle.type.d.mts +2 -2
- package/api/swizzle/swizzle.type.mjs +2 -2
- package/api/template/copy/copy.mjs +17 -23
- package/api/template/copy/copy.test.mjs +17 -0
- package/api/template/list/list.mjs +1 -0
- package/api/template/table-floating-bulk-actions.test.mjs +66 -0
- package/api/template/template-integration.test.mjs +1072 -3
- package/api/template/template-suffix.test.mjs +41 -21
- package/api/template/template.d.mts +1 -1
- package/api/template/template.doc.mjs +30 -8
- package/api/template/template.mjs +45 -8
- package/api/template/template.type.d.mts +12 -14
- package/api/template/template.type.mjs +15 -14
- package/api/theme/_adapter.d.mts +2 -3
- package/api/theme/_adapter.mjs +4 -5
- package/api/theme/add/add.binary.test.mjs +84 -0
- package/api/theme/add/add.mjs +31 -22
- package/api/theme/add/add.rollback.test.mjs +158 -0
- package/api/theme/add/add.staging.test.mjs +83 -0
- package/api/theme/add/add.test.mjs +14 -1
- package/api/theme/build/build.family.test.mjs +7 -12
- package/api/theme/build/build.mjs +140 -59
- package/api/theme/build/build.public-component-vars.test.mjs +1 -1
- package/api/theme/build/build.receipt-doc.test.mjs +111 -0
- package/api/theme/build/build.rollback.test.mjs +148 -0
- package/api/theme/build/build.test.mjs +127 -0
- package/api/theme/build/font-warning.mjs +3 -3
- package/api/theme/build/font-warning.test.mjs +5 -2
- package/api/theme/generateTonalPalette.doc.mjs +1 -0
- package/api/theme/integration-themes.test.mjs +39 -28
- package/api/theme/list/list.test.mjs +19 -20
- package/api/theme/listThemes.doc.mjs +6 -5
- package/api/theme/palette/generate/generate.mjs +8 -3
- package/api/theme/palette/generate/generate.test.mjs +96 -0
- package/api/theme/palette/generate/generator.d.mts +10 -13
- package/api/theme/palette/generate/generator.mjs +15 -4
- package/api/theme/palette/generate/generator.test.mjs +10 -0
- package/api/theme/template/template.mjs +11 -2
- package/api/theme/template/template.test.mjs +20 -0
- package/api/theme/theme.type.d.mts +170 -11
- package/api/theme/theme.type.mjs +94 -27
- package/api/theme/themeAdd.doc.mjs +4 -3
- package/api/theme/themeBuild.doc.mjs +8 -4
- package/api/theme/themeList.doc.mjs +6 -3
- package/api/theme/themeListAvailable.doc.mjs +5 -3
- package/api/theme/themePaletteGenerate.doc.mjs +1 -0
- package/api/theme/themeTargets.doc.mjs +1 -0
- package/api/theme/themeTemplate.doc.mjs +6 -2
- package/api/upgrade/_adapter.d.mts +32 -5
- package/api/upgrade/_adapter.mjs +139 -22
- package/api/upgrade/list/list.mjs +2 -1
- package/api/upgrade/list/list.test.mjs +73 -0
- package/api/upgrade/project-context.test.mjs +272 -0
- package/api/upgrade/provider-agreement.test.mjs +152 -0
- package/api/upgrade/run/run.mjs +356 -59
- package/api/upgrade/status/status.mjs +2 -2
- package/api/upgrade/upgrade.doc.mjs +12 -5
- package/api/upgrade/upgrade.type.d.mts +43 -5
- package/api/upgrade/upgrade.type.mjs +27 -11
- package/assets/codemods/__tests__/registry.test.mjs +1 -0
- package/assets/codemods/__tests__/runner.test.mjs +330 -8
- package/assets/codemods/integration-discovery.mjs +48 -4
- package/assets/codemods/integration-discovery.test.mjs +73 -0
- package/assets/codemods/integration-runner.mjs +56 -4
- package/assets/codemods/integration-runner.protection.test.mjs +153 -0
- package/assets/codemods/registry.mjs +1 -0
- package/assets/codemods/run-codemod.mjs +177 -34
- package/assets/codemods/runner.mjs +350 -102
- package/assets/codemods/term-log.mjs +32 -8
- package/assets/codemods/term-log.test.mjs +19 -1
- package/assets/codemods/transform-prop.mjs +109 -0
- package/assets/codemods/transform-prop.test.mjs +95 -0
- package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
- package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
- package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
- package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
- package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
- package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
- package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
- package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
- package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
- package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
- package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
- package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
- package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
- package/assets/docs/README.md +9 -0
- package/assets/docs/authoring.doc.mjs +14 -0
- package/assets/docs/getting-started.doc.mjs +2 -2
- package/assets/docs/internationalization.doc.mjs +7 -5
- package/assets/docs/layout.doc.dense.mjs +2 -2
- package/assets/docs/layout.doc.mjs +1 -1
- package/assets/docs/principles.doc.mjs +6 -6
- package/assets/docs/styling-libraries.doc.mjs +4 -4
- package/assets/docs/styling.doc.mjs +4 -4
- package/assets/docs/theme.doc.mjs +5 -5
- package/assets/docs/tokens.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +30 -0
- package/assets/docs/tree/cli.doc.mjs +23 -0
- package/assets/docs/tree/commands.doc.mjs +25 -0
- package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/working-with-ai.doc.mjs +4 -4
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
- package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
- package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
- package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
- package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
- package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
- package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
- package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
- package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
- package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
- package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
- package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
- package/assets/templates/pages/table-tree/page.tsx +1704 -0
- package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
- package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
- package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
- package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
- package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
- package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
- package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
- package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
- package/authoring/_shared/contract.ts +22 -0
- package/authoring/codemod/codemod.doc.mjs +7 -2
- package/authoring/codemod/parse.d.mts +8 -8
- package/authoring/codemod/parse.mjs +8 -6
- package/authoring/codemod/type.ts +12 -0
- package/authoring/config/config.doc.mjs +10 -2
- package/authoring/config/debug-composition.test.mjs +92 -0
- package/authoring/config/parse.d.mts +15 -13
- package/authoring/config/parse.mjs +27 -8
- package/authoring/config/parse.test.mjs +8 -0
- package/authoring/config/type.ts +29 -6
- package/authoring/debug/debug.doc.d.mts +11 -0
- package/authoring/debug/debug.doc.mjs +182 -0
- package/authoring/debug/parse.d.mts +8 -8
- package/authoring/debug/parse.mjs +3 -3
- 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 +790 -23
- package/authoring/doctypes/_schema.mjs +543 -39
- package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
- package/authoring/doctypes/base/type.ts +41 -0
- package/authoring/doctypes/command/command.doc.mjs +4 -3
- package/authoring/doctypes/command/parse.d.mts +2 -2
- package/authoring/doctypes/command/parse.mjs +1 -1
- package/authoring/doctypes/command/type.ts +5 -4
- package/authoring/doctypes/component/component.doc.mjs +6 -3
- package/authoring/doctypes/component/parse.d.mts +2 -2
- package/authoring/doctypes/component/parse.mjs +1 -1
- package/authoring/doctypes/component/type.ts +6 -5
- package/authoring/doctypes/doctypes-new.test.mjs +48 -6
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/parse.d.mts +2 -2
- package/authoring/doctypes/enum/parse.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +4 -2
- package/authoring/doctypes/function/function.doc.mjs +7 -2
- package/authoring/doctypes/function/parse.d.mts +2 -2
- package/authoring/doctypes/function/parse.mjs +1 -1
- package/authoring/doctypes/function/type.ts +9 -4
- package/authoring/doctypes/hook/hook.doc.mjs +4 -0
- package/authoring/doctypes/hook/parse.d.mts +2 -2
- package/authoring/doctypes/hook/parse.mjs +1 -1
- package/authoring/doctypes/hook/type.ts +5 -4
- package/authoring/doctypes/legacy.d.mts +8 -6
- package/authoring/doctypes/legacy.mjs +5 -4
- package/authoring/doctypes/load-contract.test.mjs +233 -0
- package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
- package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
- package/authoring/doctypes/namespace/parse.d.mts +12 -0
- package/authoring/doctypes/namespace/parse.mjs +25 -0
- package/authoring/doctypes/namespace/parse.test.mjs +163 -0
- package/authoring/doctypes/namespace/type.ts +74 -0
- package/authoring/doctypes/parse.d.mts +22 -18
- package/authoring/doctypes/parse.mjs +22 -11
- package/authoring/doctypes/parse.test.mjs +77 -3
- package/authoring/doctypes/reference/parse.d.mts +2 -2
- package/authoring/doctypes/reference/parse.mjs +8 -5
- package/authoring/doctypes/reference/reference.doc.mjs +48 -6
- package/authoring/doctypes/reference/type.ts +70 -7
- package/authoring/doctypes/schema/parse.d.mts +2 -2
- package/authoring/doctypes/schema/parse.mjs +1 -1
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +4 -4
- package/authoring/doctypes/template/parse.d.mts +94 -1
- package/authoring/doctypes/template/parse.mjs +40 -2
- package/authoring/doctypes/template/parse.test.mjs +26 -2
- package/authoring/doctypes/template/template.doc.mjs +13 -3
- package/authoring/doctypes/template/type.ts +13 -2
- package/authoring/doctypes/theme/parse.d.mts +35 -0
- package/authoring/doctypes/theme/parse.mjs +76 -0
- package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
- package/authoring/doctypes/theme/theme.doc.mjs +79 -0
- package/authoring/doctypes/theme/type.ts +42 -0
- package/authoring/doctypes/types.ts +12 -10
- package/authoring/gap-report/gap-report.doc.d.mts +12 -0
- package/authoring/gap-report/gap-report.doc.mjs +183 -0
- package/authoring/gap-report/parse.d.mts +10 -10
- package/authoring/gap-report/parse.mjs +6 -6
- package/authoring/gap-report/type.ts +1 -1
- package/authoring/identity/identity.doc.d.mts +9 -0
- package/authoring/identity/identity.doc.mjs +61 -0
- package/authoring/identity/type.ts +132 -0
- package/authoring/index.d.mts +3 -0
- package/authoring/index.d.ts +62 -17
- package/authoring/index.mjs +4 -1
- package/authoring/integration/integration.doc.mjs +15 -8
- package/authoring/integration/parse.d.mts +2 -2
- package/authoring/integration/parse.mjs +1 -1
- package/authoring/integration/parse.test.mjs +10 -1
- package/authoring/integration/schema.d.mts +6 -4
- package/authoring/integration/schema.mjs +9 -3
- package/authoring/integration/type.ts +19 -8
- package/authoring/shadcn/receipt.d.mts +6 -6
- package/clients/cli/__tests__/cliManifest.test.ts +27 -29
- package/clients/cli/command-load-failure.test.mjs +83 -0
- package/clients/cli/commands/blog.doc.mjs +1 -1
- package/clients/cli/commands/blog.mjs +23 -8
- package/clients/cli/commands/blog.test.mjs +42 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
- package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
- package/clients/cli/commands/build-theme.mjs +16 -50
- package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
- package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
- package/clients/cli/commands/build.doc.mjs +16 -8
- package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
- package/clients/cli/commands/build.mjs +137 -114
- package/clients/cli/commands/build.playbook.test.mjs +75 -0
- package/clients/cli/commands/build.text-fields.test.mjs +81 -0
- package/clients/cli/commands/component/index.mjs +3 -8
- package/clients/cli/commands/component-ownership.test.mjs +3 -3
- package/clients/cli/commands/component-package.test.mjs +46 -0
- package/clients/cli/commands/component-resolution.test.mjs +21 -0
- package/clients/cli/commands/component.doc.mjs +1 -1
- package/clients/cli/commands/component.test.mjs +19 -0
- package/clients/cli/commands/detail-levels.test.mjs +2 -2
- package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
- package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
- package/clients/cli/commands/discover.doc.mjs +55 -9
- package/clients/cli/commands/discover.mjs +393 -118
- package/clients/cli/commands/discover.sources.test.mjs +267 -0
- package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
- package/clients/cli/commands/docs.doc.mjs +28 -6
- package/clients/cli/commands/docs.mjs +240 -26
- package/clients/cli/commands/docs.test.mjs +193 -1
- package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
- package/clients/cli/commands/doctor-integration.test.mjs +90 -8
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +59 -32
- package/clients/cli/commands/doctor.test.mjs +42 -0
- package/clients/cli/commands/gap-report.doc.mjs +17 -6
- package/clients/cli/commands/gap-report.test.mjs +72 -0
- package/clients/cli/commands/hook/index.mjs +7 -17
- package/clients/cli/commands/hook.doc.mjs +1 -1
- package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
- package/clients/cli/commands/init.doc.mjs +20 -9
- package/clients/cli/commands/init.flag-help.test.mjs +153 -0
- package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
- package/clients/cli/commands/integration-add.doc.mjs +32 -6
- package/clients/cli/commands/integration-authoring.test.mjs +13 -9
- package/clients/cli/commands/integration-pack.doc.mjs +1 -1
- package/clients/cli/commands/integration-real-world.test.mjs +3 -9
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/integration.mjs +1 -0
- package/clients/cli/commands/interactive-guard.test.mjs +101 -24
- package/clients/cli/commands/json-contract.test.mjs +33 -0
- package/clients/cli/commands/layout-check.doc.mjs +15 -4
- package/clients/cli/commands/layout-expand.doc.mjs +22 -5
- package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
- package/clients/cli/commands/layout.doc.mjs +3 -3
- package/clients/cli/commands/layout.mjs +21 -9
- 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/no-prompt-wording.test.mjs +94 -0
- package/clients/cli/commands/search.doc.mjs +7 -4
- package/clients/cli/commands/search.mjs +28 -9
- package/clients/cli/commands/search.test.mjs +75 -0
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/swizzle.doc.mjs +3 -2
- package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
- package/clients/cli/commands/template.doc.mjs +52 -13
- package/clients/cli/commands/template.flag-help.test.mjs +117 -0
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/commands/template.path-help.test.mjs +40 -0
- package/clients/cli/commands/text-json-parity.test.mjs +719 -0
- package/clients/cli/commands/theme-add.doc.mjs +4 -3
- package/clients/cli/commands/theme-build.doc.mjs +8 -7
- package/clients/cli/commands/theme-list.doc.mjs +2 -2
- package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
- package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
- package/clients/cli/commands/theme-palette.doc.mjs +1 -1
- package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
- package/clients/cli/commands/theme-targets.doc.mjs +1 -1
- package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
- package/clients/cli/commands/theme-template.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
- package/clients/cli/commands/upgrade.doc.mjs +22 -10
- package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
- package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
- package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
- package/clients/cli/commands/upgrade.mjs +29 -7
- package/clients/cli/formatters/index.mjs +164 -1
- package/clients/cli/formatters/index.test.mjs +97 -0
- package/clients/cli/index.mjs +21 -30
- package/clients/cli/latest-version-env.test.mjs +50 -0
- package/clients/cli/lib/cli-error.test.mjs +7 -0
- package/clients/cli/lib/component-format.mjs +9 -9
- package/clients/cli/lib/component-format.test.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +32 -6
- package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
- package/clients/cli/lib/exit-codes.test.mjs +97 -0
- package/clients/cli/lib/hook-format.mjs +19 -10
- package/clients/cli/lib/json-shim.mjs +38 -2
- package/clients/cli/lib/json-shim.test.mjs +83 -0
- package/clients/cli/lib/manifest.d.ts +2 -0
- package/clients/cli/lib/manifest.mjs +37 -2
- package/clients/cli/lib/manifest.test.mjs +17 -0
- package/foundation/agent-docs/agent-docs.d.mts +7 -2
- package/foundation/agent-docs/agent-docs.mjs +82 -12
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
- package/foundation/agent-docs/agent-docs.test.mjs +19 -1
- package/foundation/config/integration-debug.test.mjs +28 -3
- package/foundation/config/project-themes.test.mjs +11 -19
- package/foundation/config/project.d.mts +20 -11
- package/foundation/config/project.mjs +263 -91
- package/foundation/config/project.test.mjs +270 -21
- package/foundation/discovery/authoring-self-docs.d.mts +87 -0
- package/foundation/discovery/authoring-self-docs.mjs +237 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +170 -0
- package/foundation/discovery/authoring-surface.d.mts +74 -0
- package/foundation/discovery/authoring-surface.mjs +525 -0
- package/foundation/discovery/authoring-surface.test.mjs +392 -0
- package/foundation/discovery/cli-self-docs.d.mts +119 -0
- package/foundation/discovery/cli-self-docs.mjs +490 -0
- package/foundation/discovery/cli-self-docs.test.mjs +375 -0
- package/foundation/discovery/component-discovery.d.mts +39 -1
- package/foundation/discovery/component-discovery.mjs +50 -1
- package/foundation/discovery/component-loader.d.mts +35 -38
- package/foundation/discovery/component-loader.mjs +53 -222
- package/foundation/discovery/docs-discovery.d.mts +119 -11
- package/foundation/discovery/docs-discovery.mjs +423 -108
- package/foundation/discovery/docs-discovery.test.mjs +365 -20
- package/foundation/discovery/docs-output-budget.d.mts +28 -0
- package/foundation/discovery/docs-output-budget.mjs +50 -0
- package/foundation/discovery/docs-section-key.d.mts +116 -0
- package/foundation/discovery/docs-section-key.mjs +322 -0
- package/foundation/discovery/docs-section-key.test.mjs +246 -0
- package/foundation/discovery/template-adapter.d.mts +113 -11
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
- package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
- package/foundation/discovery/template-adapter.mjs +774 -83
- package/foundation/discovery/template-adapter.test.mjs +57 -0
- package/foundation/discovery/template-conflict-release.d.mts +13 -0
- package/foundation/discovery/template-conflict-release.mjs +40 -0
- package/foundation/discovery/template-conflict-release.test.mjs +40 -0
- package/foundation/discovery/theme-discovery.d.mts +67 -7
- package/foundation/discovery/theme-discovery.mjs +916 -186
- package/foundation/discovery/theme-discovery.test.mjs +613 -219
- package/foundation/discovery/theming-targets.test.mjs +4 -0
- package/foundation/doc-compiler/bundle.d.mts +47 -0
- package/foundation/doc-compiler/bundle.mjs +278 -0
- package/foundation/doc-compiler/bundle.test.mjs +266 -0
- package/foundation/doc-compiler/compile.d.mts +343 -0
- package/foundation/doc-compiler/compile.mjs +558 -0
- package/foundation/doc-compiler/diagnostics.d.mts +126 -0
- package/foundation/doc-compiler/diagnostics.mjs +305 -0
- package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
- package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
- package/foundation/doc-compiler/import.d.mts +24 -0
- package/foundation/doc-compiler/import.mjs +59 -0
- package/foundation/doc-compiler/inputs.d.mts +102 -0
- package/foundation/doc-compiler/inputs.mjs +291 -0
- package/foundation/doc-compiler/inputs.test.mjs +299 -0
- package/foundation/doc-compiler/ir.d.mts +22 -0
- package/foundation/doc-compiler/ir.mjs +471 -0
- package/foundation/doc-compiler/lenses.d.mts +36 -0
- package/foundation/doc-compiler/lenses.mjs +173 -0
- package/foundation/doc-compiler/links.d.mts +162 -0
- package/foundation/doc-compiler/links.mjs +294 -0
- package/foundation/doc-compiler/links.test.mjs +192 -0
- package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
- package/foundation/doc-compiler/overlays.d.mts +37 -0
- package/foundation/doc-compiler/overlays.mjs +206 -0
- package/foundation/doc-compiler/parse-readable.d.mts +9 -0
- package/foundation/doc-compiler/parse-readable.mjs +29 -0
- package/foundation/doc-compiler/read.d.mts +127 -0
- package/foundation/doc-compiler/read.mjs +325 -0
- package/foundation/doc-compiler/read.test.mjs +313 -0
- package/foundation/doc-compiler/source.d.mts +33 -0
- package/foundation/doc-compiler/source.mjs +128 -0
- package/foundation/doc-compiler/tree.d.mts +288 -0
- package/foundation/doc-compiler/tree.mjs +876 -0
- package/foundation/doc-compiler/tree.test.mjs +598 -0
- package/foundation/fs/file-protection.d.mts +33 -0
- package/foundation/fs/file-protection.mjs +825 -0
- package/foundation/fs/file-protection.test.mjs +250 -0
- package/foundation/fs/module-loader.d.mts +1 -0
- package/foundation/fs/module-loader.mjs +50 -1
- package/foundation/fs/module-loader.stdout.test.mjs +332 -0
- package/foundation/fs/path-safety.d.mts +3 -2
- package/foundation/fs/path-safety.mjs +49 -19
- package/foundation/fs/path-safety.test.mjs +50 -0
- package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
- package/foundation/identity/provider-identity.d.mts +90 -0
- package/foundation/identity/provider-identity.mjs +320 -0
- package/foundation/identity/provider-identity.test.mjs +254 -0
- package/foundation/identity/providers.d.mts +7 -0
- package/foundation/identity/providers.mjs +16 -0
- package/foundation/integrations/autolink.d.mts +58 -1
- package/foundation/integrations/autolink.mjs +143 -45
- package/foundation/integrations/autolink.test.mjs +1 -1
- package/foundation/integrations/cli-requirement.d.mts +45 -0
- package/foundation/integrations/cli-requirement.mjs +154 -0
- package/foundation/integrations/cli-requirement.test.mjs +84 -0
- package/foundation/integrations/contribution-fixes.d.mts +145 -0
- package/foundation/integrations/contribution-fixes.mjs +1284 -0
- package/foundation/integrations/contribution-inventory.d.mts +3 -2
- package/foundation/integrations/contribution-inventory.mjs +27 -24
- package/foundation/integrations/contribution-inventory.test.mjs +86 -27
- package/foundation/integrations/integration-warnings.d.mts +9 -2
- package/foundation/integrations/integration-warnings.mjs +52 -21
- package/foundation/integrations/integration-warnings.test.mjs +74 -1
- package/foundation/integrations/integrations.d.mts +63 -3
- package/foundation/integrations/integrations.mjs +122 -9
- package/foundation/integrations/integrations.test.mjs +415 -1
- package/foundation/integrations/provider-conflicts.test.mjs +125 -0
- package/foundation/integrations/provider-ledger.test.mjs +275 -0
- package/foundation/integrations/provider-resolution.d.mts +152 -0
- package/foundation/integrations/provider-resolution.mjs +576 -0
- package/foundation/integrations/provider-resolution.test.mjs +369 -0
- package/foundation/integrations/theme-descriptor.d.mts +8 -0
- package/foundation/integrations/theme-descriptor.mjs +44 -0
- package/foundation/integrations/validate-contributions.d.mts +2 -0
- package/foundation/integrations/validate-contributions.mjs +131 -29
- package/foundation/response/base.d.ts +8 -4
- package/foundation/response/error-codes.d.mts +3 -1
- package/foundation/response/error-codes.d.ts +2 -0
- package/foundation/response/error-codes.doc.mjs +13 -4
- package/foundation/response/error-codes.mjs +8 -2
- package/foundation/response/error-codes.test.mjs +137 -10
- package/foundation/response/json-contract.test.mjs +57 -17
- package/foundation/response/json.d.mts +4 -2
- package/foundation/response/json.mjs +8 -10
- package/foundation/response/response-types.doc.d.mts +5 -1
- package/foundation/response/response-types.doc.mjs +41 -21
- package/foundation/response/response-types.doc.test.mjs +158 -0
- package/foundation/response/response.doc.mjs +1 -1
- package/foundation/text/string-utils.d.mts +8 -0
- package/foundation/text/string-utils.mjs +40 -10
- package/foundation/xle/expand.d.mts +2 -0
- package/foundation/xle/expand.mjs +4 -3
- package/foundation/xle/expand.test.mjs +54 -0
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +10 -11
- package/assets/templates/themes/manifest.json +0 -95
- package/clients/cli/lib/update-check.mjs +0 -83
- package/clients/cli/lib/update-check.test.mjs +0 -137
- package/clients/cli/update-hint-commands.test.mjs +0 -54
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file `astryx upgrade` documents how its flags interact and when it exits 1,
|
|
5
|
+
* and behaves as documented. Option text is read from the generated manifest
|
|
6
|
+
* (the same strings `--help` prints); exit codes from the CommandDoc.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
|
+
import * as os from 'node:os';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import {runCli} from '../../../test-utils/run-cli.mjs';
|
|
14
|
+
import {doc as upgradeDoc} from './upgrade.doc.mjs';
|
|
15
|
+
|
|
16
|
+
const SLOW = 30_000;
|
|
17
|
+
|
|
18
|
+
/** @type {string} */
|
|
19
|
+
let tmpDir;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-upgrade-flags-'));
|
|
23
|
+
fs.writeFileSync(path.join(tmpDir, 'package.json'), JSON.stringify({name: 'consumer'}));
|
|
24
|
+
fs.mkdirSync(path.join(tmpDir, 'src'));
|
|
25
|
+
fs.writeFileSync(path.join(tmpDir, 'src', 'index.ts'), 'const foo = 1;\n');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/** @param {string} name @param {string} version */
|
|
33
|
+
function installPackage(name, version) {
|
|
34
|
+
const dir = path.join(tmpDir, 'node_modules', ...name.split('/'));
|
|
35
|
+
fs.mkdirSync(dir, {recursive: true});
|
|
36
|
+
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({name, version}));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** @param {string} rel @param {string} body */
|
|
40
|
+
function write(rel, body) {
|
|
41
|
+
fs.mkdirSync(path.dirname(path.join(tmpDir, rel)), {recursive: true});
|
|
42
|
+
fs.writeFileSync(path.join(tmpDir, rel), body);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* upgrade's option descriptions from the manifest, keyed by long flag.
|
|
47
|
+
* @returns {Promise<Record<string, string>>}
|
|
48
|
+
*/
|
|
49
|
+
async function upgradeOptions() {
|
|
50
|
+
const {stdout} = await runCli(['--json', 'manifest'], tmpDir);
|
|
51
|
+
const upgrade = JSON.parse(stdout).data.commands.find(
|
|
52
|
+
(/** @type {{name: string}} */ c) => c.name === 'upgrade',
|
|
53
|
+
);
|
|
54
|
+
return Object.fromEntries(
|
|
55
|
+
upgrade.options.map((/** @type {{flag: string, description: string}} */ o) => [
|
|
56
|
+
o.flag.match(/--[a-z-]+/)?.[0],
|
|
57
|
+
o.description,
|
|
58
|
+
]),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** @param {string[]} args */
|
|
63
|
+
async function upgradeJson(args) {
|
|
64
|
+
const result = await runCli(['--json', 'upgrade', ...args], tmpDir);
|
|
65
|
+
return {status: result.status, envelope: JSON.parse(result.stdout)};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const exit1 = () => upgradeDoc.exitCodes?.find(e => e.code === 1)?.when ?? '';
|
|
69
|
+
|
|
70
|
+
const MIGRATION_FLAGS = [
|
|
71
|
+
['--from', '0.5.0'],
|
|
72
|
+
['--force'],
|
|
73
|
+
['--codemod', 'x'],
|
|
74
|
+
['--skip-codemod', 'x'],
|
|
75
|
+
['--integration', 'x'],
|
|
76
|
+
['--install-deps'],
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
describe('astryx upgrade flag interactions', () => {
|
|
80
|
+
it('--list: help says it ignores every other flag but --registry, and it does', async () => {
|
|
81
|
+
const options = await upgradeOptions();
|
|
82
|
+
expect(options['--list']).toMatch(/Every other flag is ignored, except --registry/);
|
|
83
|
+
|
|
84
|
+
installPackage('@astryxdesign/core', '0.6.0');
|
|
85
|
+
const listed = await upgradeJson([
|
|
86
|
+
'--list',
|
|
87
|
+
'--from',
|
|
88
|
+
'bogus',
|
|
89
|
+
'--codemod',
|
|
90
|
+
'bogus',
|
|
91
|
+
'--apply',
|
|
92
|
+
'--path',
|
|
93
|
+
'../outside',
|
|
94
|
+
]);
|
|
95
|
+
expect(listed.status).toBe(0);
|
|
96
|
+
expect(listed.envelope.type).toBe('upgrade.list');
|
|
97
|
+
|
|
98
|
+
const refused = await upgradeJson(['--list', '--registry']);
|
|
99
|
+
expect(refused.status).toBe(1);
|
|
100
|
+
expect(refused.envelope.code).toBe('ERR_INVALID_ARGUMENT');
|
|
101
|
+
}, SLOW);
|
|
102
|
+
|
|
103
|
+
it('--registry: help names every flag it refuses, and each exits 1', async () => {
|
|
104
|
+
const options = await upgradeOptions();
|
|
105
|
+
for (const [flag] of MIGRATION_FLAGS) expect(options['--registry']).toContain(flag);
|
|
106
|
+
expect(options['--registry']).toContain('--list');
|
|
107
|
+
expect(options['--registry']).toContain('ERR_INVALID_ARGUMENT');
|
|
108
|
+
|
|
109
|
+
for (const flag of MIGRATION_FLAGS) {
|
|
110
|
+
const {status, envelope} = await upgradeJson(['--registry', ...flag]);
|
|
111
|
+
expect(status, flag[0]).toBe(1);
|
|
112
|
+
expect(envelope.code, flag[0]).toBe('ERR_INVALID_ARGUMENT');
|
|
113
|
+
}
|
|
114
|
+
}, SLOW);
|
|
115
|
+
|
|
116
|
+
it('--codemod: help says it runs optional codemods and skips the composition check, and it does', async () => {
|
|
117
|
+
const options = await upgradeOptions();
|
|
118
|
+
expect(options['--codemod']).toMatch(/Optional codemods run only when named here/);
|
|
119
|
+
expect(options['--codemod']).toMatch(/skips the check of ShadCN-copied compositions/);
|
|
120
|
+
|
|
121
|
+
installPackage('@astryxdesign/core', '0.0.15');
|
|
122
|
+
// An unreadable receipt leaves a registry item unresolved.
|
|
123
|
+
write('src/components/.astryx/broken.json', '{}');
|
|
124
|
+
|
|
125
|
+
const normal = await upgradeJson(['--from', '0.0.14']);
|
|
126
|
+
expect(normal.status).toBe(1);
|
|
127
|
+
expect(normal.envelope.data.registryCompositions.invalid).toBe(1);
|
|
128
|
+
|
|
129
|
+
// 0.0.15 ships one optional codemod, which only runs when named.
|
|
130
|
+
const named = await upgradeJson(['--from', '0.0.14', '--codemod', 'migrate-theme-selectors-to-data-attrs']);
|
|
131
|
+
expect(named.status).toBe(0);
|
|
132
|
+
expect(named.envelope.data.codemods).toBe(1);
|
|
133
|
+
expect(named.envelope.data.registryCompositions).toBeUndefined();
|
|
134
|
+
expect(normal.envelope.data.codemods).toBe(6);
|
|
135
|
+
}, SLOW);
|
|
136
|
+
|
|
137
|
+
it('--from: help names the legacy @xds/core target, which upgrade falls back to', async () => {
|
|
138
|
+
const options = await upgradeOptions();
|
|
139
|
+
expect(options['--from']).toMatch(/legacy @xds\/core/);
|
|
140
|
+
|
|
141
|
+
installPackage('@xds/core', '0.0.20');
|
|
142
|
+
const {status, envelope} = await upgradeJson(['--from', '0.0.19']);
|
|
143
|
+
expect(status).toBe(0);
|
|
144
|
+
expect(envelope.data.to).toBe('0.0.20');
|
|
145
|
+
}, SLOW);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('astryx upgrade exit codes', () => {
|
|
149
|
+
it('lists a missing core, which exits 1 in both modes', async () => {
|
|
150
|
+
expect(exit1()).toMatch(/no installed @astryxdesign\/core/);
|
|
151
|
+
const text = await runCli(['upgrade', '--from', '0.5.0'], tmpDir);
|
|
152
|
+
const json = await upgradeJson(['--from', '0.5.0']);
|
|
153
|
+
expect(text.status).toBe(1);
|
|
154
|
+
expect(json.status).toBe(1);
|
|
155
|
+
expect(json.envelope.code).toBe('ERR_VERSION_DETECT');
|
|
156
|
+
}, SLOW);
|
|
157
|
+
|
|
158
|
+
it('lists an astryx.config no codemod repairs, which exits 1', async () => {
|
|
159
|
+
expect(exit1()).toMatch(/astryx\.config that fails validation/);
|
|
160
|
+
installPackage('@astryxdesign/core', '0.6.0');
|
|
161
|
+
write('astryx.config.mjs', "export default {theme: 'neutral'};\n");
|
|
162
|
+
const {status, envelope} = await upgradeJson(['--from', '0.5.0']);
|
|
163
|
+
expect(status).toBe(1);
|
|
164
|
+
expect(envelope.code).toBe('ERR_INVALID_ARGUMENT');
|
|
165
|
+
}, SLOW);
|
|
166
|
+
|
|
167
|
+
it('lists a post-codemod hook failure, which exits 1', async () => {
|
|
168
|
+
expect(exit1()).toMatch(/post-codemod hook failure/);
|
|
169
|
+
installPackage('@astryxdesign/core', '0.6.0');
|
|
170
|
+
write(
|
|
171
|
+
'src/panel.tsx',
|
|
172
|
+
"import {useResizable} from '@astryxdesign/core';\nexport const r = () => useResizable({minSizePx: 200});\n",
|
|
173
|
+
);
|
|
174
|
+
write(
|
|
175
|
+
'astryx.config.mjs',
|
|
176
|
+
"export default {hooks: {postCodemod: [{buildCommand: () => { throw new Error('formatter crashed'); }}]}};\n",
|
|
177
|
+
);
|
|
178
|
+
const {status, envelope} = await upgradeJson(['--from', '0.5.0']);
|
|
179
|
+
expect(status).toBe(1);
|
|
180
|
+
expect(envelope.code).toBe('ERR_CODEMOD_FAILED');
|
|
181
|
+
expect(envelope.error).toMatch(/Post-codemod hook failed/);
|
|
182
|
+
}, SLOW);
|
|
183
|
+
|
|
184
|
+
it('lists a missing jscodeshift and the refused flag combinations', () => {
|
|
185
|
+
expect(exit1()).toMatch(/jscodeshift missing and not installed/);
|
|
186
|
+
expect(exit1()).toMatch(/--registry with --list or a migration flag/);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Subprocess test: a post-codemod hook that prints must not corrupt the
|
|
5
|
+
* `upgrade --json` envelope. Stdout carries exactly one envelope; the hook's
|
|
6
|
+
* output goes to stderr.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {describe, it, expect, beforeEach, afterEach} from 'vitest';
|
|
10
|
+
import {spawnSync} from 'node:child_process';
|
|
11
|
+
import * as fs from 'node:fs';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import * as os from 'node:os';
|
|
14
|
+
import {fileURLToPath} from 'node:url';
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const CLI = path.resolve(__dirname, '..', 'bin', 'astryx.mjs');
|
|
18
|
+
|
|
19
|
+
let tmpDir;
|
|
20
|
+
|
|
21
|
+
/** @param {string} rel @param {string} body */
|
|
22
|
+
function write(rel, body) {
|
|
23
|
+
const file = path.join(tmpDir, rel);
|
|
24
|
+
fs.mkdirSync(path.dirname(file), {recursive: true});
|
|
25
|
+
fs.writeFileSync(file, body);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** @param {string[]} args */
|
|
29
|
+
function runCli(args) {
|
|
30
|
+
return spawnSync(process.execPath, [CLI, ...args], {
|
|
31
|
+
cwd: tmpDir,
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
timeout: 60_000,
|
|
34
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
35
|
+
env: {...process.env, FORCE_COLOR: '0', CI: ''},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-upgrade-hook-output-'));
|
|
41
|
+
write('package.json', JSON.stringify({name: 'consumer', private: true}));
|
|
42
|
+
write(
|
|
43
|
+
'node_modules/@astryxdesign/core/package.json',
|
|
44
|
+
JSON.stringify({name: '@astryxdesign/core', version: '0.6.0'}),
|
|
45
|
+
);
|
|
46
|
+
write(
|
|
47
|
+
'src/panel.tsx',
|
|
48
|
+
"import {useResizable} from '@astryxdesign/core';\n" +
|
|
49
|
+
'export const usePanel = () => useResizable({defaultSize: 250, minSizePx: 200});\n',
|
|
50
|
+
);
|
|
51
|
+
write(
|
|
52
|
+
'astryx.config.mjs',
|
|
53
|
+
'export default {hooks: {postCodemod: [{name: "noisy", buildCommand: ({files}) => {\n' +
|
|
54
|
+
' console.log("hook saw " + files.join(","));\n' +
|
|
55
|
+
' process.stdout.write("hook raw write\\n");\n' +
|
|
56
|
+
' return null;\n' +
|
|
57
|
+
'}}]}};\n',
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('upgrade post-codemod hook output', () => {
|
|
66
|
+
it('keeps --json stdout to one envelope and sends hook output to stderr', () => {
|
|
67
|
+
const r = runCli([
|
|
68
|
+
'--json',
|
|
69
|
+
'upgrade',
|
|
70
|
+
'--from',
|
|
71
|
+
'0.5.0',
|
|
72
|
+
'--codemod',
|
|
73
|
+
'rename-resizable-pixel-bounds',
|
|
74
|
+
'--apply',
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
expect(r.signal).toBeNull();
|
|
78
|
+
expect(r.status).toBe(0);
|
|
79
|
+
const envelope = JSON.parse(r.stdout);
|
|
80
|
+
expect(envelope.type).toBe('upgrade.run');
|
|
81
|
+
expect(envelope.data.filesChanged).toBe(1);
|
|
82
|
+
expect(r.stderr).toContain('hook saw src/panel.tsx');
|
|
83
|
+
expect(r.stderr).toContain('hook raw write');
|
|
84
|
+
expect(fs.readFileSync(path.join(tmpDir, 'src/panel.tsx'), 'utf8')).toContain(
|
|
85
|
+
'minSize: 200',
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import {jsonOut, jsonError} from '../../../foundation/response/json.mjs';
|
|
16
|
+
import {emit, section, records} from '../formatters/index.mjs';
|
|
16
17
|
import {logger} from '../../../api/logger.mjs';
|
|
17
18
|
import {AstryxError} from '../../../api/error.mjs';
|
|
18
19
|
import {defineCommand} from '../lib/define-command.mjs';
|
|
@@ -33,27 +34,45 @@ export function registerUpgrade(program) {
|
|
|
33
34
|
*/
|
|
34
35
|
async options => {
|
|
35
36
|
const json = program.opts().json || false;
|
|
36
|
-
|
|
37
|
+
const isList = Boolean(options.list);
|
|
38
|
+
// Silence the logger for --list so the handler renders from the
|
|
39
|
+
// result (parity with --json), and for --json as before.
|
|
40
|
+
// Re-enable on error so handled failures still print.
|
|
41
|
+
logger.setSilent(json || isList);
|
|
37
42
|
|
|
38
43
|
/** @type {import('../../../api/upgrade/upgrade.type.mjs').UpgradeListResponse | import('../../../api/upgrade/upgrade.type.mjs').UpgradeRegistryResponse | import('../../../api/upgrade/upgrade.type.mjs').UpgradeStatusResponse | import('../../../api/upgrade/upgrade.type.mjs').UpgradeRunResponse} */
|
|
39
44
|
let result;
|
|
40
45
|
try {
|
|
41
46
|
result = await upgradeApi(options, {cwd: process.cwd()});
|
|
42
47
|
} catch (e) {
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
// top-level boundary in bin/astryx.mjs format it as before.
|
|
48
|
+
// Re-enable the logger so the error is visible in text mode.
|
|
49
|
+
// For --list the logger was silent; for other modes the API
|
|
50
|
+
// already printed the error before throwing.
|
|
51
|
+
logger.setSilent(json);
|
|
48
52
|
if (e instanceof AstryxError) {
|
|
49
53
|
if (json) jsonError(e.message, undefined, e.code);
|
|
50
|
-
else
|
|
54
|
+
else {
|
|
55
|
+
// The logger was silent only for --list; replay the error there.
|
|
56
|
+
// Non-list failures were already printed by the API.
|
|
57
|
+
if (isList) logger.error(e.message);
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
51
60
|
return NO_RESULT_SET;
|
|
52
61
|
}
|
|
53
62
|
throw e;
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
if (json) jsonOut(result);
|
|
66
|
+
else if (result.type === 'upgrade.list') {
|
|
67
|
+
// Render the list from the result, not the API logger, so the text
|
|
68
|
+
// carries the same fields the JSON does (name, title, version, optional).
|
|
69
|
+
emit(
|
|
70
|
+
section('Available codemods'),
|
|
71
|
+
records(result.data, {
|
|
72
|
+
fields: ['name', 'title', 'version', 'optional'],
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
}
|
|
57
76
|
|
|
58
77
|
const registrySummary =
|
|
59
78
|
result.type === 'upgrade.registry'
|
|
@@ -65,6 +84,9 @@ export function registerUpgrade(program) {
|
|
|
65
84
|
? result.data.registryCompositions
|
|
66
85
|
: undefined;
|
|
67
86
|
if (registrySummary && !registrySummary.ok) process.exitCode = 1;
|
|
87
|
+
if (result.type === 'upgrade.run' && result.data.complete === false) {
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
}
|
|
68
90
|
|
|
69
91
|
// `--list` is the one lookup here: which migrations exist for the
|
|
70
92
|
// range. Everything else migrates the project — an effect, reported by
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
* which fields to show and in what order.
|
|
12
12
|
*
|
|
13
13
|
* Constraints (deliberately narrow):
|
|
14
|
-
* - Plain ASCII only. No color
|
|
14
|
+
* - Plain ASCII only. No color and no TTY detection. Long lines wrap at a
|
|
15
|
+
* fixed {@link WRAP_WIDTH}, never at the terminal's width, so output is
|
|
15
16
|
* byte-for-byte deterministic whether printed or piped to an agent.
|
|
16
17
|
* - Renderers return an opaque {@link Block}; `emit` accepts ONLY Blocks, so a
|
|
17
18
|
* stray string can't leak onto stdout (the compiler rejects `emit('x')`).
|
|
@@ -33,6 +34,50 @@ export const BULLET = '-';
|
|
|
33
34
|
export const ERR = '!!';
|
|
34
35
|
export const WARN = '!';
|
|
35
36
|
|
|
37
|
+
/** The column long human-output lines wrap at. */
|
|
38
|
+
export const WRAP_WIDTH = 120;
|
|
39
|
+
|
|
40
|
+
/** The widest first column an inline record pads to; a longer value overhangs. */
|
|
41
|
+
const INLINE_LEAD_MAX = 32;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Characters a terminal draws two columns wide: CJK ideographs and
|
|
45
|
+
* punctuation, kana, hangul, and fullwidth forms. A line may break between
|
|
46
|
+
* any two of them.
|
|
47
|
+
*/
|
|
48
|
+
const WIDE_CHAR =
|
|
49
|
+
/[\u1100-\u115f\u2e80-\u303e\u3041-\u33ff\u3400-\u4dbf\u4e00-\u9fff\ua000-\ua4cf\uac00-\ud7a3\uf900-\ufaff\ufe30-\ufe4f\uff00-\uff60\uffe0-\uffe6]/u;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* How many terminal columns a string takes.
|
|
53
|
+
* @param {string} s
|
|
54
|
+
* @returns {number}
|
|
55
|
+
*/
|
|
56
|
+
export function displayWidth(s) {
|
|
57
|
+
let width = 0;
|
|
58
|
+
for (const ch of String(s)) width += WIDE_CHAR.test(ch) ? 2 : 1;
|
|
59
|
+
return width;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Cut a line to `width` columns, ending it with `...` when anything was cut.
|
|
64
|
+
* @param {string} line
|
|
65
|
+
* @param {number} width
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function truncateToWidth(line, width) {
|
|
69
|
+
if (displayWidth(line) <= width) return line;
|
|
70
|
+
let out = '';
|
|
71
|
+
let used = 0;
|
|
72
|
+
for (const ch of line) {
|
|
73
|
+
const w = WIDE_CHAR.test(ch) ? 2 : 1;
|
|
74
|
+
if (used + w > width - 3) break;
|
|
75
|
+
out += ch;
|
|
76
|
+
used += w;
|
|
77
|
+
}
|
|
78
|
+
return `${out.trimEnd()}...`;
|
|
79
|
+
}
|
|
80
|
+
|
|
36
81
|
/**
|
|
37
82
|
* An opaque, renderer-produced block of output. Nominal via a private field:
|
|
38
83
|
* nothing outside this file can construct one, so `emit` can trust that whatever
|
|
@@ -67,6 +112,13 @@ export class Block {
|
|
|
67
112
|
* @property {Record<string, string>} [labels] - Rename a key for display.
|
|
68
113
|
* @property {Record<string, (value: any) => string>} [format] - Transform a
|
|
69
114
|
* value before rendering (e.g. prefix a command with the package manager).
|
|
115
|
+
* @property {'stacked' | 'inline'} [layout] - `stacked` (the default): one
|
|
116
|
+
* `key: value` line per field, a blank line between records. `inline`: one
|
|
117
|
+
* record per line, the first field in a padded column and the rest joined by
|
|
118
|
+
* ` - `, for a list a reader scans.
|
|
119
|
+
* @property {'wrap' | 'truncate'} [overflow] - What an inline record longer
|
|
120
|
+
* than {@link WRAP_WIDTH} does: `wrap` (the default) continues under the
|
|
121
|
+
* first column; `truncate` cuts it to one line.
|
|
70
122
|
*/
|
|
71
123
|
|
|
72
124
|
/** @param {unknown} v @returns {boolean} */
|
|
@@ -100,9 +152,77 @@ function toAscii(s) {
|
|
|
100
152
|
.replace(/[\u2018\u2019]/g, "'")
|
|
101
153
|
.replace(/[\u201C\u201D]/g, '"')
|
|
102
154
|
.replace(/\u2026/g, '...')
|
|
155
|
+
.replace(/\u2192/g, '->')
|
|
156
|
+
.replace(/\u2190/g, '<-')
|
|
103
157
|
.replace(/\u00a0/g, ' ');
|
|
104
158
|
}
|
|
105
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Word-wrap text at `width`, keeping its own line breaks. A word longer than
|
|
162
|
+
* the width stays whole on a line of its own. Continuation lines start with
|
|
163
|
+
* `indent`.
|
|
164
|
+
* @param {string} input
|
|
165
|
+
* @param {{width?: number, indent?: string}} [options]
|
|
166
|
+
* @returns {string}
|
|
167
|
+
*/
|
|
168
|
+
export function wrapText(input, {width = WRAP_WIDTH, indent = ''} = {}) {
|
|
169
|
+
return String(input)
|
|
170
|
+
.split('\n')
|
|
171
|
+
.map(line => wrapLine(line, width, indent))
|
|
172
|
+
.join('\n');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* @param {string} line
|
|
177
|
+
* @param {number} width
|
|
178
|
+
* @param {string} indent
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
function wrapLine(line, width, indent) {
|
|
182
|
+
if (displayWidth(line) <= width) return line;
|
|
183
|
+
const lead = /^\s*/.exec(line)?.[0] ?? '';
|
|
184
|
+
// Tokens a line may break between: words, and each wide character on its
|
|
185
|
+
// own, since CJK text has no spaces to break at. `gap` is what joined a
|
|
186
|
+
// token to the one before it.
|
|
187
|
+
/** @type {{text: string, gap: string}[]} */
|
|
188
|
+
const tokens = [];
|
|
189
|
+
let gap = '';
|
|
190
|
+
let word = '';
|
|
191
|
+
const flush = () => {
|
|
192
|
+
if (word === '') return;
|
|
193
|
+
tokens.push({text: word, gap});
|
|
194
|
+
gap = '';
|
|
195
|
+
word = '';
|
|
196
|
+
};
|
|
197
|
+
for (const ch of line.slice(lead.length)) {
|
|
198
|
+
if (ch === ' ') {
|
|
199
|
+
flush();
|
|
200
|
+
gap = ' ';
|
|
201
|
+
} else if (WIDE_CHAR.test(ch)) {
|
|
202
|
+
flush();
|
|
203
|
+
tokens.push({text: ch, gap});
|
|
204
|
+
gap = '';
|
|
205
|
+
} else {
|
|
206
|
+
word += ch;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
flush();
|
|
210
|
+
/** @type {string[]} */
|
|
211
|
+
const out = [];
|
|
212
|
+
let current = lead;
|
|
213
|
+
tokens.forEach(({text: token, gap: before}, i) => {
|
|
214
|
+
const joined = i === 0 ? current + token : current + before + token;
|
|
215
|
+
if (i > 0 && displayWidth(joined) > width && current.trim() !== '') {
|
|
216
|
+
out.push(current);
|
|
217
|
+
current = indent + token;
|
|
218
|
+
} else {
|
|
219
|
+
current = joined;
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
out.push(current);
|
|
223
|
+
return out.join('\n');
|
|
224
|
+
}
|
|
225
|
+
|
|
106
226
|
/**
|
|
107
227
|
* A group label, optionally with an explanatory subtitle rendered on the line(s)
|
|
108
228
|
* directly beneath the heading (no blank line between). Whatever list/records
|
|
@@ -177,10 +297,53 @@ export function record(obj, options = {}) {
|
|
|
177
297
|
* @returns {Block}
|
|
178
298
|
*/
|
|
179
299
|
export function records(items, options = {}) {
|
|
300
|
+
if (options.layout === 'inline') return inlineRecords(items, options);
|
|
180
301
|
const blocks = items.map(o => record(o, options).toString()).filter(Boolean);
|
|
181
302
|
return new Block(blocks.join('\n\n'));
|
|
182
303
|
}
|
|
183
304
|
|
|
305
|
+
/**
|
|
306
|
+
* @param {any[]} items
|
|
307
|
+
* @param {RecordOptions} options
|
|
308
|
+
* @returns {Block}
|
|
309
|
+
*/
|
|
310
|
+
function inlineRecords(items, options) {
|
|
311
|
+
const [lead, ...rest] = (options.fields ?? Object.keys(items[0] ?? {})).filter(
|
|
312
|
+
k => !options.omit?.includes(k),
|
|
313
|
+
);
|
|
314
|
+
if (lead == null) return new Block('');
|
|
315
|
+
/** @param {any} o @param {string} k */
|
|
316
|
+
const value = (o, k) => {
|
|
317
|
+
const fmt = options.format?.[k];
|
|
318
|
+
return fmt ? fmt(o[k]) : renderValue(o[k]);
|
|
319
|
+
};
|
|
320
|
+
const leads = items.map(o => (isEmpty(o[lead]) ? '' : value(o, lead)));
|
|
321
|
+
const column = Math.min(
|
|
322
|
+
Math.max(0, ...leads.map(l => displayWidth(l))),
|
|
323
|
+
INLINE_LEAD_MAX,
|
|
324
|
+
);
|
|
325
|
+
const indent = ' '.repeat(column + 2);
|
|
326
|
+
const lines = items.map((o, i) => {
|
|
327
|
+
const tail = toAscii(
|
|
328
|
+
rest
|
|
329
|
+
.filter(k => !isEmpty(o[k]))
|
|
330
|
+
.map(k => value(o, k))
|
|
331
|
+
.join(' - '),
|
|
332
|
+
);
|
|
333
|
+
if (tail === '') return toAscii(leads[i]);
|
|
334
|
+
const head = toAscii(`${leads[i].padEnd(column)} `);
|
|
335
|
+
if (options.overflow === 'truncate') {
|
|
336
|
+
return truncateToWidth(head + tail, WRAP_WIDTH);
|
|
337
|
+
}
|
|
338
|
+
// Wrap only the tail, so the padded first column survives the wrap.
|
|
339
|
+
const [first, ...more] = wrapText(tail, {
|
|
340
|
+
width: Math.max(20, WRAP_WIDTH - displayWidth(head)),
|
|
341
|
+
}).split('\n');
|
|
342
|
+
return [head + first, ...more.map(line => indent + line)].join('\n');
|
|
343
|
+
});
|
|
344
|
+
return new Block(lines.join('\n'));
|
|
345
|
+
}
|
|
346
|
+
|
|
184
347
|
/**
|
|
185
348
|
* A verbatim block — source dumps, layout skeletons, or a markdown doc. Output
|
|
186
349
|
* is byte-for-byte (NOT ASCII-normalized), so it survives piping
|
|
@@ -8,6 +8,9 @@ import {
|
|
|
8
8
|
list,
|
|
9
9
|
record,
|
|
10
10
|
records,
|
|
11
|
+
wrapText,
|
|
12
|
+
displayWidth,
|
|
13
|
+
WRAP_WIDTH,
|
|
11
14
|
code,
|
|
12
15
|
Block,
|
|
13
16
|
BULLET,
|
|
@@ -114,6 +117,12 @@ describe('ASCII normalization', () => {
|
|
|
114
117
|
expect(list(['Avatar \u2014 Group']).toString()).toBe('- Avatar - Group');
|
|
115
118
|
});
|
|
116
119
|
|
|
120
|
+
it('converts Unicode arrows to ASCII arrows', () => {
|
|
121
|
+
expect(text('Rename items \u2192 options').toString()).toBe('Rename items -> options');
|
|
122
|
+
expect(text('options \u2190 items').toString()).toBe('options <- items');
|
|
123
|
+
expect(record({title: 'a \u2192 b'}).toString()).toBe('title: a -> b');
|
|
124
|
+
});
|
|
125
|
+
|
|
117
126
|
it('leaves code verbatim (no normalization)', () => {
|
|
118
127
|
expect(code('a \u2014 b').toString()).toBe('a \u2014 b');
|
|
119
128
|
});
|
|
@@ -148,3 +157,91 @@ describe('emit', () => {
|
|
|
148
157
|
expect(spy).not.toHaveBeenCalled();
|
|
149
158
|
});
|
|
150
159
|
});
|
|
160
|
+
|
|
161
|
+
describe('wrapText', () => {
|
|
162
|
+
it('leaves short lines and its own line breaks alone', () => {
|
|
163
|
+
expect(wrapText('a b\nc')).toBe('a b\nc');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('wraps at the width with a hanging indent', () => {
|
|
167
|
+
expect(wrapText('aaa bbb ccc', {width: 7, indent: ' '})).toBe('aaa bbb\n ccc');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('keeps a word longer than the width whole', () => {
|
|
171
|
+
expect(wrapText('x '.concat('y'.repeat(20)), {width: 10})).toBe(
|
|
172
|
+
`x\n${'y'.repeat(20)}`,
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('records inline layout', () => {
|
|
178
|
+
const items = [
|
|
179
|
+
{id: 'a', title: 'Alpha', summary: 'First.'},
|
|
180
|
+
{id: 'bbb', title: 'Beta', summary: ''},
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
it('puts one record on each line under a padded first column', () => {
|
|
184
|
+
expect(
|
|
185
|
+
records(items, {fields: ['id', 'title', 'summary'], layout: 'inline'}).toString(),
|
|
186
|
+
).toBe('a Alpha - First.\nbbb Beta');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('wraps a long record under the first column', () => {
|
|
190
|
+
const out = records([{id: 'a', text: 'word '.repeat(40).trim()}], {
|
|
191
|
+
layout: 'inline',
|
|
192
|
+
}).toString();
|
|
193
|
+
expect(out.split('\n').length).toBeGreaterThan(1);
|
|
194
|
+
expect(out.split('\n').every(line => line.length <= WRAP_WIDTH)).toBe(true);
|
|
195
|
+
expect(out.split('\n')[1].startsWith(' word')).toBe(true);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('keeps the first column padded on a record that wraps', () => {
|
|
199
|
+
const out = records(
|
|
200
|
+
[
|
|
201
|
+
{id: 'a', text: 'word '.repeat(40).trim()},
|
|
202
|
+
{id: 'longer', text: 'x'},
|
|
203
|
+
],
|
|
204
|
+
{layout: 'inline'},
|
|
205
|
+
).toString();
|
|
206
|
+
const lines = out.split('\n');
|
|
207
|
+
expect(lines[0].startsWith(`a${' '.repeat(7)}word`)).toBe(true);
|
|
208
|
+
expect(lines[1].startsWith(' '.repeat(8) + 'word')).toBe(true);
|
|
209
|
+
expect(lines.at(-1)).toBe('longer x');
|
|
210
|
+
expect(lines.every(line => line.length <= WRAP_WIDTH)).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('cuts a long record to one line when asked', () => {
|
|
214
|
+
const out = records([{id: 'a', text: 'word '.repeat(40).trim()}], {
|
|
215
|
+
layout: 'inline',
|
|
216
|
+
overflow: 'truncate',
|
|
217
|
+
}).toString();
|
|
218
|
+
expect(out.includes('\n')).toBe(false);
|
|
219
|
+
expect(out.length).toBeLessThanOrEqual(WRAP_WIDTH);
|
|
220
|
+
expect(out.endsWith('...')).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('wide characters', () => {
|
|
225
|
+
it('counts CJK characters as two columns', () => {
|
|
226
|
+
expect(displayWidth('亮/暗模式')).toBe(9);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('wraps CJK text, which has no spaces, by columns', () => {
|
|
230
|
+
const out = wrapText('中'.repeat(100), {width: 20});
|
|
231
|
+
expect(out.split('\n').every(line => displayWidth(line) <= 20)).toBe(true);
|
|
232
|
+
expect(out.split('\n')).toHaveLength(10);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('keeps spaces between Latin words next to CJK', () => {
|
|
236
|
+
expect(wrapText('运行 astryx docs 查看', {width: 200})).toBe('运行 astryx docs 查看');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('cuts an inline record to columns, not characters', () => {
|
|
240
|
+
const out = records([{id: 'a', text: '中'.repeat(100)}], {
|
|
241
|
+
layout: 'inline',
|
|
242
|
+
overflow: 'truncate',
|
|
243
|
+
}).toString();
|
|
244
|
+
expect(displayWidth(out)).toBeLessThanOrEqual(WRAP_WIDTH);
|
|
245
|
+
expect(out.endsWith('...')).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
});
|