@astryxdesign/cli 0.6.4 → 0.6.5-canary.00f1ed9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/README.md +103 -95
- package/api/build/_adapter.d.mts +36 -2
- package/api/build/_adapter.mjs +41 -10
- package/api/build/build.doc.mjs +8 -3
- package/api/build/build.test.mjs +60 -2
- package/api/build/kit/kit.mjs +109 -26
- package/api/build/kit/rank.d.mts +24 -8
- package/api/build/kit/rank.mjs +277 -97
- package/api/build/kit/rank.test.mjs +231 -48
- package/api/component/_adapter.d.mts +25 -0
- package/api/component/_adapter.mjs +59 -5
- package/api/component/component.d.mts +6 -3
- package/api/component/component.doc.mjs +37 -17
- package/api/component/component.mjs +249 -9
- package/api/component/component.type.d.mts +25 -0
- package/api/component/component.type.mjs +44 -0
- package/api/discover/_adapter.d.mts +114 -6
- package/api/discover/_adapter.mjs +372 -17
- package/api/discover/_adapter.test.mjs +215 -0
- package/api/discover/_catalog-view.d.mts +115 -0
- package/api/discover/_catalog-view.mjs +203 -0
- package/api/discover/_catalog-view.test.mjs +128 -0
- package/api/discover/detail/detail.d.mts +18 -6
- package/api/discover/detail/detail.mjs +67 -13
- package/api/discover/detail/detail.test.mjs +85 -0
- package/api/discover/detail/item/item.d.mts +26 -0
- package/api/discover/detail/item/item.mjs +78 -0
- package/api/discover/detail/item/item.test.mjs +73 -0
- package/api/discover/discover.d.mts +3 -9
- package/api/discover/discover.doc.mjs +61 -18
- package/api/discover/discover.mjs +220 -36
- package/api/discover/discover.test.mjs +11 -2
- package/api/discover/discover.type.d.mts +147 -8
- package/api/discover/discover.type.mjs +102 -12
- package/api/discover/list/list.d.mts +20 -6
- package/api/discover/list/list.mjs +45 -12
- package/api/discover/list/list.test.mjs +46 -0
- package/api/discover/search/search.d.mts +18 -16
- package/api/discover/search/search.mjs +102 -56
- package/api/discover/search/search.test.mjs +144 -10
- package/api/docs/_adapter.d.mts +8 -3
- package/api/docs/_adapter.mjs +14 -6
- package/api/docs/docOverlays.test.mjs +27 -1
- package/api/docs/docs.doc.mjs +2 -2
- package/api/docs/docs.test.mjs +54 -18
- package/api/docs/integration-tree.test.mjs +17 -0
- package/api/docs/integrationDocs.test.mjs +27 -5
- package/api/doctor/doctor.d.mts +8 -3
- package/api/doctor/doctor.doc.mjs +17 -8
- package/api/doctor/doctor.mjs +90 -9
- package/api/doctor/doctor.test.mjs +122 -10
- package/api/doctor/doctor.type.d.mts +1 -1
- package/api/doctor/doctor.type.mjs +1 -1
- package/api/error.d.mts +22 -0
- package/api/error.mjs +42 -0
- package/api/gap-report/gap-report.doc.mjs +19 -10
- package/api/hook/hook.doc.mjs +6 -3
- package/api/index.d.mts +1 -0
- package/api/index.mjs +5 -3
- package/api/init/init.doc.mjs +17 -12
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +7 -3
- package/api/integration/add-contribution.test.mjs +3 -3
- package/api/integration/add-helpers.d.mts +5 -2
- package/api/integration/add-helpers.mjs +36 -9
- package/api/integration/add-theme.mjs +266 -23
- package/api/integration/add-theme.test.mjs +247 -0
- package/api/integration/authoring-checks.mjs +12 -9
- package/api/integration/authoring-checks.type.d.mts +8 -0
- package/api/integration/authoring-checks.type.mjs +7 -3
- package/api/integration/integration-authoring.type.d.mts +4 -1
- package/api/integration/integration-authoring.type.mjs +6 -1
- package/api/integration/integrationAdd.doc.mjs +6 -0
- package/api/integration/integrationAddTheme.doc.mjs +10 -0
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -1
- package/api/integration/integrationDocConflicts.doc.mjs +1 -1
- package/api/integration/integrationPackCheck.doc.mjs +3 -3
- package/api/integration/integrationTemplateConflicts.doc.mjs +1 -1
- package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
- package/api/integration/pack-check.mjs +92 -10
- package/api/integration/pack-check.test.mjs +140 -1
- package/api/integration/pack-check.type.mjs +1 -1
- package/api/integration/validate-integration.d.mts +4 -2
- package/api/integration/validate-integration.mjs +7 -2
- package/api/integration/validate-integration.test.mjs +55 -0
- package/api/integration/validate-integration.type.d.mts +5 -0
- package/api/integration/validate-integration.type.mjs +5 -1
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/json/assertResponse.doc.mjs +1 -1
- package/api/json/isError.doc.mjs +1 -1
- package/api/layout/expand/expand.mjs +12 -7
- package/api/layout/expand/expand.receipt.test.mjs +74 -0
- package/api/layout/layout.type.d.mts +1 -0
- package/api/layout/layout.type.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -1
- package/api/search/search.d.mts +51 -1
- package/api/search/search.doc.mjs +2 -2
- package/api/search/search.mjs +299 -17
- package/api/search/search.test.mjs +216 -18
- package/api/swizzle/copy/copy.mjs +66 -3
- package/api/swizzle/swizzle.doc.mjs +11 -5
- package/api/template/copy/copy.mjs +15 -9
- package/api/template/copy/copy.receipt.test.mjs +77 -0
- package/api/template/copy/copy.test.mjs +9 -0
- package/api/template/show/show.mjs +15 -4
- package/api/template/show/show.test.mjs +76 -0
- package/api/template/template-integration.test.mjs +14 -0
- package/api/template/template.d.mts +1 -1
- package/api/template/template.doc.mjs +8 -3
- package/api/template/template.mjs +1 -0
- package/api/template/template.type.d.mts +2 -0
- package/api/template/template.type.mjs +2 -0
- package/api/theme/add/add.mjs +17 -25
- package/api/theme/add/add.rollback.test.mjs +158 -0
- package/api/theme/add/add.staging.test.mjs +40 -23
- package/api/theme/build/build.d.mts +24 -0
- package/api/theme/build/build.family.test.mjs +7 -12
- package/api/theme/build/build.mjs +243 -26
- package/api/theme/build/build.project-core.test.mjs +165 -0
- package/api/theme/build/build.rollback.test.mjs +148 -0
- package/api/theme/generateTonalPalette.doc.mjs +1 -2
- package/api/theme/listThemes.doc.mjs +1 -1
- package/api/theme/themeAdd.doc.mjs +9 -10
- package/api/theme/themeBuild.doc.mjs +13 -13
- package/api/theme/themeList.doc.mjs +1 -1
- package/api/theme/themeListAvailable.doc.mjs +2 -1
- package/api/theme/themePaletteGenerate.doc.mjs +15 -8
- package/api/theme/themeTargets.doc.mjs +3 -2
- package/api/theme/themeTemplate.doc.mjs +2 -1
- package/api/upgrade/run/files-changed.test.mjs +111 -0
- package/api/upgrade/run/run.mjs +25 -6
- package/api/upgrade/run/run.test.mjs +45 -1
- package/api/upgrade/upgrade.doc.mjs +24 -22
- package/api/upgrade/upgrade.type.d.mts +1 -0
- package/api/upgrade/upgrade.type.mjs +3 -2
- package/assets/codemods/__tests__/runner.test.mjs +3 -1
- package/assets/codemods/file-count.test.mjs +163 -0
- package/assets/codemods/integration-runner.mjs +3 -3
- package/assets/codemods/runner.mjs +5 -4
- package/assets/docs/README.md +4 -2
- package/assets/docs/browser-support.doc.mjs +11 -11
- package/assets/docs/color.doc.mjs +8 -2
- package/assets/docs/elevation.doc.mjs +6 -4
- package/assets/docs/getting-started.doc.mjs +5 -16
- package/assets/docs/icons.doc.mjs +3 -21
- package/assets/docs/illustrations.doc.mjs +7 -15
- package/assets/docs/internationalization.doc.mjs +7 -5
- package/assets/docs/layout.doc.dense.mjs +130 -82
- package/assets/docs/layout.doc.mjs +133 -77
- package/assets/docs/migration.doc.mjs +19 -21
- package/assets/docs/motion.doc.mjs +16 -3
- package/assets/docs/principles.doc.dense.mjs +5 -5
- package/assets/docs/principles.doc.mjs +8 -0
- package/assets/docs/principles.doc.zh.mjs +6 -6
- package/assets/docs/shape.doc.mjs +8 -3
- package/assets/docs/spacing.doc.mjs +7 -2
- package/assets/docs/styling-libraries.doc.mjs +6 -2
- package/assets/docs/styling.doc.mjs +19 -23
- package/assets/docs/theme.doc.dense.mjs +58 -18
- package/assets/docs/theme.doc.mjs +57 -47
- package/assets/docs/theme.doc.zh.mjs +9 -8
- package/assets/docs/tokens.doc.dense.mjs +2 -2
- package/assets/docs/tokens.doc.mjs +389 -8
- package/assets/docs/tokens.doc.zh.mjs +2 -2
- package/assets/docs/tree/add-a-component.doc.mjs +75 -0
- package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
- package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
- package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
- package/assets/docs/tree/block-template.doc.mjs +130 -0
- package/assets/docs/tree/build-the-template.doc.mjs +28 -0
- package/assets/docs/tree/building-blocks.doc.mjs +46 -0
- package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
- package/assets/docs/tree/checks.doc.mjs +119 -0
- package/assets/docs/tree/codemods.doc.mjs +147 -0
- package/assets/docs/tree/component-family.doc.mjs +113 -0
- package/assets/docs/tree/component-imports.doc.mjs +69 -0
- package/assets/docs/tree/component-lookups.doc.mjs +149 -0
- package/assets/docs/tree/components.doc.mjs +23 -0
- package/assets/docs/tree/configuration.doc.mjs +23 -0
- package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
- package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
- package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
- package/assets/docs/tree/docs.doc.mjs +21 -0
- package/assets/docs/tree/document-the-template.doc.mjs +28 -0
- package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
- package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
- package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
- package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
- package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
- package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
- package/assets/docs/tree/help.doc.mjs +16 -0
- package/assets/docs/tree/integrations.doc.mjs +25 -451
- package/assets/docs/tree/links.doc.mjs +98 -0
- package/assets/docs/tree/package-and-test.doc.mjs +32 -0
- package/assets/docs/tree/page-template.doc.mjs +71 -0
- package/assets/docs/tree/publishing.doc.mjs +111 -0
- package/assets/docs/tree/quick-start.doc.mjs +272 -0
- package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
- package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
- package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
- package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
- package/assets/docs/tree/ship.doc.mjs +16 -0
- package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
- package/assets/docs/tree/single-component.doc.mjs +165 -0
- package/assets/docs/tree/start-a-template.doc.mjs +143 -0
- package/assets/docs/tree/subcomponent.doc.mjs +115 -0
- package/assets/docs/tree/template-assets.doc.mjs +64 -0
- package/assets/docs/tree/template-doc-overview.doc.mjs +121 -0
- package/assets/docs/tree/template-fonts.doc.mjs +102 -0
- package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
- package/assets/docs/tree/template-icons.doc.mjs +97 -0
- package/assets/docs/tree/template-images-media.doc.mjs +127 -0
- package/assets/docs/tree/template-styles.doc.mjs +93 -0
- package/assets/docs/tree/templates.doc.mjs +34 -0
- package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
- package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
- package/assets/docs/tree/themes.doc.mjs +39 -0
- package/assets/docs/tree/troubleshooting.doc.mjs +153 -0
- package/assets/docs/tree/upgrading.doc.mjs +103 -0
- package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
- package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
- package/assets/docs/tree/versioning.doc.mjs +162 -0
- package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
- package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
- package/assets/docs/typography.doc.mjs +24 -4
- package/assets/docs/working-with-ai.doc.mjs +30 -22
- package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
- package/assets/templates/pages/ai-chat/template.doc.mjs +16 -1
- package/assets/templates/pages/ai-chat-landing/template.doc.mjs +9 -1
- package/assets/templates/pages/blank/template.doc.mjs +3 -1
- package/assets/templates/pages/canvas-editor/template.doc.mjs +9 -1
- package/assets/templates/pages/centered-hero/template.doc.mjs +3 -1
- package/assets/templates/pages/checkout-wizard/template.doc.mjs +1 -0
- package/assets/templates/pages/classic-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/contact-form/template.doc.mjs +16 -1
- package/assets/templates/pages/dashboard/template.doc.mjs +15 -1
- package/assets/templates/pages/dashboard-alert-rail/template.doc.mjs +23 -1
- package/assets/templates/pages/dashboard-cohort-funnel/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-comparison/template.doc.mjs +9 -1
- package/assets/templates/pages/dashboard-composition/template.doc.mjs +10 -1
- package/assets/templates/pages/dashboard-progress/template.doc.mjs +17 -1
- package/assets/templates/pages/dashboard-scorecard/template.doc.mjs +2 -1
- package/assets/templates/pages/detail-page/template.doc.mjs +8 -0
- package/assets/templates/pages/documentation/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-design/template.doc.mjs +10 -1
- package/assets/templates/pages/documentation-technical/template.doc.mjs +10 -1
- package/assets/templates/pages/editor/template.doc.mjs +9 -1
- package/assets/templates/pages/file-explorer/template.doc.mjs +3 -1
- package/assets/templates/pages/form-two-column/template.doc.mjs +17 -1
- package/assets/templates/pages/form-wizard/template.doc.mjs +14 -1
- package/assets/templates/pages/form-wizard-dialog/template.doc.mjs +1 -0
- package/assets/templates/pages/gallery-hero/template.doc.mjs +10 -1
- package/assets/templates/pages/ide/template.doc.mjs +9 -1
- package/assets/templates/pages/incident-console/template.doc.mjs +10 -1
- package/assets/templates/pages/kanban-board/template.doc.mjs +11 -1
- package/assets/templates/pages/library/template.doc.mjs +15 -1
- package/assets/templates/pages/login/template.doc.mjs +10 -1
- package/assets/templates/pages/login-card/template.doc.mjs +11 -1
- package/assets/templates/pages/login-split/template.doc.mjs +10 -1
- package/assets/templates/pages/login-sso/template.doc.mjs +10 -1
- package/assets/templates/pages/messaging-shell/template.doc.mjs +11 -1
- package/assets/templates/pages/mixed-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/payment-form/template.doc.mjs +3 -1
- package/assets/templates/pages/product-detail/template.doc.mjs +9 -1
- package/assets/templates/pages/product-gallery/template.doc.mjs +10 -1
- package/assets/templates/pages/settings/template.doc.mjs +3 -1
- package/assets/templates/pages/settings-dialog/template.doc.mjs +9 -1
- package/assets/templates/pages/settings-sidebar/template.doc.mjs +9 -1
- package/assets/templates/pages/shell-nav/template.doc.mjs +10 -1
- package/assets/templates/pages/shell-side-nav/template.doc.mjs +14 -1
- package/assets/templates/pages/shell-top-nav/template.doc.mjs +11 -1
- package/assets/templates/pages/side-gallery/template.doc.mjs +3 -1
- package/assets/templates/pages/table/template.doc.mjs +11 -1
- package/assets/templates/pages/table-filter/template.doc.mjs +21 -1
- package/assets/templates/pages/table-grouped/template.doc.mjs +15 -1
- package/assets/templates/pages/table-inbox/template.doc.mjs +18 -6
- package/assets/templates/pages/table-page/template.doc.mjs +20 -1
- package/assets/templates/pages/table-tree/template.doc.mjs +14 -1
- package/assets/templates/pages/theme-showcase/template.doc.mjs +10 -1
- package/assets/templates/pages/work-item-detail/template.doc.mjs +10 -0
- package/assets/templates/themes/butter/icons.tsx +2 -0
- package/assets/templates/themes/chocolate/icons.tsx +2 -0
- package/assets/templates/themes/gothic/icons.tsx +2 -0
- package/assets/templates/themes/matcha/icons.tsx +2 -0
- package/assets/templates/themes/neutral/icons.tsx +2 -0
- package/assets/templates/themes/stone/icons.tsx +2 -0
- package/assets/templates/themes/y2k/icons.tsx +2 -0
- package/authoring/config/config.doc.mjs +9 -1
- package/authoring/config/parse.d.mts +2 -0
- package/authoring/config/parse.mjs +19 -0
- package/authoring/config/parse.test.mjs +8 -0
- package/authoring/config/type.ts +11 -0
- package/authoring/discover/discover.doc.d.mts +13 -0
- package/authoring/discover/discover.doc.mjs +138 -0
- package/authoring/discover/parse.d.mts +24 -0
- package/authoring/discover/parse.mjs +128 -0
- package/authoring/discover/parse.test.mjs +124 -0
- package/authoring/discover/type.ts +87 -0
- package/authoring/doctypes/_schema.d.mts +3 -2
- package/authoring/doctypes/_schema.mjs +6 -0
- package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
- package/authoring/doctypes/base/type.ts +4 -2
- package/authoring/doctypes/component/component.doc.mjs +6 -0
- package/authoring/doctypes/component/type.ts +8 -0
- package/authoring/doctypes/reference/reference.doc.mjs +7 -0
- package/authoring/doctypes/reference/type.ts +5 -0
- package/authoring/doctypes/schema/schema.doc.mjs +2 -2
- package/authoring/doctypes/template/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +1 -0
- package/authoring/doctypes/template/parse.test.mjs +21 -0
- package/authoring/doctypes/template/template.doc.mjs +7 -1
- package/authoring/doctypes/template/type.ts +12 -2
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +10 -0
- package/authoring/index.mjs +1 -0
- package/authoring/integration/integration.doc.mjs +12 -10
- package/clients/cli/commands/component/index.mjs +152 -55
- package/clients/cli/commands/component-batch.test.mjs +341 -0
- package/clients/cli/commands/component-ownership.test.mjs +89 -0
- package/clients/cli/commands/component.doc.mjs +27 -9
- package/clients/cli/commands/discover.doc.mjs +53 -9
- package/clients/cli/commands/discover.mjs +393 -118
- package/clients/cli/commands/discover.sources.test.mjs +267 -0
- package/clients/cli/commands/docs.doc.mjs +1 -1
- package/clients/cli/commands/docs.mjs +60 -17
- package/clients/cli/commands/docs.test.mjs +113 -24
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
- package/clients/cli/commands/doctor-integration.test.mjs +53 -0
- package/clients/cli/commands/doctor.doc.mjs +3 -1
- package/clients/cli/commands/doctor.mjs +53 -9
- package/clients/cli/commands/gap-report.doc.mjs +10 -9
- package/clients/cli/commands/init.doc.mjs +9 -6
- package/clients/cli/commands/integration-add.doc.mjs +17 -7
- package/clients/cli/commands/integration-authoring.test.mjs +70 -9
- package/clients/cli/commands/integration-pack.doc.mjs +5 -9
- package/clients/cli/commands/integration-real-world.test.mjs +1 -1
- package/clients/cli/commands/integration-verify.doc.mjs +22 -0
- package/clients/cli/commands/integration.doc.mjs +4 -4
- package/clients/cli/commands/integration.mjs +76 -43
- package/clients/cli/commands/layout-expand.doc.mjs +3 -1
- package/clients/cli/commands/layout.expand-receipt.test.mjs +94 -0
- package/clients/cli/commands/layout.mjs +16 -0
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +10 -3
- package/clients/cli/commands/search.mjs +21 -2
- package/clients/cli/commands/search.test.mjs +21 -4
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.copy-receipt.test.mjs +60 -0
- package/clients/cli/commands/template.doc.mjs +1 -1
- package/clients/cli/commands/template.mjs +19 -5
- package/clients/cli/commands/template.show-media.test.mjs +62 -0
- package/clients/cli/commands/text-json-parity.test.mjs +7 -1
- package/clients/cli/commands/theme-add.doc.mjs +1 -1
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
- package/clients/cli/commands/theme-palette.doc.mjs +1 -2
- package/clients/cli/commands/theme-targets.doc.mjs +2 -2
- package/clients/cli/commands/theme.doc.mjs +2 -1
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +14 -0
- package/clients/cli/commands/upgrade.doc.mjs +62 -3
- package/clients/cli/commands/write-failure.test.mjs +175 -0
- package/clients/cli/index.mjs +28 -6
- package/clients/cli/lib/define-command.mjs +28 -4
- package/clients/cli/lib/define-command.test.mjs +54 -0
- package/clients/cli/lib/exit-codes.test.mjs +17 -1
- package/clients/cli/lib/json-shim.mjs +24 -14
- package/clients/cli/lib/manifest.mjs +48 -156
- package/clients/cli/lib/manifest.test.mjs +103 -9
- package/clients/cli/lib/parse-error-format.test.mjs +81 -0
- package/foundation/agent-docs/agent-docs.mjs +1 -1
- package/foundation/agent-docs/agent-docs.test.mjs +3 -2
- package/foundation/discovery/authoring-self-docs.mjs +1 -0
- package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
- package/foundation/discovery/cli-self-docs.mjs +16 -2
- package/foundation/discovery/cli-self-docs.test.mjs +20 -0
- package/foundation/discovery/docs-discovery.mjs +5 -1
- package/foundation/discovery/docs-discovery.test.mjs +21 -0
- package/foundation/discovery/docs-section-key.d.mts +1 -1
- package/foundation/discovery/docs-section-key.mjs +1 -1
- package/foundation/discovery/template-adapter.d.mts +14 -0
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +37 -1
- package/foundation/discovery/template-adapter.mjs +21 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +5 -4
- package/foundation/doc-compiler/inputs.test.mjs +0 -1
- package/foundation/doc-compiler/tree.d.mts +4 -0
- package/foundation/doc-compiler/tree.mjs +6 -1
- package/foundation/doc-compiler/tree.test.mjs +65 -14
- package/foundation/integrations/cli-requirement.d.mts +75 -11
- package/foundation/integrations/cli-requirement.mjs +120 -23
- package/foundation/integrations/cli-requirement.test.mjs +141 -9
- package/foundation/integrations/contribution-inventory.mjs +1 -1
- package/foundation/integrations/integrations.d.mts +14 -1
- package/foundation/integrations/integrations.mjs +41 -1
- package/foundation/integrations/integrations.test.mjs +31 -0
- package/foundation/response/batch.type.d.mts +33 -0
- package/foundation/response/batch.type.mjs +34 -0
- package/foundation/response/error-codes.doc.mjs +6 -8
- package/foundation/response/error-codes.test.mjs +30 -5
- package/foundation/response/response-types.doc.d.mts +5 -4
- package/foundation/response/response-types.doc.mjs +49 -19
- package/foundation/response/response-types.doc.test.mjs +23 -0
- package/foundation/response/response.doc.mjs +11 -10
- package/package.json +9 -9
- package/assets/docs/tree/integrations.test.mjs +0 -62
- package/assets/docs/tree/writing-docs.doc.mjs +0 -286
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
* @file `astryx integration add theme` — scaffold one strongly typed,
|
|
5
5
|
* same-stem source/descriptor pair into an integration package and declare the
|
|
6
6
|
* themes root on first use.
|
|
7
|
+
*
|
|
8
|
+
* `--from <base>` forks an existing theme's source files as the starting point
|
|
9
|
+
* instead of writing a blank `defineTheme` skeleton.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
import * as fs from 'node:fs';
|
|
@@ -15,7 +18,10 @@ import {
|
|
|
15
18
|
PathSafetyError,
|
|
16
19
|
sanitizeName,
|
|
17
20
|
} from '../../foundation/fs/path-safety.mjs';
|
|
18
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
discoverBundledThemes,
|
|
23
|
+
discoverThemeDirectory,
|
|
24
|
+
} from '../../foundation/discovery/theme-discovery.mjs';
|
|
19
25
|
import {
|
|
20
26
|
findLocalIntegrationManifestOrNull,
|
|
21
27
|
IntegrationRootConflictError,
|
|
@@ -24,6 +30,12 @@ import {
|
|
|
24
30
|
import {loadManifestObject} from '../../foundation/integrations/integrations.mjs';
|
|
25
31
|
import {themeDescriptorSource} from '../../foundation/integrations/theme-descriptor.mjs';
|
|
26
32
|
import {assertContributionVisible} from '../../foundation/integrations/contribution-inventory.mjs';
|
|
33
|
+
import {
|
|
34
|
+
THEMES_CLI,
|
|
35
|
+
themesCliProblem,
|
|
36
|
+
withCliPeer,
|
|
37
|
+
} from '../../foundation/integrations/cli-requirement.mjs';
|
|
38
|
+
import {stripCopyrightHeader} from '../../foundation/text/copyright-header.mjs';
|
|
27
39
|
import {
|
|
28
40
|
applyWrites,
|
|
29
41
|
findPackageDir,
|
|
@@ -65,6 +77,7 @@ function themeIdentity(slug) {
|
|
|
65
77
|
.join(' ');
|
|
66
78
|
return {
|
|
67
79
|
slug,
|
|
80
|
+
identifier,
|
|
68
81
|
displayName,
|
|
69
82
|
exportName: `${identifier}Theme`,
|
|
70
83
|
entry: `${identifier}Theme.ts`,
|
|
@@ -88,6 +101,182 @@ function themeDescriptor(identity) {
|
|
|
88
101
|
});
|
|
89
102
|
}
|
|
90
103
|
|
|
104
|
+
// ── --from: resolve and fork a base theme ───────────────────────────
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Resolve a theme slug to its discovered record, searching the bundled themes
|
|
108
|
+
* and the package's own themes root (when it has one).
|
|
109
|
+
* @param {string} slug
|
|
110
|
+
* @param {string} packageDir
|
|
111
|
+
* @param {string} owner
|
|
112
|
+
* @param {string | undefined} themesRoot resolved absolute themes root, if any
|
|
113
|
+
* @returns {import('../../foundation/discovery/theme-discovery.mjs').DiscoveredTheme}
|
|
114
|
+
*/
|
|
115
|
+
function resolveBaseTheme(slug, packageDir, owner, themesRoot) {
|
|
116
|
+
const normalized = slug.toLowerCase();
|
|
117
|
+
|
|
118
|
+
/** @type {import('../../foundation/discovery/theme-discovery.mjs').DiscoveredTheme[]} */
|
|
119
|
+
let candidates = [];
|
|
120
|
+
try {
|
|
121
|
+
candidates = discoverBundledThemes();
|
|
122
|
+
} catch {
|
|
123
|
+
// Bundled themes unavailable — not fatal, package themes may suffice.
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Also check the package's own themes root.
|
|
127
|
+
if (themesRoot && fs.existsSync(themesRoot)) {
|
|
128
|
+
try {
|
|
129
|
+
candidates = [...candidates, ...discoverThemeDirectory(themesRoot, owner)];
|
|
130
|
+
} catch {
|
|
131
|
+
// A broken local root is not the caller's problem here.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const match = candidates.find(t => t.slug.toLowerCase() === normalized);
|
|
136
|
+
if (!match) {
|
|
137
|
+
throw new AstryxError(
|
|
138
|
+
`Unknown base theme "${slug}". Available themes: ${candidates.map(t => t.slug).join(', ') || '(none)'}.`,
|
|
139
|
+
candidates.map(t => ({
|
|
140
|
+
name: t.slug,
|
|
141
|
+
reason: t.bundled ? 'bundled theme' : `provided by ${t.package}`,
|
|
142
|
+
})),
|
|
143
|
+
ERROR_CODES.ERR_UNKNOWN_THEME,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return match;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Rename a file from the base theme's namespace to the new theme's namespace.
|
|
151
|
+
* Files whose name starts with the base identifier get the new identifier;
|
|
152
|
+
* others keep their name as-is.
|
|
153
|
+
* @param {string} file POSIX relative path inside the theme directory
|
|
154
|
+
* @param {string} baseIdentifier
|
|
155
|
+
* @param {string} newIdentifier
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
function renameThemeFile(file, baseIdentifier, newIdentifier) {
|
|
159
|
+
const basename = path.posix.basename(file);
|
|
160
|
+
const dir = path.posix.dirname(file);
|
|
161
|
+
if (!basename.startsWith(baseIdentifier)) return file;
|
|
162
|
+
const renamed = newIdentifier + basename.slice(baseIdentifier.length);
|
|
163
|
+
return dir === '.' ? renamed : `${dir}/${renamed}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Rewrite a source file's contents from the base theme's names to the new
|
|
168
|
+
* theme's names. This replaces:
|
|
169
|
+
* 1. Identifier prefixes (camelCase variables, import paths): baseIdentifier
|
|
170
|
+
* followed by an uppercase letter.
|
|
171
|
+
* 2. The theme slug in string literals: 'baseSlug' → 'newSlug' and
|
|
172
|
+
* 'astryx-baseSlug' → 'astryx-newSlug'.
|
|
173
|
+
* 3. CSS custom properties scoped to the theme: --astryx-theme-baseSlug-.
|
|
174
|
+
* @param {string} source
|
|
175
|
+
* @param {string} baseSlug
|
|
176
|
+
* @param {string} baseIdentifier
|
|
177
|
+
* @param {string} newSlug
|
|
178
|
+
* @param {string} newIdentifier
|
|
179
|
+
* @returns {string}
|
|
180
|
+
*/
|
|
181
|
+
function rewriteThemeSource(
|
|
182
|
+
source,
|
|
183
|
+
baseSlug,
|
|
184
|
+
baseIdentifier,
|
|
185
|
+
newSlug,
|
|
186
|
+
newIdentifier,
|
|
187
|
+
) {
|
|
188
|
+
let result = source;
|
|
189
|
+
// 1. Identifier prefixes: neutralPalettes → oceanPalettes, neutralTheme → oceanTheme
|
|
190
|
+
const identRe = new RegExp(`${escapeRegExp(baseIdentifier)}(?=[A-Z])`, 'gu');
|
|
191
|
+
result = result.replace(identRe, newIdentifier);
|
|
192
|
+
// 2. String-literal theme names
|
|
193
|
+
result = result.replace(
|
|
194
|
+
new RegExp(`'astryx-${escapeRegExp(baseSlug)}'`, 'gu'),
|
|
195
|
+
`'astryx-${newSlug}'`,
|
|
196
|
+
);
|
|
197
|
+
result = result.replace(
|
|
198
|
+
new RegExp(`'${escapeRegExp(baseSlug)}'`, 'gu'),
|
|
199
|
+
`'${newSlug}'`,
|
|
200
|
+
);
|
|
201
|
+
// 3. CSS custom properties scoped to the base theme
|
|
202
|
+
result = result.replace(
|
|
203
|
+
new RegExp(
|
|
204
|
+
`--astryx-theme-${escapeRegExp(baseSlug)}-`,
|
|
205
|
+
'gu',
|
|
206
|
+
),
|
|
207
|
+
`--astryx-theme-${newSlug}-`,
|
|
208
|
+
);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** @param {string} s */
|
|
213
|
+
function escapeRegExp(s) {
|
|
214
|
+
return s.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Build the write plans for a forked theme.
|
|
219
|
+
* @param {import('../../foundation/discovery/theme-discovery.mjs').DiscoveredTheme} base
|
|
220
|
+
* @param {ReturnType<typeof themeIdentity>} identity new theme identity
|
|
221
|
+
* @param {string} themeDir absolute target directory
|
|
222
|
+
* @returns {import('./add-helpers.mjs').WritePlan[]}
|
|
223
|
+
*/
|
|
224
|
+
function forkThemePlans(base, identity, themeDir) {
|
|
225
|
+
const baseIdentity = themeIdentity(base.slug);
|
|
226
|
+
/** @type {import('./add-helpers.mjs').WritePlan[]} */
|
|
227
|
+
const plans = [];
|
|
228
|
+
|
|
229
|
+
// Copy every file the base theme ships (entry first, then the rest), except
|
|
230
|
+
// the descriptor — we write a fresh one.
|
|
231
|
+
const filesToCopy = base.files.filter(
|
|
232
|
+
file => path.posix.basename(file) !== `${baseIdentity.exportName}.doc.mjs`,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
for (const file of filesToCopy) {
|
|
236
|
+
const srcPath = path.join(base.sourceDir, file);
|
|
237
|
+
const newFileName = renameThemeFile(
|
|
238
|
+
file,
|
|
239
|
+
baseIdentity.identifier,
|
|
240
|
+
identity.identifier,
|
|
241
|
+
);
|
|
242
|
+
const destPath = assertWithin(newFileName, themeDir, {
|
|
243
|
+
label: `forked theme file "${newFileName}"`,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Read source bytes; for text, strip copyright and rewrite identifiers.
|
|
247
|
+
const bytes = fs.readFileSync(srcPath);
|
|
248
|
+
const text = bytes.toString('utf-8');
|
|
249
|
+
const isText = Buffer.from(text, 'utf-8').equals(bytes);
|
|
250
|
+
|
|
251
|
+
/** @type {string | Buffer} */
|
|
252
|
+
let contents;
|
|
253
|
+
if (isText) {
|
|
254
|
+
contents = rewriteThemeSource(
|
|
255
|
+
stripCopyrightHeader(text),
|
|
256
|
+
base.slug,
|
|
257
|
+
baseIdentity.identifier,
|
|
258
|
+
identity.slug,
|
|
259
|
+
identity.identifier,
|
|
260
|
+
);
|
|
261
|
+
} else {
|
|
262
|
+
contents = bytes;
|
|
263
|
+
}
|
|
264
|
+
plans.push({path: destPath, contents, createOnly: true});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Write a fresh descriptor — the fork is a new theme, not a derivative.
|
|
268
|
+
const descriptorPath = assertWithin(identity.descriptor, themeDir, {
|
|
269
|
+
label: 'theme descriptor file',
|
|
270
|
+
});
|
|
271
|
+
plans.push({
|
|
272
|
+
path: descriptorPath,
|
|
273
|
+
contents: themeDescriptor(identity),
|
|
274
|
+
createOnly: true,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
return plans;
|
|
278
|
+
}
|
|
279
|
+
|
|
91
280
|
/**
|
|
92
281
|
* Read bytes back through the same discovery seam Project uses. A write is not
|
|
93
282
|
* successful until the requested slug resolves.
|
|
@@ -125,7 +314,7 @@ async function verifyThemeContribution(packageDir, manifestFile, owner, slug) {
|
|
|
125
314
|
* @returns {Promise<import('./integration-authoring.type.mjs').IntegrationAddResponse>}
|
|
126
315
|
*/
|
|
127
316
|
export async function integrationAddTheme(name, options = {}) {
|
|
128
|
-
const {cwd = process.cwd(), dryRun = false} = options;
|
|
317
|
+
const {cwd = process.cwd(), dryRun = false, from} = options;
|
|
129
318
|
const packageDir = findPackageDir(cwd);
|
|
130
319
|
const packageFile = path.join(packageDir, 'package.json');
|
|
131
320
|
let pkg;
|
|
@@ -207,37 +396,86 @@ export async function integrationAddTheme(name, options = {}) {
|
|
|
207
396
|
const themeDir = assertWithin(identity.slug, root, {
|
|
208
397
|
label: 'theme directory',
|
|
209
398
|
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// An existing folder is filled; only a file this would write is refused.
|
|
217
|
-
for (const file of [sourceFile, descriptorFile]) {
|
|
218
|
-
if (fs.existsSync(file)) {
|
|
399
|
+
|
|
400
|
+
// Resolve the base theme when --from is given, before writing anything.
|
|
401
|
+
/** @type {import('../../foundation/discovery/theme-discovery.mjs').DiscoveredTheme | undefined} */
|
|
402
|
+
let baseTheme;
|
|
403
|
+
if (from != null) {
|
|
404
|
+
if (from === name) {
|
|
219
405
|
throw new AstryxError(
|
|
220
|
-
`
|
|
406
|
+
`Cannot fork theme "${from}" into itself.`,
|
|
221
407
|
undefined,
|
|
222
|
-
ERROR_CODES.
|
|
408
|
+
ERROR_CODES.ERR_INVALID_ARGUMENT,
|
|
223
409
|
);
|
|
224
410
|
}
|
|
411
|
+
baseTheme = resolveBaseTheme(from, packageDir, owner, root);
|
|
225
412
|
}
|
|
226
413
|
|
|
227
414
|
/** @type {import('./add-helpers.mjs').WritePlan[]} */
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
415
|
+
let plans;
|
|
416
|
+
|
|
417
|
+
if (baseTheme) {
|
|
418
|
+
// Fork: copy the base theme's files, renamed and rewritten.
|
|
419
|
+
plans = forkThemePlans(baseTheme, identity, themeDir);
|
|
420
|
+
// Check that no target file already exists.
|
|
421
|
+
for (const plan of plans) {
|
|
422
|
+
if (fs.existsSync(plan.path)) {
|
|
423
|
+
throw new AstryxError(
|
|
424
|
+
`Refusing to overwrite existing file ${projectPath(path.relative(packageDir, plan.path))}.`,
|
|
425
|
+
undefined,
|
|
426
|
+
ERROR_CODES.ERR_FILE_EXISTS,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
// Blank scaffold.
|
|
432
|
+
const sourceFile = assertWithin(identity.entry, themeDir, {
|
|
433
|
+
label: 'theme source file',
|
|
434
|
+
});
|
|
435
|
+
const descriptorFile = assertWithin(identity.descriptor, themeDir, {
|
|
436
|
+
label: 'theme descriptor file',
|
|
437
|
+
});
|
|
438
|
+
for (const file of [sourceFile, descriptorFile]) {
|
|
439
|
+
if (fs.existsSync(file)) {
|
|
440
|
+
throw new AstryxError(
|
|
441
|
+
`Refusing to overwrite existing file ${projectPath(path.relative(packageDir, file))}.`,
|
|
442
|
+
undefined,
|
|
443
|
+
ERROR_CODES.ERR_FILE_EXISTS,
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
plans = [
|
|
448
|
+
{path: sourceFile, contents: themeSource(identity), createOnly: true},
|
|
449
|
+
{
|
|
450
|
+
path: descriptorFile,
|
|
451
|
+
contents: themeDescriptor(identity),
|
|
452
|
+
createOnly: true,
|
|
453
|
+
},
|
|
454
|
+
];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
let packageUpdate = packageJsonUpdate(
|
|
237
458
|
packageFile,
|
|
238
459
|
rootPath,
|
|
239
460
|
path.basename(manifestFile),
|
|
240
461
|
);
|
|
462
|
+
// A CLI older than the one that reads typed theme descriptors rejects the
|
|
463
|
+
// themes root and withholds the package's themes and docs. Declare the CLI
|
|
464
|
+
// that reads them as a peer, so an older one is flagged at install instead.
|
|
465
|
+
{
|
|
466
|
+
const expectedOriginal =
|
|
467
|
+
packageUpdate?.expectedOriginal ?? fs.readFileSync(packageFile);
|
|
468
|
+
const text = packageUpdate?.contents ?? expectedOriginal.toString('utf-8');
|
|
469
|
+
const current = JSON.parse(text);
|
|
470
|
+
if (themesCliProblem(current) != null) {
|
|
471
|
+
packageUpdate = {
|
|
472
|
+
contents:
|
|
473
|
+
JSON.stringify(withCliPeer(current, THEMES_CLI), null, 2) +
|
|
474
|
+
(text.endsWith('\n') ? '\n' : ''),
|
|
475
|
+
expectedOriginal,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
}
|
|
241
479
|
if (packageUpdate != null) {
|
|
242
480
|
plans.push({
|
|
243
481
|
path: packageFile,
|
|
@@ -287,7 +525,8 @@ export async function integrationAddTheme(name, options = {}) {
|
|
|
287
525
|
writtenFiles.push(manifestPath);
|
|
288
526
|
}
|
|
289
527
|
|
|
290
|
-
|
|
528
|
+
/** @type {import('./integration-authoring.type.mjs').IntegrationAddResponse} */
|
|
529
|
+
const response = {
|
|
291
530
|
type: 'integration.add',
|
|
292
531
|
data: {
|
|
293
532
|
kind: 'theme',
|
|
@@ -299,4 +538,8 @@ export async function integrationAddTheme(name, options = {}) {
|
|
|
299
538
|
dryRun,
|
|
300
539
|
},
|
|
301
540
|
};
|
|
541
|
+
if (from != null) {
|
|
542
|
+
response.data.from = from;
|
|
543
|
+
}
|
|
544
|
+
return response;
|
|
302
545
|
}
|
|
@@ -77,6 +77,40 @@ describe('integrationAddTheme', () => {
|
|
|
77
77
|
expect((await validateLocalIntegration(tmpDir)).issues).toEqual([]);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
it('declares the optional CLI peer that reads typed theme descriptors', async () => {
|
|
81
|
+
setup({includeFiles: false});
|
|
82
|
+
const result = await integrationAddTheme('ocean', {cwd: tmpDir});
|
|
83
|
+
expect(result.data.files).toContain('package.json');
|
|
84
|
+
const pkg = JSON.parse(
|
|
85
|
+
fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'),
|
|
86
|
+
);
|
|
87
|
+
expect(pkg.peerDependencies).toEqual({'@astryxdesign/cli': '>=0.6.4'});
|
|
88
|
+
expect(pkg.peerDependenciesMeta).toEqual({
|
|
89
|
+
'@astryxdesign/cli': {optional: true},
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('keeps a CLI peer that already reads typed theme descriptors', async () => {
|
|
94
|
+
const pkg = {
|
|
95
|
+
name: '@acme/themes',
|
|
96
|
+
version: '1.0.0',
|
|
97
|
+
peerDependencies: {'@astryxdesign/cli': '^0.7.2'},
|
|
98
|
+
};
|
|
99
|
+
fs.writeFileSync(
|
|
100
|
+
path.join(tmpDir, 'package.json'),
|
|
101
|
+
`${JSON.stringify(pkg, null, 2)}\n`,
|
|
102
|
+
);
|
|
103
|
+
fs.writeFileSync(
|
|
104
|
+
path.join(tmpDir, 'astryx.integration.mjs'),
|
|
105
|
+
'export default {};\n',
|
|
106
|
+
);
|
|
107
|
+
const result = await integrationAddTheme('ocean', {cwd: tmpDir});
|
|
108
|
+
expect(result.data.files).not.toContain('package.json');
|
|
109
|
+
expect(
|
|
110
|
+
JSON.parse(fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8')),
|
|
111
|
+
).toEqual(pkg);
|
|
112
|
+
});
|
|
113
|
+
|
|
80
114
|
it('dry-runs the identical receipt without writing anything', async () => {
|
|
81
115
|
setup();
|
|
82
116
|
const beforeManifest = fs.readFileSync(
|
|
@@ -276,3 +310,216 @@ describe('integrationAddTheme', () => {
|
|
|
276
310
|
);
|
|
277
311
|
});
|
|
278
312
|
});
|
|
313
|
+
|
|
314
|
+
describe('integrationAddTheme --from', () => {
|
|
315
|
+
it('forks a bundled theme into the integration package', async () => {
|
|
316
|
+
setup();
|
|
317
|
+
const result = await integrationAddTheme('ocean', {
|
|
318
|
+
cwd: tmpDir,
|
|
319
|
+
from: 'neutral',
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
expect(result.type).toBe('integration.add');
|
|
323
|
+
expect(result.data.kind).toBe('theme');
|
|
324
|
+
expect(result.data.name).toBe('ocean');
|
|
325
|
+
expect(result.data.from).toBe('neutral');
|
|
326
|
+
expect(result.data.written).toBe(true);
|
|
327
|
+
expect(result.data.dryRun).toBe(false);
|
|
328
|
+
expect(result.data.root).toEqual({path: './themes', created: true});
|
|
329
|
+
|
|
330
|
+
// The entry file must exist with the new export name.
|
|
331
|
+
const entry = fs.readFileSync(
|
|
332
|
+
path.join(tmpDir, 'themes/ocean/oceanTheme.ts'),
|
|
333
|
+
'utf-8',
|
|
334
|
+
);
|
|
335
|
+
expect(entry).toContain('oceanTheme');
|
|
336
|
+
expect(entry).toContain("name: 'ocean'");
|
|
337
|
+
// It must NOT contain the original theme identifier as a prefix.
|
|
338
|
+
expect(entry).not.toMatch(/\bneutralTheme\b/);
|
|
339
|
+
expect(entry).not.toMatch(/\bneutralPalettes\b/);
|
|
340
|
+
expect(entry).not.toMatch(/\bneutralSyntax\b/);
|
|
341
|
+
expect(entry).toContain('defineTheme');
|
|
342
|
+
|
|
343
|
+
// A fresh descriptor, not copied from the base.
|
|
344
|
+
const descriptor = fs.readFileSync(
|
|
345
|
+
path.join(tmpDir, 'themes/ocean/oceanTheme.doc.mjs'),
|
|
346
|
+
'utf-8',
|
|
347
|
+
);
|
|
348
|
+
expect(descriptor).toContain("name: 'ocean'");
|
|
349
|
+
expect(descriptor).toContain("@astryxdesign/cli/authoring').ThemeDoc");
|
|
350
|
+
expect(descriptor).toContain('maintained: true');
|
|
351
|
+
|
|
352
|
+
// The theme must validate.
|
|
353
|
+
expect((await validateLocalIntegration(tmpDir)).issues).toEqual([]);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('renames palette and support files from the base theme', async () => {
|
|
357
|
+
setup();
|
|
358
|
+
await integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'});
|
|
359
|
+
|
|
360
|
+
const themeDir = path.join(tmpDir, 'themes/ocean');
|
|
361
|
+
const files = fs.readdirSync(themeDir).sort();
|
|
362
|
+
// neutralPalettes.ts → oceanPalettes.ts, etc.
|
|
363
|
+
expect(files).toContain('oceanTheme.ts');
|
|
364
|
+
expect(files).toContain('oceanTheme.doc.mjs');
|
|
365
|
+
expect(files).toContain('oceanPalettes.ts');
|
|
366
|
+
expect(files).toContain('oceanPaletteRefs.generated.ts');
|
|
367
|
+
expect(files).toContain('icons.tsx');
|
|
368
|
+
// Must not contain any file starting with 'neutral'.
|
|
369
|
+
expect(files.filter(f => f.startsWith('neutral'))).toEqual([]);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('rewrites import paths inside forked files', async () => {
|
|
373
|
+
setup();
|
|
374
|
+
await integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'});
|
|
375
|
+
|
|
376
|
+
const entry = fs.readFileSync(
|
|
377
|
+
path.join(tmpDir, 'themes/ocean/oceanTheme.ts'),
|
|
378
|
+
'utf-8',
|
|
379
|
+
);
|
|
380
|
+
// Import references must use the new identifier.
|
|
381
|
+
expect(entry).toContain('./oceanPaletteRefs.generated');
|
|
382
|
+
expect(entry).not.toContain('./neutralPaletteRefs.generated');
|
|
383
|
+
// The icon registry import must also be renamed.
|
|
384
|
+
expect(entry).toContain('oceanIconRegistry');
|
|
385
|
+
expect(entry).not.toContain('neutralIconRegistry');
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('rewrites CSS custom properties scoped to the base theme', async () => {
|
|
389
|
+
setup();
|
|
390
|
+
await integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'});
|
|
391
|
+
|
|
392
|
+
const entry = fs.readFileSync(
|
|
393
|
+
path.join(tmpDir, 'themes/ocean/oceanTheme.ts'),
|
|
394
|
+
'utf-8',
|
|
395
|
+
);
|
|
396
|
+
expect(entry).toContain('--astryx-theme-ocean-');
|
|
397
|
+
expect(entry).not.toContain('--astryx-theme-neutral-');
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it('rewrites the syntax theme name', async () => {
|
|
401
|
+
setup();
|
|
402
|
+
await integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'});
|
|
403
|
+
|
|
404
|
+
const entry = fs.readFileSync(
|
|
405
|
+
path.join(tmpDir, 'themes/ocean/oceanTheme.ts'),
|
|
406
|
+
'utf-8',
|
|
407
|
+
);
|
|
408
|
+
expect(entry).toContain("'astryx-ocean'");
|
|
409
|
+
expect(entry).not.toContain("'astryx-neutral'");
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
it('strips the copyright header from forked files', async () => {
|
|
413
|
+
setup();
|
|
414
|
+
await integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'});
|
|
415
|
+
|
|
416
|
+
const entry = fs.readFileSync(
|
|
417
|
+
path.join(tmpDir, 'themes/ocean/oceanTheme.ts'),
|
|
418
|
+
'utf-8',
|
|
419
|
+
);
|
|
420
|
+
expect(entry).not.toContain('Copyright (c) Meta Platforms');
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('includes data.from in the receipt only when --from is used', async () => {
|
|
424
|
+
setup();
|
|
425
|
+
const blank = await integrationAddTheme('plain', {cwd: tmpDir});
|
|
426
|
+
expect(blank.data).not.toHaveProperty('from');
|
|
427
|
+
|
|
428
|
+
const forked = await integrationAddTheme('ocean', {
|
|
429
|
+
cwd: tmpDir,
|
|
430
|
+
from: 'neutral',
|
|
431
|
+
});
|
|
432
|
+
expect(forked.data.from).toBe('neutral');
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('refuses to fork a theme into itself', async () => {
|
|
436
|
+
setup();
|
|
437
|
+
await expect(
|
|
438
|
+
integrationAddTheme('neutral', {cwd: tmpDir, from: 'neutral'}),
|
|
439
|
+
).rejects.toMatchObject({code: 'ERR_INVALID_ARGUMENT'});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('refuses an unknown base theme with a helpful suggestion list', async () => {
|
|
443
|
+
setup();
|
|
444
|
+
await expect(
|
|
445
|
+
integrationAddTheme('ocean', {cwd: tmpDir, from: 'nonexistent'}),
|
|
446
|
+
).rejects.toMatchObject({
|
|
447
|
+
code: 'ERR_UNKNOWN_THEME',
|
|
448
|
+
message: expect.stringContaining('nonexistent'),
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('dry-runs with --from without writing anything', async () => {
|
|
453
|
+
setup();
|
|
454
|
+
const result = await integrationAddTheme('ocean', {
|
|
455
|
+
cwd: tmpDir,
|
|
456
|
+
from: 'neutral',
|
|
457
|
+
dryRun: true,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
expect(result.data.written).toBe(false);
|
|
461
|
+
expect(result.data.dryRun).toBe(true);
|
|
462
|
+
expect(result.data.from).toBe('neutral');
|
|
463
|
+
expect(result.data.files).toContain('themes/ocean/oceanTheme.ts');
|
|
464
|
+
expect(result.data.files).toContain('themes/ocean/oceanTheme.doc.mjs');
|
|
465
|
+
expect(fs.existsSync(path.join(tmpDir, 'themes'))).toBe(false);
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('refuses to overwrite when the target already exists', async () => {
|
|
469
|
+
setup();
|
|
470
|
+
await integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'});
|
|
471
|
+
await expect(
|
|
472
|
+
integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'}),
|
|
473
|
+
).rejects.toMatchObject({code: 'ERR_FILE_EXISTS'});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('works with different bundled themes', async () => {
|
|
477
|
+
setup();
|
|
478
|
+
const result = await integrationAddTheme('dusk', {
|
|
479
|
+
cwd: tmpDir,
|
|
480
|
+
from: 'gothic',
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
expect(result.data.from).toBe('gothic');
|
|
484
|
+
const entry = fs.readFileSync(
|
|
485
|
+
path.join(tmpDir, 'themes/dusk/duskTheme.ts'),
|
|
486
|
+
'utf-8',
|
|
487
|
+
);
|
|
488
|
+
expect(entry).toContain('duskTheme');
|
|
489
|
+
expect(entry).toContain("name: 'dusk'");
|
|
490
|
+
expect(entry).not.toMatch(/\bgothicTheme\b/);
|
|
491
|
+
expect((await validateLocalIntegration(tmpDir)).issues).toEqual([]);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('forks from a theme the package already owns', async () => {
|
|
495
|
+
setup();
|
|
496
|
+
// First, add a blank theme.
|
|
497
|
+
await integrationAddTheme('base', {cwd: tmpDir});
|
|
498
|
+
// Then fork it.
|
|
499
|
+
const result = await integrationAddTheme('variant', {
|
|
500
|
+
cwd: tmpDir,
|
|
501
|
+
from: 'base',
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
expect(result.data.from).toBe('base');
|
|
505
|
+
const entry = fs.readFileSync(
|
|
506
|
+
path.join(tmpDir, 'themes/variant/variantTheme.ts'),
|
|
507
|
+
'utf-8',
|
|
508
|
+
);
|
|
509
|
+
expect(entry).toContain('variantTheme');
|
|
510
|
+
expect(entry).toContain("name: 'variant'");
|
|
511
|
+
expect((await validateLocalIntegration(tmpDir)).issues).toEqual([]);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it('declares the optional CLI peer that reads typed theme descriptors', async () => {
|
|
515
|
+
setup({includeFiles: false});
|
|
516
|
+
await integrationAddTheme('ocean', {cwd: tmpDir, from: 'neutral'});
|
|
517
|
+
const pkg = JSON.parse(
|
|
518
|
+
fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf-8'),
|
|
519
|
+
);
|
|
520
|
+
expect(pkg.peerDependencies).toEqual({'@astryxdesign/cli': '>=0.6.4'});
|
|
521
|
+
expect(pkg.peerDependenciesMeta).toEqual({
|
|
522
|
+
'@astryxdesign/cli': {optional: true},
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
});
|