@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
|
@@ -12,7 +12,15 @@
|
|
|
12
12
|
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
|
|
13
13
|
import * as fs from 'node:fs';
|
|
14
14
|
import * as path from 'node:path';
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
discoverAllWithErrors,
|
|
17
|
+
discoverCoreTemplates,
|
|
18
|
+
template,
|
|
19
|
+
} from './template.mjs';
|
|
20
|
+
import {search} from '../search/search.mjs';
|
|
21
|
+
import {build} from '../build/build.mjs';
|
|
22
|
+
import {layoutExpand} from '../layout/layout.mjs';
|
|
23
|
+
import {runCli} from '../../test-utils/run-cli.mjs';
|
|
16
24
|
|
|
17
25
|
let tmpDir;
|
|
18
26
|
let originalCwd;
|
|
@@ -50,7 +58,7 @@ function installWidgets(consumerDir) {
|
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
/** Write a template doc + same-stem source under the templates root. */
|
|
53
|
-
function writeTemplate(pkgDir, id, {kind, body, withSource = true}) {
|
|
61
|
+
function writeTemplate(pkgDir, id, {kind, body, source, withSource = true}) {
|
|
54
62
|
const docPath = path.join(pkgDir, 'templates', `${id}.doc.mjs`);
|
|
55
63
|
fs.mkdirSync(path.dirname(docPath), {recursive: true});
|
|
56
64
|
fs.writeFileSync(
|
|
@@ -61,7 +69,32 @@ function writeTemplate(pkgDir, id, {kind, body, withSource = true}) {
|
|
|
61
69
|
if (withSource) {
|
|
62
70
|
fs.writeFileSync(
|
|
63
71
|
path.join(pkgDir, 'templates', `${id}.tsx`),
|
|
64
|
-
|
|
72
|
+
source ??
|
|
73
|
+
`export default function ${id.replace(/[^a-zA-Z0-9]/g, '')}() { return null; }\n`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Set `replaces` on each named template's own doc, declaring the templates
|
|
80
|
+
* root first when the package has no manifest yet.
|
|
81
|
+
*/
|
|
82
|
+
function declareReplaces(pkgDir, replacements) {
|
|
83
|
+
const manifest = path.join(pkgDir, 'astryx.integration.mjs');
|
|
84
|
+
if (!fs.existsSync(manifest)) {
|
|
85
|
+
fs.writeFileSync(manifest, `export default {templates: './templates'};\n`);
|
|
86
|
+
}
|
|
87
|
+
for (const [id, target] of Object.entries(replacements)) {
|
|
88
|
+
const docPath = path.join(pkgDir, 'templates', `${id}.doc.mjs`);
|
|
89
|
+
const doc = fs.readFileSync(docPath, 'utf-8');
|
|
90
|
+
if (!doc.includes('export default {'))
|
|
91
|
+
throw new Error(`${id}: no doc object`);
|
|
92
|
+
fs.writeFileSync(
|
|
93
|
+
docPath,
|
|
94
|
+
doc.replace(
|
|
95
|
+
'export default {',
|
|
96
|
+
`export default {replaces: ${JSON.stringify(target)}, `,
|
|
97
|
+
),
|
|
65
98
|
);
|
|
66
99
|
}
|
|
67
100
|
}
|
|
@@ -178,6 +211,26 @@ describe('integration template discovery', () => {
|
|
|
178
211
|
expect(result.data.find(t => t.id === 'untyped')).toBeUndefined();
|
|
179
212
|
});
|
|
180
213
|
|
|
214
|
+
it('does not read Object prototype keys as replacements', async () => {
|
|
215
|
+
const pkgDir = installWidgets(tmpDir);
|
|
216
|
+
for (const id of ['constructor', 'toString', '__proto__']) {
|
|
217
|
+
writeTemplate(pkgDir, id, {kind: 'page'});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
221
|
+
expect(
|
|
222
|
+
discovered.errors.filter(error => error.replacementTarget != null),
|
|
223
|
+
).toEqual([]);
|
|
224
|
+
for (const id of ['constructor', 'toString', '__proto__']) {
|
|
225
|
+
const entry = discovered.templates.find(
|
|
226
|
+
template =>
|
|
227
|
+
template.dirName === id && template.package === '@acme/widgets',
|
|
228
|
+
);
|
|
229
|
+
expect(entry).toBeDefined();
|
|
230
|
+
expect(entry?.replaces).toBeUndefined();
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
181
234
|
it('errors with candidates when an id is ambiguous across type/package', async () => {
|
|
182
235
|
const pkgDir = installWidgets(tmpDir);
|
|
183
236
|
// Same id "hero" as both a page and a block within the integration.
|
|
@@ -239,6 +292,972 @@ describe('integration template discovery', () => {
|
|
|
239
292
|
expect(fs.existsSync(path.join(tmpDir, 'dest', 'page.tsx'))).toBe(true);
|
|
240
293
|
});
|
|
241
294
|
|
|
295
|
+
it('uses a declared integration replacement by default and keeps the Core original addressable', async () => {
|
|
296
|
+
const pkgDir = installWidgets(tmpDir);
|
|
297
|
+
writeTemplate(pkgDir, 'acme-app-shell', {
|
|
298
|
+
kind: 'page',
|
|
299
|
+
source:
|
|
300
|
+
'export default function AcmeAppShell() { return <nav>AcmeSideNav + AcmeTopNav</nav>; }\n',
|
|
301
|
+
});
|
|
302
|
+
declareReplaces(pkgDir, {
|
|
303
|
+
'acme-app-shell': 'shell-side-nav',
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const listed = await template(undefined, {list: true, cwd: tmpDir});
|
|
307
|
+
expect(
|
|
308
|
+
listed.data.find(entry => entry.id === 'acme-app-shell'),
|
|
309
|
+
).toMatchObject({
|
|
310
|
+
package: '@acme/widgets',
|
|
311
|
+
replaces: 'shell-side-nav',
|
|
312
|
+
});
|
|
313
|
+
expect(
|
|
314
|
+
listed.data.find(
|
|
315
|
+
entry =>
|
|
316
|
+
entry.id === 'shell-side-nav' &&
|
|
317
|
+
entry.package === '@astryxdesign/core',
|
|
318
|
+
),
|
|
319
|
+
).toBeUndefined();
|
|
320
|
+
|
|
321
|
+
const selected = await template('shell-side-nav', {
|
|
322
|
+
show: true,
|
|
323
|
+
cwd: tmpDir,
|
|
324
|
+
});
|
|
325
|
+
expect(selected.data.template).toBe('acme-app-shell');
|
|
326
|
+
expect(selected.data.source).toContain('AcmeSideNav + AcmeTopNav');
|
|
327
|
+
|
|
328
|
+
const searched = await search('shell-side-nav', {
|
|
329
|
+
type: 'template',
|
|
330
|
+
cwd: tmpDir,
|
|
331
|
+
});
|
|
332
|
+
expect(searched.data.results).toEqual(
|
|
333
|
+
expect.arrayContaining([
|
|
334
|
+
expect.objectContaining({name: 'acme-app-shell'}),
|
|
335
|
+
]),
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
const original = await template('shell-side-nav', {
|
|
339
|
+
package: '@astryxdesign/core',
|
|
340
|
+
show: true,
|
|
341
|
+
cwd: tmpDir,
|
|
342
|
+
});
|
|
343
|
+
expect(original.data.template).toBe('shell-side-nav');
|
|
344
|
+
expect(original.data.source).toContain('@astryxdesign/core/SideNav');
|
|
345
|
+
expect(original.data.source).not.toContain('AcmeSideNav + AcmeTopNav');
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
it('uses canonical replacement resolution for copy collisions', async () => {
|
|
349
|
+
const pkgDir = installWidgets(tmpDir);
|
|
350
|
+
writeTemplate(pkgDir, 'acme-app-shell', {kind: 'page'});
|
|
351
|
+
declareReplaces(pkgDir, {
|
|
352
|
+
'acme-app-shell': 'shell-side-nav',
|
|
353
|
+
});
|
|
354
|
+
const target = path.join(tmpDir, 'existing');
|
|
355
|
+
fs.mkdirSync(target);
|
|
356
|
+
fs.writeFileSync(path.join(target, 'page.tsx'), '// keep me\n');
|
|
357
|
+
|
|
358
|
+
const result = await runCli(
|
|
359
|
+
['template', 'shell-side-nav', 'existing'],
|
|
360
|
+
tmpDir,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(result.status).toBe(1);
|
|
364
|
+
expect(`${result.stdout}\n${result.stderr}`).toContain(
|
|
365
|
+
'Refusing to overwrite existing file',
|
|
366
|
+
);
|
|
367
|
+
expect(fs.readFileSync(path.join(target, 'page.tsx'), 'utf-8')).toBe(
|
|
368
|
+
'// keep me\n',
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('keeps package-scoped lookup exact when one package also owns the Core id', async () => {
|
|
373
|
+
const pkgDir = installWidgets(tmpDir);
|
|
374
|
+
writeTemplate(pkgDir, 'acme-app-shell', {
|
|
375
|
+
kind: 'page',
|
|
376
|
+
source:
|
|
377
|
+
"export default function AcmeAppShell() { return 'active replacement'; }\n",
|
|
378
|
+
});
|
|
379
|
+
writeTemplate(pkgDir, 'shell-side-nav', {
|
|
380
|
+
kind: 'page',
|
|
381
|
+
source:
|
|
382
|
+
"export default function PackageShellSideNav() { return 'package exact id'; }\n",
|
|
383
|
+
});
|
|
384
|
+
declareReplaces(pkgDir, {
|
|
385
|
+
'acme-app-shell': 'shell-side-nav',
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
const unqualified = await template('shell-side-nav', {
|
|
389
|
+
show: true,
|
|
390
|
+
cwd: tmpDir,
|
|
391
|
+
});
|
|
392
|
+
expect(unqualified.data.template).toBe('acme-app-shell');
|
|
393
|
+
expect(unqualified.data.source).toContain('active replacement');
|
|
394
|
+
|
|
395
|
+
const packageExact = await template('shell-side-nav', {
|
|
396
|
+
package: '@acme/widgets',
|
|
397
|
+
show: true,
|
|
398
|
+
cwd: tmpDir,
|
|
399
|
+
});
|
|
400
|
+
expect(packageExact.data.template).toBe('shell-side-nav');
|
|
401
|
+
expect(packageExact.data.source).toContain('package exact id');
|
|
402
|
+
|
|
403
|
+
const packageOwnId = await template('acme-app-shell', {
|
|
404
|
+
package: '@acme/widgets',
|
|
405
|
+
show: true,
|
|
406
|
+
cwd: tmpDir,
|
|
407
|
+
});
|
|
408
|
+
expect(packageOwnId.data.template).toBe('acme-app-shell');
|
|
409
|
+
expect(packageOwnId.data.source).toContain('active replacement');
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('preserves an active replacement whose own id is another active target', async () => {
|
|
413
|
+
const pkgDir = installWidgets(tmpDir);
|
|
414
|
+
writeTemplate(pkgDir, 'shell-top-nav', {
|
|
415
|
+
kind: 'page',
|
|
416
|
+
source:
|
|
417
|
+
"export default function FirstReplacement() { return 'first replacement'; }\n",
|
|
418
|
+
});
|
|
419
|
+
writeTemplate(pkgDir, 'acme-second-shell', {
|
|
420
|
+
kind: 'page',
|
|
421
|
+
source:
|
|
422
|
+
"export default function SecondReplacement() { return 'second replacement'; }\n",
|
|
423
|
+
});
|
|
424
|
+
declareReplaces(pkgDir, {
|
|
425
|
+
'shell-top-nav': 'shell-side-nav',
|
|
426
|
+
'acme-second-shell': 'shell-top-nav',
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
const first = await template('shell-side-nav', {show: true, cwd: tmpDir});
|
|
430
|
+
expect(first.data.template).toBe('shell-top-nav');
|
|
431
|
+
expect(first.data.source).toContain('first replacement');
|
|
432
|
+
|
|
433
|
+
const second = await template('shell-top-nav', {show: true, cwd: tmpDir});
|
|
434
|
+
expect(second.data.template).toBe('acme-second-shell');
|
|
435
|
+
expect(second.data.source).toContain('second replacement');
|
|
436
|
+
|
|
437
|
+
const firstSearch = await search('shell-side-nav', {
|
|
438
|
+
type: 'template',
|
|
439
|
+
cwd: tmpDir,
|
|
440
|
+
});
|
|
441
|
+
expect(firstSearch.data.results[0]).toMatchObject({
|
|
442
|
+
name: 'shell-top-nav',
|
|
443
|
+
command: 'astryx template shell-side-nav --type page',
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const secondSearch = await search('shell-top-nav', {
|
|
447
|
+
type: 'template',
|
|
448
|
+
cwd: tmpDir,
|
|
449
|
+
});
|
|
450
|
+
expect(secondSearch.data.results[0]).toMatchObject({
|
|
451
|
+
name: 'acme-second-shell',
|
|
452
|
+
command: 'astryx template shell-top-nav --type page',
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
const renderedBuild = await runCli(['build', 'shell-side-nav'], tmpDir);
|
|
456
|
+
expect(renderedBuild.status).toBe(0);
|
|
457
|
+
expect(renderedBuild.stdout).toContain(
|
|
458
|
+
'template shell-side-nav --type page <path>',
|
|
459
|
+
);
|
|
460
|
+
expect(renderedBuild.stdout).not.toContain('template shell-top-nav <path>');
|
|
461
|
+
|
|
462
|
+
const listed = await template(undefined, {list: true, cwd: tmpDir});
|
|
463
|
+
expect(listed.data).toEqual(
|
|
464
|
+
expect.arrayContaining([
|
|
465
|
+
expect.objectContaining({
|
|
466
|
+
id: 'shell-top-nav',
|
|
467
|
+
replaces: 'shell-side-nav',
|
|
468
|
+
package: '@acme/widgets',
|
|
469
|
+
}),
|
|
470
|
+
expect.objectContaining({
|
|
471
|
+
id: 'acme-second-shell',
|
|
472
|
+
replaces: 'shell-top-nav',
|
|
473
|
+
package: '@acme/widgets',
|
|
474
|
+
}),
|
|
475
|
+
]),
|
|
476
|
+
);
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('keeps an active replacement ahead of an undeclared exact-id template in search', async () => {
|
|
480
|
+
const active = installWidgets(tmpDir);
|
|
481
|
+
writeTemplate(active, 'active-shell', {
|
|
482
|
+
kind: 'page',
|
|
483
|
+
body: `export default {type: 'page', name: 'ZZZ active shell', description: 'replacement'};\n`,
|
|
484
|
+
});
|
|
485
|
+
declareReplaces(active, {'active-shell': 'shell-side-nav'});
|
|
486
|
+
|
|
487
|
+
const accidental = path.join(tmpDir, 'node_modules', '@acme', 'extra');
|
|
488
|
+
fs.mkdirSync(path.join(accidental, 'templates'), {recursive: true});
|
|
489
|
+
fs.writeFileSync(
|
|
490
|
+
path.join(accidental, 'package.json'),
|
|
491
|
+
JSON.stringify({name: '@acme/extra', version: '1.0.0'}),
|
|
492
|
+
);
|
|
493
|
+
fs.writeFileSync(
|
|
494
|
+
path.join(accidental, 'astryx.integration.mjs'),
|
|
495
|
+
`export default {templates: './templates'};\n`,
|
|
496
|
+
);
|
|
497
|
+
writeTemplate(accidental, 'shell-side-nav', {
|
|
498
|
+
kind: 'page',
|
|
499
|
+
body: `export default {type: 'page', name: 'AAA accidental shell', description: 'collision'};\n`,
|
|
500
|
+
});
|
|
501
|
+
fs.writeFileSync(
|
|
502
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
503
|
+
`export default { integrations: ['@acme/widgets', '@acme/extra'] };\n`,
|
|
504
|
+
);
|
|
505
|
+
|
|
506
|
+
const selected = await template('shell-side-nav', {
|
|
507
|
+
show: true,
|
|
508
|
+
cwd: tmpDir,
|
|
509
|
+
});
|
|
510
|
+
expect(selected.data.template).toBe('active-shell');
|
|
511
|
+
const searched = await search('shell-side-nav', {
|
|
512
|
+
type: 'template',
|
|
513
|
+
cwd: tmpDir,
|
|
514
|
+
});
|
|
515
|
+
expect(searched.data.results[0]).toMatchObject({
|
|
516
|
+
name: 'active-shell',
|
|
517
|
+
command: 'astryx template shell-side-nav --type page',
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
it('resolves a replacement block through the layout alias', async () => {
|
|
522
|
+
const pkgDir = installWidgets(tmpDir);
|
|
523
|
+
writeTemplate(pkgDir, 'acme-card-callout', {
|
|
524
|
+
kind: 'block',
|
|
525
|
+
source:
|
|
526
|
+
'export default function AcmeCardCallout() { return <span>Acme replacement block</span>; }\n',
|
|
527
|
+
});
|
|
528
|
+
declareReplaces(pkgDir, {
|
|
529
|
+
'acme-card-callout': 'CardCallout',
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
const accidental = path.join(tmpDir, 'node_modules', '@acme', 'extra');
|
|
533
|
+
fs.mkdirSync(path.join(accidental, 'templates'), {recursive: true});
|
|
534
|
+
fs.writeFileSync(
|
|
535
|
+
path.join(accidental, 'package.json'),
|
|
536
|
+
JSON.stringify({name: '@acme/extra', version: '1.0.0'}),
|
|
537
|
+
);
|
|
538
|
+
fs.writeFileSync(
|
|
539
|
+
path.join(accidental, 'astryx.integration.mjs'),
|
|
540
|
+
`export default {templates: './templates'};\n`,
|
|
541
|
+
);
|
|
542
|
+
writeTemplate(accidental, 'CardCallout', {
|
|
543
|
+
kind: 'block',
|
|
544
|
+
body: `export default {type: 'block', name: 'ZZZ accidental block', description: 'collision'};\n`,
|
|
545
|
+
source:
|
|
546
|
+
'export default function Accidental() { return <span>Accidental block</span>; }\n',
|
|
547
|
+
});
|
|
548
|
+
fs.writeFileSync(
|
|
549
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
550
|
+
`export default { integrations: ['@acme/widgets', '@acme/extra'] };\n`,
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
const result = await layoutExpand('C{card-callout}', {
|
|
554
|
+
name: 'ReplacementLayout',
|
|
555
|
+
cwd: tmpDir,
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
expect(result.data.code).toContain('Acme replacement block');
|
|
559
|
+
expect(result.data.code).not.toContain('Accidental block');
|
|
560
|
+
expect(result.data.blocksReferenced).toEqual(
|
|
561
|
+
expect.arrayContaining([
|
|
562
|
+
expect.objectContaining({name: 'CardCallout', mode: 'splice'}),
|
|
563
|
+
]),
|
|
564
|
+
);
|
|
565
|
+
}, 30_000);
|
|
566
|
+
|
|
567
|
+
it('resolves chained block aliases consistently in template and layout', async () => {
|
|
568
|
+
const [firstTarget, secondTarget] = (await discoverCoreTemplates()).filter(
|
|
569
|
+
candidate => candidate.type === 'block',
|
|
570
|
+
);
|
|
571
|
+
expect(firstTarget).toBeDefined();
|
|
572
|
+
expect(secondTarget).toBeDefined();
|
|
573
|
+
|
|
574
|
+
const pkgDir = installWidgets(tmpDir);
|
|
575
|
+
writeTemplate(pkgDir, secondTarget.dirName, {
|
|
576
|
+
kind: 'block',
|
|
577
|
+
source:
|
|
578
|
+
'export default function FirstReplacement() { return <span>First chain replacement</span>; }\n',
|
|
579
|
+
});
|
|
580
|
+
writeTemplate(pkgDir, 'acme-final-block', {
|
|
581
|
+
kind: 'block',
|
|
582
|
+
source:
|
|
583
|
+
'export default function FinalReplacement() { return <span>Final chain replacement</span>; }\n',
|
|
584
|
+
});
|
|
585
|
+
declareReplaces(pkgDir, {
|
|
586
|
+
[secondTarget.dirName]: firstTarget.dirName,
|
|
587
|
+
'acme-final-block': secondTarget.dirName,
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
const firstTemplate = await template(firstTarget.dirName, {
|
|
591
|
+
show: true,
|
|
592
|
+
cwd: tmpDir,
|
|
593
|
+
});
|
|
594
|
+
expect(firstTemplate.data.source).toContain('First chain replacement');
|
|
595
|
+
const layoutId = id =>
|
|
596
|
+
id.replace(/([a-z0-9])([A-Z])/gu, '$1-$2').toLowerCase();
|
|
597
|
+
const firstLayout = await layoutExpand(
|
|
598
|
+
`C{${layoutId(firstTarget.dirName)}}`,
|
|
599
|
+
{
|
|
600
|
+
cwd: tmpDir,
|
|
601
|
+
},
|
|
602
|
+
);
|
|
603
|
+
expect(firstLayout.data.code).toContain('First chain replacement');
|
|
604
|
+
expect(firstLayout.data.code).not.toContain('Final chain replacement');
|
|
605
|
+
|
|
606
|
+
const secondTemplate = await template(secondTarget.dirName, {
|
|
607
|
+
show: true,
|
|
608
|
+
cwd: tmpDir,
|
|
609
|
+
});
|
|
610
|
+
expect(secondTemplate.data.source).toContain('Final chain replacement');
|
|
611
|
+
const secondLayout = await layoutExpand(
|
|
612
|
+
`C{${layoutId(secondTarget.dirName)}}`,
|
|
613
|
+
{
|
|
614
|
+
cwd: tmpDir,
|
|
615
|
+
},
|
|
616
|
+
);
|
|
617
|
+
expect(secondLayout.data.code).toContain('Final chain replacement');
|
|
618
|
+
}, 30_000);
|
|
619
|
+
|
|
620
|
+
it('keeps the old discovery behavior when no replacement is declared', async () => {
|
|
621
|
+
const pkgDir = installWidgets(tmpDir);
|
|
622
|
+
writeTemplate(pkgDir, 'acme-app-shell', {kind: 'page'});
|
|
623
|
+
|
|
624
|
+
const core = await template('shell-side-nav', {show: true, cwd: tmpDir});
|
|
625
|
+
expect(core.data.template).toBe('shell-side-nav');
|
|
626
|
+
const integration = await template('acme-app-shell', {
|
|
627
|
+
show: true,
|
|
628
|
+
cwd: tmpDir,
|
|
629
|
+
});
|
|
630
|
+
expect(integration.data.template).toBe('acme-app-shell');
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
it('fails an ambiguous replacement closed and reports every declaration', async () => {
|
|
634
|
+
const pkgDir = installWidgets(tmpDir);
|
|
635
|
+
writeTemplate(pkgDir, 'acme-app-shell-a', {kind: 'page'});
|
|
636
|
+
writeTemplate(pkgDir, 'acme-app-shell-b', {kind: 'page'});
|
|
637
|
+
declareReplaces(pkgDir, {
|
|
638
|
+
'acme-app-shell-a': 'shell-side-nav',
|
|
639
|
+
'acme-app-shell-b': 'shell-side-nav',
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
643
|
+
expect(
|
|
644
|
+
discovered.errors.filter(
|
|
645
|
+
error => error.code === 'ambiguous_template_replacement',
|
|
646
|
+
),
|
|
647
|
+
).toHaveLength(2);
|
|
648
|
+
const selected = await template('shell-side-nav', {
|
|
649
|
+
show: true,
|
|
650
|
+
cwd: tmpDir,
|
|
651
|
+
});
|
|
652
|
+
expect(selected.data.template).toBe('shell-side-nav');
|
|
653
|
+
expect(
|
|
654
|
+
discovered.templates.filter(template =>
|
|
655
|
+
template.dirName.startsWith('acme-app-shell-'),
|
|
656
|
+
),
|
|
657
|
+
).toHaveLength(2);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
it('does not activate a replacement when the same package also declares a missing template', async () => {
|
|
661
|
+
const pkgDir = installWidgets(tmpDir);
|
|
662
|
+
writeTemplate(pkgDir, 'acme-app-shell', {kind: 'page'});
|
|
663
|
+
writeTemplate(pkgDir, 'missing-app-shell', {
|
|
664
|
+
kind: 'page',
|
|
665
|
+
withSource: false,
|
|
666
|
+
});
|
|
667
|
+
declareReplaces(pkgDir, {
|
|
668
|
+
'acme-app-shell': 'shell-side-nav',
|
|
669
|
+
'missing-app-shell': 'shell-side-nav',
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
673
|
+
expect(discovered.errors).toEqual(
|
|
674
|
+
expect.arrayContaining([
|
|
675
|
+
expect.objectContaining({
|
|
676
|
+
code: 'invalid_template_replacement',
|
|
677
|
+
template: 'missing-app-shell',
|
|
678
|
+
}),
|
|
679
|
+
]),
|
|
680
|
+
);
|
|
681
|
+
const selected = await template('shell-side-nav', {
|
|
682
|
+
show: true,
|
|
683
|
+
cwd: tmpDir,
|
|
684
|
+
});
|
|
685
|
+
expect(selected.data.template).toBe('shell-side-nav');
|
|
686
|
+
const own = await template('acme-app-shell', {show: true, cwd: tmpDir});
|
|
687
|
+
expect(own.data.template).toBe('acme-app-shell');
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('keeps Core when a template whose metadata does not load declares the same replacement', async () => {
|
|
691
|
+
const pkgDir = installWidgets(tmpDir);
|
|
692
|
+
writeTemplate(pkgDir, 'acme-app-shell', {kind: 'page'});
|
|
693
|
+
writeTemplate(pkgDir, 'broken-app-shell', {
|
|
694
|
+
kind: 'page',
|
|
695
|
+
body: "export default {type: 'page', name: 'broken', description: 'b', bogusField: 1};\n",
|
|
696
|
+
});
|
|
697
|
+
writeTemplate(pkgDir, 'unparsable-app-shell', {
|
|
698
|
+
kind: 'page',
|
|
699
|
+
body: "export default {replaces: 'shell-side-nav', type: 'page' name: 'x'};\n",
|
|
700
|
+
});
|
|
701
|
+
declareReplaces(pkgDir, {
|
|
702
|
+
'acme-app-shell': 'shell-side-nav',
|
|
703
|
+
'broken-app-shell': 'shell-side-nav',
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
707
|
+
for (const id of ['broken-app-shell', 'unparsable-app-shell']) {
|
|
708
|
+
expect(discovered.errors).toEqual(
|
|
709
|
+
expect.arrayContaining([
|
|
710
|
+
expect.objectContaining({
|
|
711
|
+
code: 'invalid_template_replacement',
|
|
712
|
+
template: id,
|
|
713
|
+
replacementTarget: 'shell-side-nav',
|
|
714
|
+
}),
|
|
715
|
+
]),
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
const selected = await template('shell-side-nav', {show: true, cwd: tmpDir});
|
|
719
|
+
expect(selected.data.template).toBe('shell-side-nav');
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
it('keeps Core when an installed .doc.ts template with invalid metadata computes its replacement', async () => {
|
|
723
|
+
const pkgDir = installWidgets(tmpDir);
|
|
724
|
+
writeTemplate(pkgDir, 'acme-app-shell', {kind: 'page'});
|
|
725
|
+
declareReplaces(pkgDir, {'acme-app-shell': 'shell-side-nav'});
|
|
726
|
+
fs.writeFileSync(
|
|
727
|
+
path.join(pkgDir, 'templates', 'ts-app-shell.doc.ts'),
|
|
728
|
+
"const target: string = 'shell-side-nav';\nexport default {replaces: target, type: 'page', name: 'ts', description: 'd', bogusField: 1};\n",
|
|
729
|
+
);
|
|
730
|
+
fs.writeFileSync(
|
|
731
|
+
path.join(pkgDir, 'templates', 'ts-app-shell.tsx'),
|
|
732
|
+
'export default function TsAppShell() { return null; }\n',
|
|
733
|
+
);
|
|
734
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
735
|
+
expect(discovered.errors).toEqual(
|
|
736
|
+
expect.arrayContaining([
|
|
737
|
+
expect.objectContaining({
|
|
738
|
+
code: 'invalid_template_replacement',
|
|
739
|
+
template: 'ts-app-shell',
|
|
740
|
+
replacementTarget: 'shell-side-nav',
|
|
741
|
+
}),
|
|
742
|
+
]),
|
|
743
|
+
);
|
|
744
|
+
const selected = await template('shell-side-nav', {show: true, cwd: tmpDir});
|
|
745
|
+
expect(selected.data.template).toBe('shell-side-nav');
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it('keeps --json stdout clean when an unusable template doc writes as it loads', async () => {
|
|
749
|
+
fs.writeFileSync(
|
|
750
|
+
path.join(tmpDir, 'astryx.integration.mjs'),
|
|
751
|
+
"export default {templates: './local-templates'};\n",
|
|
752
|
+
);
|
|
753
|
+
fs.mkdirSync(path.join(tmpDir, 'local-templates'), {recursive: true});
|
|
754
|
+
fs.writeFileSync(
|
|
755
|
+
path.join(tmpDir, 'local-templates', 'acme-shell.doc.ts'),
|
|
756
|
+
"process.stdout.write('HELLO-FROM-DOC\\n');\nexport default {name: 'acme-shell', description: 'Acme shell.', replaces: 'dashboard'};\n",
|
|
757
|
+
);
|
|
758
|
+
fs.writeFileSync(
|
|
759
|
+
path.join(tmpDir, 'local-templates', 'acme-shell.tsx'),
|
|
760
|
+
'export default function AcmeShell() { return null; }\n',
|
|
761
|
+
);
|
|
762
|
+
const result = await runCli(['--json', 'template', '--list'], tmpDir);
|
|
763
|
+
expect(result.stdout).not.toContain('HELLO-FROM-DOC');
|
|
764
|
+
// One clean JSON envelope, and the doc was read: its replacement shows.
|
|
765
|
+
const envelope = JSON.parse(result.stdout);
|
|
766
|
+
expect(envelope.type).toBe('template.list');
|
|
767
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
768
|
+
expect(discovered.errors).toEqual(
|
|
769
|
+
expect.arrayContaining([
|
|
770
|
+
expect.objectContaining({template: 'acme-shell', replacementTarget: 'dashboard'}),
|
|
771
|
+
]),
|
|
772
|
+
);
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it('lets the later configured package win a shared replacement target with a warning', async () => {
|
|
776
|
+
const first = installWidgets(tmpDir);
|
|
777
|
+
writeTemplate(first, 'acme-app-shell-a', {
|
|
778
|
+
kind: 'page',
|
|
779
|
+
source:
|
|
780
|
+
"export default function AcmeAppShellA() { return 'first replacement'; }\n",
|
|
781
|
+
});
|
|
782
|
+
declareReplaces(first, {
|
|
783
|
+
'acme-app-shell-a': 'shell-side-nav',
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
const second = path.join(tmpDir, 'node_modules', '@acme', 'extra');
|
|
787
|
+
fs.mkdirSync(path.join(second, 'templates'), {recursive: true});
|
|
788
|
+
fs.writeFileSync(
|
|
789
|
+
path.join(second, 'package.json'),
|
|
790
|
+
JSON.stringify({name: '@acme/extra', version: '1.0.0'}),
|
|
791
|
+
);
|
|
792
|
+
writeTemplate(second, 'acme-app-shell-b', {
|
|
793
|
+
kind: 'page',
|
|
794
|
+
source:
|
|
795
|
+
"export default function AcmeAppShellB() { return 'later replacement'; }\n",
|
|
796
|
+
});
|
|
797
|
+
declareReplaces(second, {
|
|
798
|
+
'acme-app-shell-b': 'shell-side-nav',
|
|
799
|
+
});
|
|
800
|
+
fs.writeFileSync(
|
|
801
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
802
|
+
`export default { integrations: ['@acme/widgets', '@acme/extra'] };\n`,
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
806
|
+
expect(discovered.errors).toEqual(
|
|
807
|
+
expect.arrayContaining([
|
|
808
|
+
expect.objectContaining({
|
|
809
|
+
code: 'ambiguous_template_replacement',
|
|
810
|
+
severity: 'warning',
|
|
811
|
+
package: '@acme/extra',
|
|
812
|
+
}),
|
|
813
|
+
]),
|
|
814
|
+
);
|
|
815
|
+
const selected = await template('shell-side-nav', {
|
|
816
|
+
show: true,
|
|
817
|
+
cwd: tmpDir,
|
|
818
|
+
});
|
|
819
|
+
expect(selected.data.template).toBe('acme-app-shell-b');
|
|
820
|
+
expect(selected.data.source).toContain('later replacement');
|
|
821
|
+
|
|
822
|
+
const command = await runCli(['template', 'shell-side-nav'], tmpDir);
|
|
823
|
+
expect(command.status).toBe(0);
|
|
824
|
+
expect(command.stderr).toContain('@acme/extra has 1 integration issue');
|
|
825
|
+
expect(command.stderr).toContain('astryx doctor');
|
|
826
|
+
|
|
827
|
+
const doctor = await runCli(['doctor'], tmpDir);
|
|
828
|
+
expect(doctor.status).toBe(0);
|
|
829
|
+
expect(doctor.stdout).toContain('configured later');
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it('keeps an explicitly configured replacement ahead of an autolinked one', async () => {
|
|
833
|
+
const explicit = installWidgets(tmpDir);
|
|
834
|
+
writeTemplate(explicit, 'explicit-shell', {
|
|
835
|
+
kind: 'page',
|
|
836
|
+
source:
|
|
837
|
+
"export default function ExplicitShell() { return 'explicit replacement'; }\n",
|
|
838
|
+
});
|
|
839
|
+
declareReplaces(explicit, {
|
|
840
|
+
'explicit-shell': 'shell-side-nav',
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
const implicit = path.join(tmpDir, 'node_modules', '@acme', 'implicit');
|
|
844
|
+
fs.mkdirSync(path.join(implicit, 'templates'), {recursive: true});
|
|
845
|
+
fs.writeFileSync(
|
|
846
|
+
path.join(implicit, 'package.json'),
|
|
847
|
+
JSON.stringify({name: '@acme/implicit', version: '1.0.0'}),
|
|
848
|
+
);
|
|
849
|
+
writeTemplate(implicit, 'implicit-shell', {
|
|
850
|
+
kind: 'page',
|
|
851
|
+
source:
|
|
852
|
+
"export default function ImplicitShell() { return 'implicit replacement'; }\n",
|
|
853
|
+
});
|
|
854
|
+
declareReplaces(implicit, {
|
|
855
|
+
'implicit-shell': 'shell-side-nav',
|
|
856
|
+
});
|
|
857
|
+
fs.writeFileSync(
|
|
858
|
+
path.join(tmpDir, 'package.json'),
|
|
859
|
+
JSON.stringify({
|
|
860
|
+
name: 'consumer',
|
|
861
|
+
dependencies: {
|
|
862
|
+
'@acme/widgets': '1.0.0',
|
|
863
|
+
'@acme/implicit': '1.0.0',
|
|
864
|
+
},
|
|
865
|
+
}),
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
869
|
+
expect(discovered.errors).toEqual(
|
|
870
|
+
expect.arrayContaining([
|
|
871
|
+
expect.objectContaining({
|
|
872
|
+
code: 'ambiguous_template_replacement',
|
|
873
|
+
severity: 'warning',
|
|
874
|
+
package: '@acme/widgets',
|
|
875
|
+
message: expect.stringContaining('explicitly configured'),
|
|
876
|
+
}),
|
|
877
|
+
]),
|
|
878
|
+
);
|
|
879
|
+
const selected = await template('shell-side-nav', {
|
|
880
|
+
show: true,
|
|
881
|
+
cwd: tmpDir,
|
|
882
|
+
});
|
|
883
|
+
expect(selected.data.template).toBe('explicit-shell');
|
|
884
|
+
expect(selected.data.source).toContain('explicit replacement');
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
it('describes precedence truthfully when only autolinked packages conflict', async () => {
|
|
888
|
+
const first = installWidgets(tmpDir);
|
|
889
|
+
writeTemplate(first, 'first-shell', {kind: 'page'});
|
|
890
|
+
declareReplaces(first, {
|
|
891
|
+
'first-shell': 'shell-side-nav',
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
const second = path.join(tmpDir, 'node_modules', '@acme', 'implicit');
|
|
895
|
+
fs.mkdirSync(path.join(second, 'templates'), {recursive: true});
|
|
896
|
+
fs.writeFileSync(
|
|
897
|
+
path.join(second, 'package.json'),
|
|
898
|
+
JSON.stringify({name: '@acme/implicit', version: '1.0.0'}),
|
|
899
|
+
);
|
|
900
|
+
writeTemplate(second, 'second-shell', {kind: 'page'});
|
|
901
|
+
declareReplaces(second, {
|
|
902
|
+
'second-shell': 'shell-side-nav',
|
|
903
|
+
});
|
|
904
|
+
fs.writeFileSync(
|
|
905
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
906
|
+
'export default {};\n',
|
|
907
|
+
);
|
|
908
|
+
fs.writeFileSync(
|
|
909
|
+
path.join(tmpDir, 'package.json'),
|
|
910
|
+
JSON.stringify({
|
|
911
|
+
name: 'consumer',
|
|
912
|
+
dependencies: {
|
|
913
|
+
'@acme/widgets': '1.0.0',
|
|
914
|
+
'@acme/implicit': '1.0.0',
|
|
915
|
+
},
|
|
916
|
+
}),
|
|
917
|
+
);
|
|
918
|
+
|
|
919
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
920
|
+
const warning = discovered.errors.find(
|
|
921
|
+
error => error.code === 'ambiguous_template_replacement',
|
|
922
|
+
);
|
|
923
|
+
|
|
924
|
+
expect(warning?.package).toBe('@acme/implicit');
|
|
925
|
+
expect(warning?.message).toContain(
|
|
926
|
+
'listed later in package.json dependencies',
|
|
927
|
+
);
|
|
928
|
+
expect(warning?.message).toContain('astryx.config');
|
|
929
|
+
expect(warning?.message).not.toContain('configured later');
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
it('does not let an invalid autolinked declaration disable an explicit replacement', async () => {
|
|
933
|
+
const explicit = installWidgets(tmpDir);
|
|
934
|
+
writeTemplate(explicit, 'explicit-shell', {
|
|
935
|
+
kind: 'page',
|
|
936
|
+
source:
|
|
937
|
+
"export default function ExplicitShell() { return 'explicit replacement'; }\n",
|
|
938
|
+
});
|
|
939
|
+
declareReplaces(explicit, {
|
|
940
|
+
'explicit-shell': 'shell-side-nav',
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
const implicit = path.join(tmpDir, 'node_modules', '@acme', 'implicit');
|
|
944
|
+
fs.mkdirSync(path.join(implicit, 'templates'), {recursive: true});
|
|
945
|
+
fs.writeFileSync(
|
|
946
|
+
path.join(implicit, 'package.json'),
|
|
947
|
+
JSON.stringify({name: '@acme/implicit', version: '1.0.0'}),
|
|
948
|
+
);
|
|
949
|
+
writeTemplate(implicit, 'wrong-kind-shell', {kind: 'block'});
|
|
950
|
+
declareReplaces(implicit, {
|
|
951
|
+
'wrong-kind-shell': 'shell-side-nav',
|
|
952
|
+
});
|
|
953
|
+
fs.writeFileSync(
|
|
954
|
+
path.join(tmpDir, 'package.json'),
|
|
955
|
+
JSON.stringify({
|
|
956
|
+
name: 'consumer',
|
|
957
|
+
dependencies: {
|
|
958
|
+
'@acme/widgets': '1.0.0',
|
|
959
|
+
'@acme/implicit': '1.0.0',
|
|
960
|
+
},
|
|
961
|
+
}),
|
|
962
|
+
);
|
|
963
|
+
|
|
964
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
965
|
+
expect(discovered.errors).toEqual(
|
|
966
|
+
expect.arrayContaining([
|
|
967
|
+
expect.objectContaining({
|
|
968
|
+
code: 'invalid_template_replacement',
|
|
969
|
+
package: '@acme/implicit',
|
|
970
|
+
}),
|
|
971
|
+
]),
|
|
972
|
+
);
|
|
973
|
+
const selected = await template('shell-side-nav', {
|
|
974
|
+
show: true,
|
|
975
|
+
cwd: tmpDir,
|
|
976
|
+
});
|
|
977
|
+
expect(selected.data.template).toBe('explicit-shell');
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
it('fails closed when an earlier package has an invalid replacement for the winning target', async () => {
|
|
981
|
+
const first = installWidgets(tmpDir);
|
|
982
|
+
writeTemplate(first, 'wrong-kind-shell', {kind: 'block'});
|
|
983
|
+
declareReplaces(first, {
|
|
984
|
+
'wrong-kind-shell': 'shell-side-nav',
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
const second = path.join(tmpDir, 'node_modules', '@acme', 'extra');
|
|
988
|
+
fs.mkdirSync(path.join(second, 'templates'), {recursive: true});
|
|
989
|
+
fs.writeFileSync(
|
|
990
|
+
path.join(second, 'package.json'),
|
|
991
|
+
JSON.stringify({name: '@acme/extra', version: '1.0.0'}),
|
|
992
|
+
);
|
|
993
|
+
writeTemplate(second, 'valid-shell', {kind: 'page'});
|
|
994
|
+
declareReplaces(second, {
|
|
995
|
+
'valid-shell': 'shell-side-nav',
|
|
996
|
+
});
|
|
997
|
+
fs.writeFileSync(
|
|
998
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
999
|
+
`export default { integrations: ['@acme/widgets', '@acme/extra'] };\n`,
|
|
1000
|
+
);
|
|
1001
|
+
|
|
1002
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
1003
|
+
expect(discovered.errors).toEqual(
|
|
1004
|
+
expect.arrayContaining([
|
|
1005
|
+
expect.objectContaining({
|
|
1006
|
+
code: 'invalid_template_replacement',
|
|
1007
|
+
package: '@acme/widgets',
|
|
1008
|
+
}),
|
|
1009
|
+
]),
|
|
1010
|
+
);
|
|
1011
|
+
const selected = await template('shell-side-nav', {
|
|
1012
|
+
show: true,
|
|
1013
|
+
cwd: tmpDir,
|
|
1014
|
+
});
|
|
1015
|
+
expect(selected.data.template).toBe('shell-side-nav');
|
|
1016
|
+
});
|
|
1017
|
+
|
|
1018
|
+
it('reports a missing Core target and leaves the integration template available by its own id', async () => {
|
|
1019
|
+
const pkgDir = installWidgets(tmpDir);
|
|
1020
|
+
writeTemplate(pkgDir, 'acme-app-shell', {kind: 'page'});
|
|
1021
|
+
declareReplaces(pkgDir, {
|
|
1022
|
+
'acme-app-shell': 'missing-core-shell',
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
1026
|
+
expect(discovered.errors).toEqual(
|
|
1027
|
+
expect.arrayContaining([
|
|
1028
|
+
expect.objectContaining({
|
|
1029
|
+
code: 'missing_template_replacement_target',
|
|
1030
|
+
template: 'acme-app-shell',
|
|
1031
|
+
}),
|
|
1032
|
+
]),
|
|
1033
|
+
);
|
|
1034
|
+
const integration = await template('acme-app-shell', {
|
|
1035
|
+
show: true,
|
|
1036
|
+
cwd: tmpDir,
|
|
1037
|
+
});
|
|
1038
|
+
expect(integration.data.template).toBe('acme-app-shell');
|
|
1039
|
+
const listed = await template(undefined, {
|
|
1040
|
+
list: true,
|
|
1041
|
+
package: '@acme/widgets',
|
|
1042
|
+
cwd: tmpDir,
|
|
1043
|
+
});
|
|
1044
|
+
expect(
|
|
1045
|
+
listed.data.find(entry => entry.id === 'acme-app-shell')?.replaces,
|
|
1046
|
+
).toBeUndefined();
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
it('reports a missing self-id target without hiding the integration template', async () => {
|
|
1050
|
+
const pkgDir = installWidgets(tmpDir);
|
|
1051
|
+
writeTemplate(pkgDir, 'ghost-shell', {kind: 'page'});
|
|
1052
|
+
declareReplaces(pkgDir, {
|
|
1053
|
+
'ghost-shell': 'ghost-shell',
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
1057
|
+
expect(discovered.errors).toEqual(
|
|
1058
|
+
expect.arrayContaining([
|
|
1059
|
+
expect.objectContaining({
|
|
1060
|
+
code: 'missing_template_replacement_target',
|
|
1061
|
+
template: 'ghost-shell',
|
|
1062
|
+
replacementTarget: 'ghost-shell',
|
|
1063
|
+
}),
|
|
1064
|
+
]),
|
|
1065
|
+
);
|
|
1066
|
+
expect(
|
|
1067
|
+
discovered.templates.find(
|
|
1068
|
+
candidate =>
|
|
1069
|
+
candidate.dirName === 'ghost-shell' &&
|
|
1070
|
+
candidate.package === '@acme/widgets',
|
|
1071
|
+
),
|
|
1072
|
+
).toBeDefined();
|
|
1073
|
+
|
|
1074
|
+
const selected = await template('ghost-shell', {
|
|
1075
|
+
show: true,
|
|
1076
|
+
cwd: tmpDir,
|
|
1077
|
+
});
|
|
1078
|
+
expect(selected.data.template).toBe('ghost-shell');
|
|
1079
|
+
expect(selected.data.source).toContain('ghostshell');
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
it('reports a replacement whose kind does not match Core', async () => {
|
|
1083
|
+
const pkgDir = installWidgets(tmpDir);
|
|
1084
|
+
writeTemplate(pkgDir, 'acme-app-shell', {kind: 'block'});
|
|
1085
|
+
declareReplaces(pkgDir, {
|
|
1086
|
+
'acme-app-shell': 'shell-side-nav',
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
const discovered = await discoverAllWithErrors(tmpDir);
|
|
1090
|
+
expect(discovered.errors).toEqual(
|
|
1091
|
+
expect.arrayContaining([
|
|
1092
|
+
expect.objectContaining({
|
|
1093
|
+
code: 'invalid_template_replacement',
|
|
1094
|
+
template: 'acme-app-shell',
|
|
1095
|
+
}),
|
|
1096
|
+
]),
|
|
1097
|
+
);
|
|
1098
|
+
const selected = await template('shell-side-nav', {
|
|
1099
|
+
show: true,
|
|
1100
|
+
cwd: tmpDir,
|
|
1101
|
+
});
|
|
1102
|
+
expect(selected.data.template).toBe('shell-side-nav');
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
it('falls back to Core for a rejected same-id replacement while package lookup keeps the integration template', async () => {
|
|
1106
|
+
const pkgDir = installWidgets(tmpDir);
|
|
1107
|
+
writeTemplate(pkgDir, 'shell-side-nav', {kind: 'block'});
|
|
1108
|
+
declareReplaces(pkgDir, {
|
|
1109
|
+
'shell-side-nav': 'shell-side-nav',
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
const selected = await template('shell-side-nav', {
|
|
1113
|
+
show: true,
|
|
1114
|
+
cwd: tmpDir,
|
|
1115
|
+
});
|
|
1116
|
+
expect(selected.data.template).toBe('shell-side-nav');
|
|
1117
|
+
expect(selected.data.type).toBe('page');
|
|
1118
|
+
|
|
1119
|
+
const integration = await template('shell-side-nav', {
|
|
1120
|
+
package: '@acme/widgets',
|
|
1121
|
+
show: true,
|
|
1122
|
+
cwd: tmpDir,
|
|
1123
|
+
});
|
|
1124
|
+
expect(integration.data.template).toBe('shell-side-nav');
|
|
1125
|
+
expect(integration.data.type).toBe('block');
|
|
1126
|
+
|
|
1127
|
+
const block = await template('shell-side-nav', {
|
|
1128
|
+
type: 'block',
|
|
1129
|
+
show: true,
|
|
1130
|
+
cwd: tmpDir,
|
|
1131
|
+
});
|
|
1132
|
+
expect(block.data.template).toBe('shell-side-nav');
|
|
1133
|
+
expect(block.data.type).toBe('block');
|
|
1134
|
+
|
|
1135
|
+
const listedBlocks = await template(undefined, {
|
|
1136
|
+
list: true,
|
|
1137
|
+
type: 'block',
|
|
1138
|
+
cwd: tmpDir,
|
|
1139
|
+
});
|
|
1140
|
+
expect(listedBlocks.data).toEqual(
|
|
1141
|
+
expect.arrayContaining([
|
|
1142
|
+
expect.objectContaining({
|
|
1143
|
+
id: 'shell-side-nav',
|
|
1144
|
+
type: 'block',
|
|
1145
|
+
package: '@acme/widgets',
|
|
1146
|
+
}),
|
|
1147
|
+
]),
|
|
1148
|
+
);
|
|
1149
|
+
|
|
1150
|
+
const searched = await search('shell-side-nav', {
|
|
1151
|
+
type: 'template',
|
|
1152
|
+
limit: 100,
|
|
1153
|
+
cwd: tmpDir,
|
|
1154
|
+
});
|
|
1155
|
+
const sameId = searched.data.results.filter(
|
|
1156
|
+
result => result.name === 'shell-side-nav',
|
|
1157
|
+
);
|
|
1158
|
+
expect(sameId).toEqual(
|
|
1159
|
+
expect.arrayContaining([
|
|
1160
|
+
expect.objectContaining({
|
|
1161
|
+
kind: 'page',
|
|
1162
|
+
command: 'astryx template shell-side-nav --type page',
|
|
1163
|
+
}),
|
|
1164
|
+
expect.objectContaining({
|
|
1165
|
+
kind: 'block',
|
|
1166
|
+
command: 'astryx template shell-side-nav --type block',
|
|
1167
|
+
}),
|
|
1168
|
+
]),
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
const kit = await build('shell-side-nav', {
|
|
1172
|
+
type: 'template',
|
|
1173
|
+
limit: 100,
|
|
1174
|
+
cwd: tmpDir,
|
|
1175
|
+
});
|
|
1176
|
+
expect(kit.type).toBe('build.kit');
|
|
1177
|
+
if (kit.type !== 'build.kit') throw new Error('expected build.kit');
|
|
1178
|
+
expect(kit.data.blocks).toEqual(
|
|
1179
|
+
expect.arrayContaining([
|
|
1180
|
+
expect.objectContaining({
|
|
1181
|
+
name: 'shell-side-nav',
|
|
1182
|
+
command: 'astryx template shell-side-nav --type block',
|
|
1183
|
+
}),
|
|
1184
|
+
]),
|
|
1185
|
+
);
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
it('round-trips both kinds when a block replacement shadows a page with the same id', async () => {
|
|
1189
|
+
const pkgDir = installWidgets(tmpDir);
|
|
1190
|
+
writeTemplate(pkgDir, 'acme-callout', {
|
|
1191
|
+
kind: 'block',
|
|
1192
|
+
source:
|
|
1193
|
+
"export default function AcmeCallout() { return 'replacement block'; }\n",
|
|
1194
|
+
});
|
|
1195
|
+
writeTemplate(pkgDir, 'CardCallout', {
|
|
1196
|
+
kind: 'page',
|
|
1197
|
+
source:
|
|
1198
|
+
"export default function CardCalloutPage() { return 'ordinary page'; }\n",
|
|
1199
|
+
});
|
|
1200
|
+
declareReplaces(pkgDir, {
|
|
1201
|
+
'acme-callout': 'CardCallout',
|
|
1202
|
+
});
|
|
1203
|
+
|
|
1204
|
+
const bare = await template('CardCallout', {show: true, cwd: tmpDir});
|
|
1205
|
+
expect(bare.data.template).toBe('acme-callout');
|
|
1206
|
+
expect(bare.data.type).toBe('block');
|
|
1207
|
+
|
|
1208
|
+
const page = await template('CardCallout', {
|
|
1209
|
+
type: 'page',
|
|
1210
|
+
show: true,
|
|
1211
|
+
cwd: tmpDir,
|
|
1212
|
+
});
|
|
1213
|
+
expect(page.data.template).toBe('CardCallout');
|
|
1214
|
+
expect(page.data.type).toBe('page');
|
|
1215
|
+
|
|
1216
|
+
const searched = await search('CardCallout', {
|
|
1217
|
+
type: 'template',
|
|
1218
|
+
limit: 100,
|
|
1219
|
+
cwd: tmpDir,
|
|
1220
|
+
});
|
|
1221
|
+
expect(searched.data.results).toEqual(
|
|
1222
|
+
expect.arrayContaining([
|
|
1223
|
+
expect.objectContaining({
|
|
1224
|
+
name: 'CardCallout',
|
|
1225
|
+
kind: 'page',
|
|
1226
|
+
command: 'astryx template CardCallout --type page',
|
|
1227
|
+
}),
|
|
1228
|
+
expect.objectContaining({
|
|
1229
|
+
name: 'acme-callout',
|
|
1230
|
+
kind: 'block',
|
|
1231
|
+
command: 'astryx template CardCallout --type block',
|
|
1232
|
+
}),
|
|
1233
|
+
]),
|
|
1234
|
+
);
|
|
1235
|
+
|
|
1236
|
+
const kit = await build('CardCallout', {
|
|
1237
|
+
type: 'template',
|
|
1238
|
+
limit: 100,
|
|
1239
|
+
cwd: tmpDir,
|
|
1240
|
+
});
|
|
1241
|
+
expect(kit.type).toBe('build.kit');
|
|
1242
|
+
if (kit.type !== 'build.kit') throw new Error('expected build.kit');
|
|
1243
|
+
expect(kit.data.pages).toEqual(
|
|
1244
|
+
expect.arrayContaining([
|
|
1245
|
+
expect.objectContaining({
|
|
1246
|
+
name: 'CardCallout',
|
|
1247
|
+
command: 'astryx template CardCallout --type page',
|
|
1248
|
+
}),
|
|
1249
|
+
]),
|
|
1250
|
+
);
|
|
1251
|
+
expect(kit.data.blocks).toEqual(
|
|
1252
|
+
expect.arrayContaining([
|
|
1253
|
+
expect.objectContaining({
|
|
1254
|
+
name: 'acme-callout',
|
|
1255
|
+
command: 'astryx template CardCallout --type block',
|
|
1256
|
+
}),
|
|
1257
|
+
]),
|
|
1258
|
+
);
|
|
1259
|
+
});
|
|
1260
|
+
|
|
242
1261
|
it('copies a block template into a directory as <id-basename>.tsx', async () => {
|
|
243
1262
|
const pkgDir = installWidgets(tmpDir);
|
|
244
1263
|
writeTemplate(pkgDir, 'marketing/hero', {kind: 'block'});
|
|
@@ -252,3 +1271,53 @@ describe('integration template discovery', () => {
|
|
|
252
1271
|
expect(fs.existsSync(path.join(tmpDir, 'dest', 'hero.tsx'))).toBe(true);
|
|
253
1272
|
});
|
|
254
1273
|
});
|
|
1274
|
+
|
|
1275
|
+
describe('build names a start its command selects', () => {
|
|
1276
|
+
it('scaffolds an integration replacement through the Core id it replaces', async () => {
|
|
1277
|
+
const pkgDir = installWidgets(tmpDir);
|
|
1278
|
+
writeTemplate(pkgDir, 'acme-app-shell', {
|
|
1279
|
+
kind: 'page',
|
|
1280
|
+
body: "export default {type: 'page', name: 'Acme App Shell', category: 'Shell - Acme Sidebar', description: 'Acme application frame with a left sidebar navigation. Shell, frame, or sidebar navigation.'};\n",
|
|
1281
|
+
});
|
|
1282
|
+
declareReplaces(pkgDir, {'acme-app-shell': 'shell-side-nav'});
|
|
1283
|
+
|
|
1284
|
+
const kit = await build('acme application frame with sidebar navigation', {
|
|
1285
|
+
cwd: tmpDir,
|
|
1286
|
+
});
|
|
1287
|
+
expect(kit.type).toBe('build.kit');
|
|
1288
|
+
if (kit.type !== 'build.kit') throw new Error('expected build.kit');
|
|
1289
|
+
expect(kit.data.start).toMatchObject({
|
|
1290
|
+
name: 'acme-app-shell',
|
|
1291
|
+
command: 'astryx template shell-side-nav --type page <path>',
|
|
1292
|
+
});
|
|
1293
|
+
const selected = await template('shell-side-nav', {
|
|
1294
|
+
type: 'page',
|
|
1295
|
+
show: true,
|
|
1296
|
+
cwd: tmpDir,
|
|
1297
|
+
});
|
|
1298
|
+
expect(selected.data.template).toBe('acme-app-shell');
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
it('keeps the start command unambiguous when a block shares the id', async () => {
|
|
1302
|
+
const pkgDir = installWidgets(tmpDir);
|
|
1303
|
+
writeTemplate(pkgDir, 'contact-form', {kind: 'block'});
|
|
1304
|
+
|
|
1305
|
+
const kit = await build('contact form', {cwd: tmpDir});
|
|
1306
|
+
expect(kit.type).toBe('build.kit');
|
|
1307
|
+
if (kit.type !== 'build.kit') throw new Error('expected build.kit');
|
|
1308
|
+
expect(kit.data.start).toMatchObject({
|
|
1309
|
+
name: 'contact-form',
|
|
1310
|
+
command: 'astryx template contact-form --type page <path>',
|
|
1311
|
+
});
|
|
1312
|
+
const selected = await template('contact-form', {
|
|
1313
|
+
type: 'page',
|
|
1314
|
+
show: true,
|
|
1315
|
+
cwd: tmpDir,
|
|
1316
|
+
});
|
|
1317
|
+
expect(selected.data.template).toBe('contact-form');
|
|
1318
|
+
// The bare id is ambiguous here, which is why the command carries --type.
|
|
1319
|
+
await expect(
|
|
1320
|
+
template('contact-form', {show: true, cwd: tmpDir}),
|
|
1321
|
+
).rejects.toMatchObject({code: 'ERR_AMBIGUOUS_TEMPLATE'});
|
|
1322
|
+
});
|
|
1323
|
+
});
|