@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db52d98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -15
- 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 +14 -7
- package/api/build/build.test.mjs +184 -6
- package/api/build/build.type.d.mts +57 -2
- package/api/build/build.type.mjs +30 -7
- package/api/build/help/help.d.mts +4 -0
- package/api/build/help/help.mjs +24 -11
- 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 +1 -0
- package/api/component/component.mjs +60 -12
- package/api/component/list/list.mjs +3 -2
- package/api/discover/discover.doc.mjs +1 -0
- package/api/docs/_adapter.d.mts +252 -34
- package/api/docs/_adapter.mjs +923 -132
- package/api/docs/detail/detail.mjs +11 -3
- package/api/docs/detail/section/section.mjs +24 -13
- package/api/docs/detail/section/section.test.mjs +15 -6
- package/api/docs/docs.d.mts +5 -3
- package/api/docs/docs.doc.mjs +39 -17
- package/api/docs/docs.mjs +44 -8
- package/api/docs/docs.test.mjs +158 -4
- package/api/docs/docs.type.d.mts +181 -2
- package/api/docs/docs.type.mjs +117 -3
- package/api/docs/index/index.mjs +11 -3
- package/api/docs/index/index.test.mjs +1 -1
- package/api/docs/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +14 -14
- 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 +54 -4
- package/api/doctor/doctor.doc.mjs +1 -0
- package/api/doctor/doctor.mjs +331 -16
- package/api/doctor/doctor.test.mjs +420 -7
- package/api/gap-report/gap-report.doc.mjs +1 -0
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +1 -0
- package/api/hook/list/list.d.mts +1 -1
- 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 +2 -1
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +114 -12
- package/api/integration/add-contribution.test.mjs +174 -3
- 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 +2 -0
- 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 +7 -0
- package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
- package/api/integration/integrationAddCodemod.doc.mjs +1 -0
- package/api/integration/integrationAddComponent.doc.mjs +1 -0
- 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 +34 -0
- package/api/integration/pack-check.test.mjs +138 -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 +50 -102
- package/api/integration/validate-integration.test.mjs +88 -26
- package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
- package/api/integration/validateIntegration.doc.mjs +2 -1
- package/api/json/assertResponse.doc.mjs +1 -0
- package/api/json/index.ts +2 -0
- package/api/json/isError.doc.mjs +1 -0
- package/api/json/parseResponse.doc.mjs +1 -0
- package/api/layout/_adapter.mjs +20 -5
- package/api/layout/layoutCheck.doc.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -0
- package/api/layout/layoutGrammar.doc.mjs +1 -0
- package/api/search/search.d.mts +59 -1
- package/api/search/search.doc.mjs +5 -3
- package/api/search/search.mjs +458 -77
- package/api/search/search.test.mjs +91 -2
- package/api/search/search.type.d.mts +13 -1
- package/api/search/search.type.mjs +4 -1
- package/api/swizzle/swizzle.doc.mjs +1 -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.doc.mjs +28 -7
- package/api/template/template.mjs +45 -8
- package/api/template/template.type.d.mts +6 -8
- package/api/template/template.type.mjs +3 -2
- package/api/theme/_adapter.d.mts +2 -3
- package/api/theme/_adapter.mjs +4 -5
- package/api/theme/add/add.binary.test.mjs +10 -17
- package/api/theme/add/add.test.mjs +14 -1
- 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/template/template.test.mjs +5 -0
- package/api/theme/themeAdd.doc.mjs +4 -3
- package/api/theme/themeBuild.doc.mjs +1 -0
- 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 +2 -1
- package/api/upgrade/_adapter.d.mts +32 -5
- package/api/upgrade/_adapter.mjs +97 -69
- package/api/upgrade/provider-agreement.test.mjs +152 -0
- package/api/upgrade/run/run.mjs +356 -59
- package/api/upgrade/upgrade.doc.mjs +6 -1
- package/api/upgrade/upgrade.type.d.mts +34 -0
- package/api/upgrade/upgrade.type.mjs +15 -0
- package/assets/codemods/__tests__/runner.test.mjs +330 -8
- package/assets/codemods/integration-discovery.mjs +8 -2
- package/assets/codemods/integration-discovery.test.mjs +15 -0
- package/assets/codemods/integration-runner.mjs +56 -4
- package/assets/codemods/integration-runner.protection.test.mjs +153 -0
- package/assets/codemods/run-codemod.mjs +177 -34
- package/assets/codemods/runner.mjs +350 -102
- package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
- package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
- package/assets/codemods/transforms/next/index.mjs +19 -1
- package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
- package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
- package/assets/docs/getting-started.doc.mjs +2 -2
- 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 +3 -3
- 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} +66 -38
- 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 +3 -3
- 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/Item/ItemDocumentTabs.doc.mjs +14 -0
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -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/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/codemod/codemod.doc.mjs +1 -1
- package/authoring/codemod/type.ts +12 -0
- package/authoring/config/config.doc.mjs +1 -1
- package/authoring/debug/debug.doc.d.mts +11 -0
- package/authoring/debug/debug.doc.mjs +182 -0
- package/authoring/debug/parse.d.mts +3 -3
- package/authoring/doctypes/_schema.d.mts +119 -117
- package/authoring/doctypes/_schema.mjs +54 -3
- package/authoring/doctypes/base/graph-fields.doc.mjs +8 -6
- package/authoring/doctypes/base/type.ts +6 -5
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +2 -2
- package/authoring/doctypes/component/type.ts +2 -2
- package/authoring/doctypes/doctypes-new.test.mjs +48 -6
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +1 -1
- package/authoring/doctypes/function/function.doc.mjs +3 -2
- package/authoring/doctypes/function/type.ts +3 -2
- package/authoring/doctypes/hook/type.ts +2 -2
- package/authoring/doctypes/load-contract.test.mjs +28 -2
- package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
- package/authoring/doctypes/namespace/parse.test.mjs +23 -25
- package/authoring/doctypes/namespace/type.ts +5 -2
- package/authoring/doctypes/parse.d.mts +4 -2
- package/authoring/doctypes/parse.mjs +10 -5
- package/authoring/doctypes/reference/reference.doc.mjs +35 -6
- package/authoring/doctypes/reference/type.ts +30 -13
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +1 -2
- package/authoring/doctypes/template/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +4 -0
- package/authoring/doctypes/template/parse.test.mjs +18 -0
- package/authoring/doctypes/template/template.doc.mjs +9 -3
- package/authoring/doctypes/template/type.ts +8 -0
- 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 +2 -1
- package/authoring/gap-report/gap-report.doc.d.mts +12 -0
- package/authoring/gap-report/gap-report.doc.mjs +183 -0
- package/authoring/identity/identity.doc.mjs +2 -2
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +7 -4
- package/authoring/index.mjs +2 -1
- package/authoring/integration/integration.doc.mjs +2 -2
- package/authoring/integration/type.ts +4 -10
- package/clients/cli/__tests__/cliManifest.test.ts +27 -29
- package/clients/cli/commands/blog.doc.mjs +1 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.mjs +11 -45
- package/clients/cli/commands/build.doc.mjs +12 -7
- package/clients/cli/commands/build.mjs +105 -67
- package/clients/cli/commands/build.text-fields.test.mjs +41 -0
- package/clients/cli/commands/component/index.mjs +1 -1
- package/clients/cli/commands/component-ownership.test.mjs +3 -3
- package/clients/cli/commands/component.doc.mjs +1 -1
- package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
- package/clients/cli/commands/discover.doc.mjs +1 -1
- package/clients/cli/commands/discover.mjs +1 -1
- package/clients/cli/commands/docs.doc.mjs +21 -9
- package/clients/cli/commands/docs.mjs +184 -70
- package/clients/cli/commands/docs.test.mjs +120 -16
- 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.test.mjs +90 -8
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +53 -16
- package/clients/cli/commands/gap-report.doc.mjs +1 -1
- package/clients/cli/commands/hook.doc.mjs +1 -1
- package/clients/cli/commands/init.doc.mjs +1 -1
- package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
- package/clients/cli/commands/integration-add.doc.mjs +11 -1
- 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/layout-check.doc.mjs +1 -1
- package/clients/cli/commands/layout-expand.doc.mjs +1 -1
- package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
- package/clients/cli/commands/layout.doc.mjs +1 -1
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +1 -1
- package/clients/cli/commands/search.mjs +11 -2
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.doc.mjs +24 -7
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/commands/text-json-parity.test.mjs +719 -0
- package/clients/cli/commands/theme-add.doc.mjs +2 -2
- package/clients/cli/commands/theme-build.doc.mjs +1 -1
- package/clients/cli/commands/theme-list.doc.mjs +2 -2
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
- 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.doc.mjs +1 -1
- package/clients/cli/commands/theme.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.doc.mjs +3 -2
- package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
- package/clients/cli/commands/upgrade.mjs +29 -7
- package/clients/cli/formatters/index.mjs +2 -0
- package/clients/cli/formatters/index.test.mjs +6 -0
- package/clients/cli/index.mjs +8 -0
- package/clients/cli/lib/hook-format.mjs +14 -5
- package/clients/cli/lib/manifest.mjs +9 -2
- package/foundation/agent-docs/agent-docs.d.mts +3 -2
- package/foundation/agent-docs/agent-docs.mjs +13 -9
- package/foundation/agent-docs/agent-docs.test.mjs +19 -1
- package/foundation/config/project-themes.test.mjs +11 -19
- package/foundation/config/project.d.mts +20 -11
- package/foundation/config/project.mjs +123 -80
- package/foundation/config/project.test.mjs +144 -21
- package/foundation/discovery/authoring-self-docs.d.mts +18 -0
- package/foundation/discovery/authoring-self-docs.mjs +37 -15
- package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
- 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 +38 -0
- package/foundation/discovery/component-discovery.mjs +48 -0
- package/foundation/discovery/component-loader.d.mts +35 -38
- package/foundation/discovery/component-loader.mjs +53 -222
- package/foundation/discovery/docs-discovery.d.mts +112 -11
- package/foundation/discovery/docs-discovery.mjs +231 -36
- package/foundation/discovery/docs-discovery.test.mjs +111 -32
- package/foundation/discovery/docs-output-budget.d.mts +2 -2
- package/foundation/discovery/docs-output-budget.mjs +1 -1
- package/foundation/discovery/docs-section-key.d.mts +28 -10
- package/foundation/discovery/docs-section-key.mjs +134 -33
- package/foundation/discovery/docs-section-key.test.mjs +41 -19
- package/foundation/discovery/template-adapter.d.mts +99 -6
- package/foundation/discovery/template-adapter.mjs +480 -57
- 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/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 +220 -39
- package/foundation/doc-compiler/compile.mjs +311 -15
- 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 +28 -1
- 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 +13 -0
- package/foundation/doc-compiler/ir.mjs +196 -12
- package/foundation/doc-compiler/lenses.d.mts +8 -5
- package/foundation/doc-compiler/lenses.mjs +50 -4
- 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/integrations/autolink.d.mts +58 -1
- package/foundation/integrations/autolink.mjs +143 -57
- 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 +51 -26
- package/foundation/integrations/integration-warnings.test.mjs +74 -1
- package/foundation/integrations/integrations.d.mts +3 -0
- package/foundation/integrations/integrations.mjs +11 -97
- 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.mjs +121 -29
- 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 +12 -2
- package/foundation/response/error-codes.mjs +7 -1
- package/foundation/response/error-codes.test.mjs +55 -11
- package/foundation/response/response-types.doc.d.mts +5 -1
- package/foundation/response/response-types.doc.mjs +20 -11
- 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.mjs +1 -1
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +10 -9
- package/assets/templates/themes/manifest.json +0 -95
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Integration docs in the docs tree (spec:AST-046, spec:AST-047): an
|
|
5
|
+
* integration's namespace docs and placed guides join the CLI's tree with the
|
|
6
|
+
* same moves, links, search, and doctor checks, named by its provider id.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as fs from 'node:fs';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import {afterEach, beforeEach, describe, expect, it} from 'vitest';
|
|
12
|
+
import {docs} from './docs.mjs';
|
|
13
|
+
import {loadDocsCatalog} from './_adapter.mjs';
|
|
14
|
+
import {search} from '../search/search.mjs';
|
|
15
|
+
import {checkDocsTree} from '../doctor/doctor.mjs';
|
|
16
|
+
import {integrationDocConflicts} from '../integration/authoring-checks.mjs';
|
|
17
|
+
|
|
18
|
+
const SLOW = 60_000;
|
|
19
|
+
|
|
20
|
+
/** @type {string} */
|
|
21
|
+
let tmpDir;
|
|
22
|
+
|
|
23
|
+
const NAMESPACE = {
|
|
24
|
+
type: 'namespace',
|
|
25
|
+
name: 'acme',
|
|
26
|
+
title: 'Acme',
|
|
27
|
+
summary: 'Everything about the Acme kit.',
|
|
28
|
+
slots: {guides: {title: 'Guides', accepts: {kinds: ['generic']}}},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A guide placed in the acme namespace.
|
|
33
|
+
* @param {string} name
|
|
34
|
+
* @param {object} [fields]
|
|
35
|
+
*/
|
|
36
|
+
function guide(name, fields = {}) {
|
|
37
|
+
return {
|
|
38
|
+
type: 'generic',
|
|
39
|
+
name,
|
|
40
|
+
title: `Acme ${name}`,
|
|
41
|
+
description: `How to ${name} the Acme kit.`,
|
|
42
|
+
placement: {parent: 'namespace:acme', slot: 'guides'},
|
|
43
|
+
sections: [
|
|
44
|
+
{title: 'Overview', content: [{type: 'prose', text: `Do ${name}.`}]},
|
|
45
|
+
],
|
|
46
|
+
...fields,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The docs of a small integration: a namespace, two guides, and a topic. */
|
|
51
|
+
function kit() {
|
|
52
|
+
return {
|
|
53
|
+
'acme.doc.mjs': NAMESPACE,
|
|
54
|
+
'setup.doc.mjs': guide('setup', {
|
|
55
|
+
placement: {parent: 'namespace:acme', slot: 'guides', order: 1},
|
|
56
|
+
sections: [
|
|
57
|
+
{
|
|
58
|
+
title: 'Install',
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: 'prose',
|
|
62
|
+
text: 'Frobnicate the kit, then check it with {@link @astryxdesign/cli:command:doctor}.',
|
|
63
|
+
},
|
|
64
|
+
{type: 'prose', text: 'Next: {@link generic:deploy}.'},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
title: 'Configure',
|
|
69
|
+
content: [{type: 'prose', text: 'Add it to astryx.config.'}],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
}),
|
|
73
|
+
'deploy.doc.mjs': guide('deploy', {
|
|
74
|
+
placement: {parent: 'namespace:acme', slot: 'guides', order: 2},
|
|
75
|
+
}),
|
|
76
|
+
'notes.doc.mjs': {
|
|
77
|
+
type: 'generic',
|
|
78
|
+
name: 'acme-notes',
|
|
79
|
+
title: 'Acme notes',
|
|
80
|
+
description: 'Notes about the kit.',
|
|
81
|
+
sections: [
|
|
82
|
+
{
|
|
83
|
+
title: 'Start',
|
|
84
|
+
content: [{type: 'prose', text: 'Start at {@link generic:setup}.'}],
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** A guide whose only link names no doc. */
|
|
92
|
+
const broken = () =>
|
|
93
|
+
guide('broken', {
|
|
94
|
+
sections: [
|
|
95
|
+
{
|
|
96
|
+
title: 'Overview',
|
|
97
|
+
content: [{type: 'prose', text: 'See {@link generic:nope}.'}],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A consumer project that configures `@acme/kit`, whose manifest names a
|
|
104
|
+
* provider id that differs from its package name.
|
|
105
|
+
* @param {Record<string, object>} files
|
|
106
|
+
*/
|
|
107
|
+
function scaffold(files) {
|
|
108
|
+
fs.writeFileSync(
|
|
109
|
+
path.join(tmpDir, 'package.json'),
|
|
110
|
+
JSON.stringify({name: 'consumer'}),
|
|
111
|
+
);
|
|
112
|
+
fs.writeFileSync(
|
|
113
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
114
|
+
"export default {integrations: ['@acme/kit']};\n",
|
|
115
|
+
);
|
|
116
|
+
const pkgDir = path.join(tmpDir, 'node_modules', '@acme', 'kit');
|
|
117
|
+
fs.mkdirSync(path.join(pkgDir, 'docs'), {recursive: true});
|
|
118
|
+
fs.writeFileSync(
|
|
119
|
+
path.join(pkgDir, 'package.json'),
|
|
120
|
+
JSON.stringify({name: '@acme/kit', version: '1.0.0'}),
|
|
121
|
+
);
|
|
122
|
+
fs.writeFileSync(
|
|
123
|
+
path.join(pkgDir, 'astryx.integration.mjs'),
|
|
124
|
+
"export default {docs: './docs', providerId: '@acme/tree-provider'};\n",
|
|
125
|
+
);
|
|
126
|
+
for (const [file, doc] of Object.entries(files)) {
|
|
127
|
+
fs.writeFileSync(
|
|
128
|
+
path.join(pkgDir, 'docs', file),
|
|
129
|
+
`export const docs = ${JSON.stringify(doc, null, 2)};\n`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
tmpDir = fs.mkdtempSync(
|
|
136
|
+
path.join(process.cwd(), '.astryx-integration-tree-test-'),
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
afterEach(() => {
|
|
141
|
+
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('integration docs in the docs tree', () => {
|
|
145
|
+
it('names a package whose docs did not load, instead of dropping them silently', async () => {
|
|
146
|
+
scaffold({
|
|
147
|
+
...kit(),
|
|
148
|
+
'broken.doc.mjs': {
|
|
149
|
+
type: 'generic',
|
|
150
|
+
name: 'broken',
|
|
151
|
+
title: 'Broken',
|
|
152
|
+
description: 'A topic whose block no topic may hold.',
|
|
153
|
+
sections: [
|
|
154
|
+
{
|
|
155
|
+
title: 'Only',
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: 'workflow',
|
|
159
|
+
steps: [{title: 'Set up', references: ['generic:setup']}],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
const listed = await docs(undefined, undefined, {cwd: tmpDir});
|
|
167
|
+
expect(listed.meta?.notLoaded).toEqual([
|
|
168
|
+
expect.objectContaining({package: '@acme/kit', message: expect.any(String)}),
|
|
169
|
+
]);
|
|
170
|
+
}, 60_000);
|
|
171
|
+
|
|
172
|
+
it('lists an integration namespace beside the CLI, named by its provider id', async () => {
|
|
173
|
+
scaffold(kit());
|
|
174
|
+
const listed = await docs(undefined, undefined, {cwd: tmpDir});
|
|
175
|
+
expect(listed.meta.namespaces).toContainEqual({
|
|
176
|
+
topic: 'acme',
|
|
177
|
+
description: 'Everything about the Acme kit.',
|
|
178
|
+
package: '@acme/kit',
|
|
179
|
+
});
|
|
180
|
+
const top = await docs('acme', undefined, {cwd: tmpDir});
|
|
181
|
+
expect(top.type).toBe('docs.node');
|
|
182
|
+
expect(top.data).toMatchObject({
|
|
183
|
+
id: 'astryx:artifact:v1/%40acme%2Ftree-provider/namespace/acme',
|
|
184
|
+
package: '@acme/kit',
|
|
185
|
+
links: {up: 'astryx docs'},
|
|
186
|
+
});
|
|
187
|
+
expect(
|
|
188
|
+
top.data.slots[0].children.map((/** @type {any} */ c) => c.route),
|
|
189
|
+
).toEqual(['acme/setup', 'acme/deploy']);
|
|
190
|
+
}, SLOW);
|
|
191
|
+
|
|
192
|
+
it('reads a placed guide one level at a time, with its links resolved', async () => {
|
|
193
|
+
scaffold(kit());
|
|
194
|
+
const index = await docs('acme/setup', undefined, {cwd: tmpDir, index: true});
|
|
195
|
+
expect(index.type).toBe('docs.index');
|
|
196
|
+
expect(index.data.links.up).toBe('astryx docs acme');
|
|
197
|
+
const install = await docs('acme/setup', 'install', {cwd: tmpDir});
|
|
198
|
+
const [prose, next] = install.data.content;
|
|
199
|
+
expect(prose.text).toBe(
|
|
200
|
+
'Frobnicate the kit, then check it with `astryx docs cli/commands/doctor`.',
|
|
201
|
+
);
|
|
202
|
+
expect(next.text).toBe('Next: `astryx docs acme/deploy`.');
|
|
203
|
+
const notes = await docs('acme-notes', undefined, {cwd: tmpDir});
|
|
204
|
+
expect(notes.data.sections[0].content[0].text).toBe(
|
|
205
|
+
'Start at `astryx docs acme/setup`.',
|
|
206
|
+
);
|
|
207
|
+
}, SLOW);
|
|
208
|
+
|
|
209
|
+
it('finds a section of the guide by search, with its package and parent', async () => {
|
|
210
|
+
scaffold(kit());
|
|
211
|
+
const {data} = await search('frobnicate', {cwd: tmpDir, type: 'doc'});
|
|
212
|
+
expect(data.results[0]).toMatchObject({
|
|
213
|
+
name: 'acme/setup',
|
|
214
|
+
section: 'install',
|
|
215
|
+
command: 'astryx docs acme/setup install',
|
|
216
|
+
parent: 'astryx docs acme/setup --index',
|
|
217
|
+
package: '@acme/kit',
|
|
218
|
+
});
|
|
219
|
+
}, SLOW);
|
|
220
|
+
|
|
221
|
+
it('passes doctor, and warns on a link that names no doc', async () => {
|
|
222
|
+
scaffold(kit());
|
|
223
|
+
const ok = await checkDocsTree({
|
|
224
|
+
docsCatalog: await loadDocsCatalog(tmpDir),
|
|
225
|
+
});
|
|
226
|
+
expect(ok).toMatchObject({id: 'docs-tree', status: 'pass'});
|
|
227
|
+
scaffold({...kit(), 'broken.doc.mjs': broken()});
|
|
228
|
+
const bad = await checkDocsTree({
|
|
229
|
+
docsCatalog: await loadDocsCatalog(tmpDir),
|
|
230
|
+
});
|
|
231
|
+
expect(bad.status).toBe('warn');
|
|
232
|
+
expect(bad.message).toContain('acme/broken');
|
|
233
|
+
expect(bad.message).toContain('"generic:nope" names no doc');
|
|
234
|
+
}, SLOW);
|
|
235
|
+
|
|
236
|
+
it("keeps the CLI's routes: an integration namespace or topic that claims one is withdrawn, and both doctors say so", async () => {
|
|
237
|
+
const flat = (/** @type {string} */ name) => ({
|
|
238
|
+
type: 'generic',
|
|
239
|
+
name,
|
|
240
|
+
title: `Acme ${name}`,
|
|
241
|
+
description: `Acme's ${name} notes.`,
|
|
242
|
+
sections: [
|
|
243
|
+
{title: 'One', content: [{type: 'prose', text: 'One.'}]},
|
|
244
|
+
{title: 'Two', content: [{type: 'prose', text: 'Two.'}]},
|
|
245
|
+
],
|
|
246
|
+
});
|
|
247
|
+
scaffold({
|
|
248
|
+
...kit(),
|
|
249
|
+
'unorganized.doc.mjs': {...NAMESPACE, name: 'unorganized', title: 'Mine', summary: 'Mine.'},
|
|
250
|
+
'tokens.doc.mjs': {...NAMESPACE, name: 'tokens', title: 'Acme tokens', summary: 'Acme tokens.'},
|
|
251
|
+
'cli.doc.mjs': flat('cli'),
|
|
252
|
+
});
|
|
253
|
+
const listed = await docs(undefined, undefined, {cwd: tmpDir});
|
|
254
|
+
expect(listed.meta.namespaces).toEqual(
|
|
255
|
+
expect.arrayContaining([
|
|
256
|
+
expect.objectContaining({topic: 'cli', package: '@astryxdesign/cli'}),
|
|
257
|
+
expect.objectContaining({topic: 'unorganized', package: '@astryxdesign/cli'}),
|
|
258
|
+
]),
|
|
259
|
+
);
|
|
260
|
+
expect(listed.meta.namespaces.filter(n => ['tokens', 'unorganized'].includes(n.topic) && n.package === '@acme/kit')).toEqual([]);
|
|
261
|
+
expect(listed.data.map(entry => entry.topic)).not.toContain('cli');
|
|
262
|
+
// Each name opens the CLI's doc, and a CLI topic keeps its home.
|
|
263
|
+
expect((await docs('cli', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'cli', package: '@astryxdesign/cli'});
|
|
264
|
+
expect((await docs('unorganized', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'unorganized', package: '@astryxdesign/cli'});
|
|
265
|
+
const tokens = await docs('tokens', undefined, {cwd: tmpDir});
|
|
266
|
+
expect(tokens.type).toBe('docs.detail');
|
|
267
|
+
expect(tokens.data.links.up).toBe('astryx docs unorganized');
|
|
268
|
+
// The project doctor and the package doctor both name the claims.
|
|
269
|
+
const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
|
|
270
|
+
expect(check.status).toBe('warn');
|
|
271
|
+
expect(check.message).toContain('takes the route "unorganized", which the CLI\'s own docs keep');
|
|
272
|
+
expect(check.message).toContain('takes the route "tokens"');
|
|
273
|
+
expect(check.message).toContain('both have the route "cli"');
|
|
274
|
+
const result = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
|
|
275
|
+
const graph = result.data.issues.filter(issue => issue.code === 'invalid_doc_graph').map(issue => issue.message).join('\n');
|
|
276
|
+
expect(graph).toContain('takes the route "unorganized"');
|
|
277
|
+
expect(graph).toContain('takes the route "tokens"');
|
|
278
|
+
expect(graph).toContain('route "cli"');
|
|
279
|
+
}, SLOW);
|
|
280
|
+
|
|
281
|
+
it("keeps the CLI's routes from a claim that differs only in case", async () => {
|
|
282
|
+
const flat = (/** @type {string} */ name) => ({
|
|
283
|
+
type: 'generic',
|
|
284
|
+
name,
|
|
285
|
+
title: `Acme ${name}`,
|
|
286
|
+
description: `Acme's ${name} notes.`,
|
|
287
|
+
sections: [
|
|
288
|
+
{title: 'One', content: [{type: 'prose', text: 'One.'}]},
|
|
289
|
+
{title: 'Two', content: [{type: 'prose', text: 'Two.'}]},
|
|
290
|
+
],
|
|
291
|
+
});
|
|
292
|
+
scaffold({...kit(), 'caps-cli.doc.mjs': flat('CLI')});
|
|
293
|
+
// Another spelling, in a nested folder of the package's docs.
|
|
294
|
+
const nested = path.join(tmpDir, 'node_modules', '@acme', 'kit', 'docs', 'nested');
|
|
295
|
+
fs.mkdirSync(nested, {recursive: true});
|
|
296
|
+
fs.writeFileSync(
|
|
297
|
+
path.join(nested, 'caps-unorganized.doc.mjs'),
|
|
298
|
+
`export const docs = ${JSON.stringify(flat('Unorganized'), null, 2)};\n`,
|
|
299
|
+
);
|
|
300
|
+
fs.writeFileSync(
|
|
301
|
+
path.join(nested, 'caps-tokens.doc.mjs'),
|
|
302
|
+
`export const docs = ${JSON.stringify(flat('TOKENS'), null, 2)};\n`,
|
|
303
|
+
);
|
|
304
|
+
expect((await docs('cli', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'cli', package: '@astryxdesign/cli'});
|
|
305
|
+
expect((await docs('unorganized', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'unorganized', package: '@astryxdesign/cli'});
|
|
306
|
+
expect((await docs('theme', undefined, {cwd: tmpDir, index: true})).data.links.up).toBe('astryx docs unorganized');
|
|
307
|
+
const listed = await docs(undefined, undefined, {cwd: tmpDir});
|
|
308
|
+
expect(listed.data.map(entry => entry.topic.toLowerCase())).not.toContain('cli');
|
|
309
|
+
// Search does not offer a topic whose route another doc owns.
|
|
310
|
+
const found = await search("Acme's CLI notes", {cwd: tmpDir, type: 'doc', limit: 20});
|
|
311
|
+
expect(
|
|
312
|
+
found.data.results.filter(hit => hit.package === '@acme/kit' && /^cli$/i.test(hit.name)),
|
|
313
|
+
).toEqual([]);
|
|
314
|
+
const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
|
|
315
|
+
expect(check.status).toBe('warn');
|
|
316
|
+
expect(check.message).toContain('route "CLI"');
|
|
317
|
+
expect(check.message).toContain('takes the route "Unorganized"');
|
|
318
|
+
// `TOKENS` is the CLI's topic `tokens` in another spelling: the CLI's wins.
|
|
319
|
+
const tokens = await docs('TOKENS', undefined, {cwd: tmpDir, index: true});
|
|
320
|
+
expect(tokens.data).toMatchObject({name: 'tokens', links: {up: 'astryx docs unorganized'}});
|
|
321
|
+
expect(tokens.data.title).not.toBe('Acme TOKENS');
|
|
322
|
+
const result = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
|
|
323
|
+
const graph = result.data.issues
|
|
324
|
+
.filter(issue => issue.code === 'invalid_doc_graph')
|
|
325
|
+
.map(issue => issue.message)
|
|
326
|
+
.join('\n');
|
|
327
|
+
expect(graph).toContain('route "CLI"');
|
|
328
|
+
expect(graph).toContain('takes the route "Unorganized"');
|
|
329
|
+
}, SLOW);
|
|
330
|
+
|
|
331
|
+
it("keeps a replaced name for the topic that replaces it, over another integration's namespace", async () => {
|
|
332
|
+
scaffold({...kit(), 'tokens-ns.doc.mjs': {...NAMESPACE, name: 'tokens', title: 'Kit tokens', summary: 'Kit tokens.'}});
|
|
333
|
+
const two = path.join(tmpDir, 'node_modules', '@acme', 'two');
|
|
334
|
+
fs.mkdirSync(path.join(two, 'docs'), {recursive: true});
|
|
335
|
+
fs.writeFileSync(path.join(two, 'package.json'), JSON.stringify({name: '@acme/two', version: '1.0.0'}));
|
|
336
|
+
fs.writeFileSync(path.join(two, 'astryx.integration.mjs'), "export default {docs: './docs'};\n");
|
|
337
|
+
fs.writeFileSync(
|
|
338
|
+
path.join(two, 'docs', 'acme-tokens.doc.mjs'),
|
|
339
|
+
`export const docs = ${JSON.stringify({
|
|
340
|
+
type: 'generic',
|
|
341
|
+
name: 'acme-tokens',
|
|
342
|
+
title: 'Acme tokens',
|
|
343
|
+
description: 'Acme tokens.',
|
|
344
|
+
replaces: 'tokens',
|
|
345
|
+
sections: [{title: 'One', content: [{type: 'prose', text: 'One.'}]}],
|
|
346
|
+
}, null, 2)};\n`,
|
|
347
|
+
);
|
|
348
|
+
fs.writeFileSync(
|
|
349
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
350
|
+
"export default {integrations: ['@acme/kit', '@acme/two']};\n",
|
|
351
|
+
);
|
|
352
|
+
const listed = await docs(undefined, undefined, {cwd: tmpDir});
|
|
353
|
+
expect(listed.meta.namespaces.map(entry => entry.topic)).not.toContain('tokens');
|
|
354
|
+
const read = await docs('tokens', undefined, {cwd: tmpDir, index: true});
|
|
355
|
+
expect(read.data.name).toBe('acme-tokens');
|
|
356
|
+
const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
|
|
357
|
+
expect(check.status).toBe('warn');
|
|
358
|
+
expect(check.message).toContain('which the topic "acme-tokens" also answers to');
|
|
359
|
+
}, SLOW);
|
|
360
|
+
|
|
361
|
+
it('keeps every name a topic replaced twice answers to, and the CLI\'s links to it still open', async () => {
|
|
362
|
+
scaffold({...kit(), 'tokens-ns.doc.mjs': {...NAMESPACE, name: 'tokens', title: 'Kit tokens', summary: 'Kit tokens.'}});
|
|
363
|
+
/** @param {string} pkg @param {string} name @param {string} replaces */
|
|
364
|
+
const replacement = (pkg, name, replaces) => {
|
|
365
|
+
const dir = path.join(tmpDir, 'node_modules', '@acme', pkg);
|
|
366
|
+
fs.mkdirSync(path.join(dir, 'docs'), {recursive: true});
|
|
367
|
+
fs.writeFileSync(path.join(dir, 'package.json'), JSON.stringify({name: `@acme/${pkg}`, version: '1.0.0'}));
|
|
368
|
+
fs.writeFileSync(path.join(dir, 'astryx.integration.mjs'), "export default {docs: './docs'};\n");
|
|
369
|
+
fs.writeFileSync(
|
|
370
|
+
path.join(dir, 'docs', `${name}.doc.mjs`),
|
|
371
|
+
`export const docs = ${JSON.stringify({
|
|
372
|
+
type: 'generic',
|
|
373
|
+
name,
|
|
374
|
+
title: `Tokens by ${pkg}`,
|
|
375
|
+
description: `Tokens by ${pkg}.`,
|
|
376
|
+
replaces,
|
|
377
|
+
sections: [{title: 'One', content: [{type: 'prose', text: 'One.'}]}],
|
|
378
|
+
}, null, 2)};\n`,
|
|
379
|
+
);
|
|
380
|
+
};
|
|
381
|
+
replacement('two', 'acme-tokens', 'tokens');
|
|
382
|
+
replacement('three', 'b-tokens', 'tokens');
|
|
383
|
+
replacement('four', 'super-tokens', 'b-tokens');
|
|
384
|
+
fs.writeFileSync(
|
|
385
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
386
|
+
"export default {integrations: ['@acme/kit', '@acme/two', '@acme/three', '@acme/four']};\n",
|
|
387
|
+
);
|
|
388
|
+
const listed = await docs(undefined, undefined, {cwd: tmpDir});
|
|
389
|
+
expect(listed.meta.namespaces.map(entry => entry.topic)).not.toContain('tokens');
|
|
390
|
+
for (const name of ['tokens', 'acme-tokens', 'b-tokens', 'super-tokens']) {
|
|
391
|
+
expect((await docs(name, undefined, {cwd: tmpDir, index: true})).data.name).toBe('super-tokens');
|
|
392
|
+
}
|
|
393
|
+
// A walk over every topic: the CLI's own links to `tokens` still open it.
|
|
394
|
+
/** @type {string[]} */
|
|
395
|
+
const raw = [];
|
|
396
|
+
for (const entry of listed.data) {
|
|
397
|
+
const read = await docs(entry.topic, undefined, {cwd: tmpDir});
|
|
398
|
+
if (/\{@link [^}]*tokens\}/.test(JSON.stringify(read.data))) raw.push(entry.topic);
|
|
399
|
+
}
|
|
400
|
+
expect(raw).toEqual([]);
|
|
401
|
+
const principles = await docs('principles', undefined, {cwd: tmpDir});
|
|
402
|
+
expect(JSON.stringify(principles.data)).toContain('astryx docs super-tokens');
|
|
403
|
+
const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
|
|
404
|
+
expect(check.message).toContain('takes the route "tokens", which the topic "super-tokens" also answers to');
|
|
405
|
+
expect(check.message).not.toContain('names no doc');
|
|
406
|
+
}, SLOW);
|
|
407
|
+
|
|
408
|
+
it("keeps the CLI's routes from a replacement that answers to one, in one package or across a chain", async () => {
|
|
409
|
+
const flat = (/** @type {string} */ name, /** @type {object} */ extra = {}) => ({
|
|
410
|
+
type: 'generic',
|
|
411
|
+
name,
|
|
412
|
+
title: `Acme ${name}`,
|
|
413
|
+
description: `Acme's ${name} notes.`,
|
|
414
|
+
sections: [
|
|
415
|
+
{title: 'One', content: [{type: 'prose', text: 'One.'}]},
|
|
416
|
+
{title: 'Two', content: [{type: 'prose', text: 'Two.'}]},
|
|
417
|
+
],
|
|
418
|
+
...extra,
|
|
419
|
+
});
|
|
420
|
+
scaffold({
|
|
421
|
+
...kit(),
|
|
422
|
+
'a-cli.doc.mjs': flat('cli'),
|
|
423
|
+
'a-unorganized.doc.mjs': flat('Unorganized'),
|
|
424
|
+
'z-b2.doc.mjs': flat('b2', {replaces: 'cli'}),
|
|
425
|
+
'z-u2.doc.mjs': flat('u2', {replaces: 'Unorganized'}),
|
|
426
|
+
});
|
|
427
|
+
// Another package carries the chain on: c3 replaces b2, which replaced cli.
|
|
428
|
+
const three = path.join(tmpDir, 'node_modules', '@acme', 'three');
|
|
429
|
+
fs.mkdirSync(path.join(three, 'docs'), {recursive: true});
|
|
430
|
+
fs.writeFileSync(path.join(three, 'package.json'), JSON.stringify({name: '@acme/three', version: '1.0.0'}));
|
|
431
|
+
fs.writeFileSync(path.join(three, 'astryx.integration.mjs'), "export default {docs: './docs'};\n");
|
|
432
|
+
fs.writeFileSync(
|
|
433
|
+
path.join(three, 'docs', 'c3.doc.mjs'),
|
|
434
|
+
`export const docs = ${JSON.stringify(flat('c3', {replaces: 'b2'}), null, 2)};\n`,
|
|
435
|
+
);
|
|
436
|
+
fs.writeFileSync(
|
|
437
|
+
path.join(tmpDir, 'astryx.config.mjs'),
|
|
438
|
+
"export default {integrations: ['@acme/kit', '@acme/three']};\n",
|
|
439
|
+
);
|
|
440
|
+
for (const name of ['cli', 'CLI']) {
|
|
441
|
+
expect((await docs(name, undefined, {cwd: tmpDir})).data).toMatchObject({route: 'cli', package: '@astryxdesign/cli'});
|
|
442
|
+
}
|
|
443
|
+
for (const name of ['unorganized', 'UNORGANIZED']) {
|
|
444
|
+
expect((await docs(name, undefined, {cwd: tmpDir})).data).toMatchObject({route: 'unorganized', package: '@astryxdesign/cli'});
|
|
445
|
+
}
|
|
446
|
+
// The replacement still answers to its own names.
|
|
447
|
+
for (const name of ['b2', 'c3']) {
|
|
448
|
+
expect((await docs(name, undefined, {cwd: tmpDir, index: true})).data.name).toBe('c3');
|
|
449
|
+
}
|
|
450
|
+
const listed = await docs(undefined, undefined, {cwd: tmpDir});
|
|
451
|
+
for (const entry of listed.data) {
|
|
452
|
+
const read = await docs(entry.topic, undefined, {cwd: tmpDir});
|
|
453
|
+
expect(read.type).toBe('docs.detail');
|
|
454
|
+
expect(read.data.name).toBe(entry.topic);
|
|
455
|
+
}
|
|
456
|
+
const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
|
|
457
|
+
expect(check.status).toBe('warn');
|
|
458
|
+
expect(check.message).toContain('The topic "c3" answers to "cli" through replaces');
|
|
459
|
+
expect(check.message).toContain('The topic "u2" answers to "unorganized" through replaces');
|
|
460
|
+
const inPackage = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
|
|
461
|
+
const graph = inPackage.data.issues
|
|
462
|
+
.filter(issue => issue.code === 'invalid_doc_graph')
|
|
463
|
+
.map(issue => issue.message)
|
|
464
|
+
.join('\n');
|
|
465
|
+
expect(graph).toContain('answers to "cli" through replaces');
|
|
466
|
+
}, SLOW);
|
|
467
|
+
|
|
468
|
+
it('runs the same checks inside the package: doctor integration docs', async () => {
|
|
469
|
+
scaffold({...kit(), 'broken.doc.mjs': broken()});
|
|
470
|
+
const result = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
|
|
471
|
+
expect(result.data.issues).toContainEqual(
|
|
472
|
+
expect.objectContaining({
|
|
473
|
+
code: 'invalid_doc_graph',
|
|
474
|
+
severity: 'warning',
|
|
475
|
+
message: expect.stringContaining('"generic:nope" names no doc'),
|
|
476
|
+
}),
|
|
477
|
+
);
|
|
478
|
+
}, SLOW);
|
|
479
|
+
|
|
480
|
+
it("resolves an extension's links against the extension's provider, not the base topic's", async () => {
|
|
481
|
+
scaffold({
|
|
482
|
+
...kit(),
|
|
483
|
+
'integrations.doc.mjs': guide('integrations'),
|
|
484
|
+
'theme-acme.doc.mjs': {
|
|
485
|
+
type: 'generic',
|
|
486
|
+
name: 'theme-acme',
|
|
487
|
+
title: 'Acme theming',
|
|
488
|
+
description: 'Acme notes on theming.',
|
|
489
|
+
extends: 'theme',
|
|
490
|
+
sections: [
|
|
491
|
+
{
|
|
492
|
+
title: 'Acme theming notes',
|
|
493
|
+
content: [
|
|
494
|
+
{
|
|
495
|
+
type: 'prose',
|
|
496
|
+
text: 'See {@link generic:integrations}, and {@link @astryxdesign/cli:generic:tokens} for the tokens.',
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
},
|
|
500
|
+
],
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
const {data} = await docs('theme', 'acme-theming-notes', {cwd: tmpDir});
|
|
504
|
+
// `generic:integrations` is the extension's own guide, never the CLI's
|
|
505
|
+
// `cli/integrations`; a provider-qualified link reaches the CLI's topic.
|
|
506
|
+
expect(data.content[0].text).toBe(
|
|
507
|
+
'See `astryx docs acme/integrations`, and `astryx docs tokens` for the tokens.',
|
|
508
|
+
);
|
|
509
|
+
// The CLI's own sections of the topic still resolve against the CLI.
|
|
510
|
+
const full = await docs('theme', undefined, {cwd: tmpDir});
|
|
511
|
+
expect(JSON.stringify(full.data)).not.toContain('{@link');
|
|
512
|
+
}, SLOW);
|
|
513
|
+
|
|
514
|
+
it("warns, in the project and in the package, on an extension link that names no doc of its provider", async () => {
|
|
515
|
+
scaffold({
|
|
516
|
+
...kit(),
|
|
517
|
+
'theme-acme.doc.mjs': {
|
|
518
|
+
type: 'generic',
|
|
519
|
+
name: 'theme-acme',
|
|
520
|
+
title: 'Acme theming',
|
|
521
|
+
description: 'Acme notes on theming.',
|
|
522
|
+
extends: 'theme',
|
|
523
|
+
sections: [
|
|
524
|
+
{
|
|
525
|
+
title: 'Acme theming notes',
|
|
526
|
+
content: [{type: 'prose', text: 'See {@link generic:tokens}.'}],
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
const check = await checkDocsTree({
|
|
532
|
+
docsCatalog: await loadDocsCatalog(tmpDir),
|
|
533
|
+
});
|
|
534
|
+
expect(check.status).toBe('warn');
|
|
535
|
+
expect(check.message).toContain('"generic:tokens" names no doc');
|
|
536
|
+
const result = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
|
|
537
|
+
expect(result.data.issues).toContainEqual(
|
|
538
|
+
expect.objectContaining({
|
|
539
|
+
code: 'invalid_doc_graph',
|
|
540
|
+
message: expect.stringContaining('"generic:tokens" names no doc'),
|
|
541
|
+
}),
|
|
542
|
+
);
|
|
543
|
+
}, SLOW);
|
|
544
|
+
|
|
545
|
+
it('keeps the CLI route when an integration claims it, and names the claim', async () => {
|
|
546
|
+
scaffold({'cli.doc.mjs': {...NAMESPACE, name: 'cli', title: 'Not the CLI'}});
|
|
547
|
+
const top = await docs('cli', undefined, {cwd: tmpDir});
|
|
548
|
+
expect(top.data.package).toBe('@astryxdesign/cli');
|
|
549
|
+
const check = await checkDocsTree({
|
|
550
|
+
docsCatalog: await loadDocsCatalog(tmpDir),
|
|
551
|
+
});
|
|
552
|
+
expect(check.status).toBe('warn');
|
|
553
|
+
expect(check.message).toContain('both have the route "cli"');
|
|
554
|
+
}, SLOW);
|
|
555
|
+
});
|
|
@@ -102,7 +102,7 @@ describe('integration-contributed topics', () => {
|
|
|
102
102
|
// The built-in topics keep their own owner.
|
|
103
103
|
expect(listed.data.find(t => t.topic === 'tokens').package).toBe('@astryxdesign/cli');
|
|
104
104
|
|
|
105
|
-
const detail = await docs('deploying', undefined, {cwd: tmpDir});
|
|
105
|
+
const detail = await docs('deploying', undefined, {cwd: tmpDir, full: true});
|
|
106
106
|
expect(detail.type).toBe('docs.detail');
|
|
107
107
|
expect(detail.data.sections[0].content[0].text).toBe('Push the button.');
|
|
108
108
|
|
|
@@ -129,7 +129,7 @@ describe('integration-contributed topics', () => {
|
|
|
129
129
|
}),
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
const detail = await docs('getting-started', undefined, {cwd: tmpDir});
|
|
132
|
+
const detail = await docs('getting-started', undefined, {cwd: tmpDir, full: true});
|
|
133
133
|
expect(detail.data.sections.map(s => s.title)).toEqual(['Install']);
|
|
134
134
|
expect(detail.data.sections[0].content[0].text).toBe('yarn add @acme/widgets');
|
|
135
135
|
|
|
@@ -146,14 +146,14 @@ describe('integration-contributed topics', () => {
|
|
|
146
146
|
scaffold({
|
|
147
147
|
'setup.doc.mjs': topic({name: 'setup', replaces: 'getting-started'}),
|
|
148
148
|
});
|
|
149
|
-
const byOldName = await docs('getting-started', undefined, {cwd: tmpDir});
|
|
150
|
-
const byNewName = await docs('setup', undefined, {cwd: tmpDir});
|
|
149
|
+
const byOldName = await docs('getting-started', undefined, {cwd: tmpDir, full: true});
|
|
150
|
+
const byNewName = await docs('setup', undefined, {cwd: tmpDir, full: true});
|
|
151
151
|
expect(byOldName.data.name).toBe('setup');
|
|
152
152
|
expect(byNewName.data.name).toBe('setup');
|
|
153
153
|
}, SLOW);
|
|
154
154
|
|
|
155
155
|
it('merges an extension into the topic it extends', async () => {
|
|
156
|
-
const builtin = await docs('theme');
|
|
156
|
+
const builtin = await docs('theme', undefined, {full: true});
|
|
157
157
|
const baseSectionTitle = builtin.data.sections[0].title;
|
|
158
158
|
scaffold({
|
|
159
159
|
'theme-internal.doc.mjs': topic({
|
|
@@ -166,7 +166,7 @@ describe('integration-contributed topics', () => {
|
|
|
166
166
|
}),
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
const extended = await docs('theme', undefined, {cwd: tmpDir});
|
|
169
|
+
const extended = await docs('theme', undefined, {cwd: tmpDir, full: true});
|
|
170
170
|
// The extension is not a topic of its own.
|
|
171
171
|
expect((await docs(undefined, undefined, {cwd: tmpDir})).data.some(
|
|
172
172
|
t => t.topic === 'theme-internal',
|
|
@@ -178,7 +178,7 @@ describe('integration-contributed topics', () => {
|
|
|
178
178
|
}, SLOW);
|
|
179
179
|
|
|
180
180
|
it('migrates a real built-in section to a stable ID without duplicating it', async () => {
|
|
181
|
-
const builtin = await docs('theme');
|
|
181
|
+
const builtin = await docs('theme', undefined, {full: true});
|
|
182
182
|
// Readers see a key on every section; the migration case is one whose
|
|
183
183
|
// source authors no id.
|
|
184
184
|
const {docs: authored} = await import('../../assets/docs/theme.doc.mjs');
|
|
@@ -198,7 +198,7 @@ describe('integration-contributed topics', () => {
|
|
|
198
198
|
}),
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
-
const extended = await docs('theme', undefined, {cwd: tmpDir});
|
|
201
|
+
const extended = await docs('theme', undefined, {cwd: tmpDir, full: true});
|
|
202
202
|
expect(extended.data.sections).toHaveLength(builtin.data.sections.length);
|
|
203
203
|
const matches = extended.data.sections.filter(
|
|
204
204
|
section => section.title === target.title,
|
|
@@ -228,7 +228,7 @@ describe('integration-contributed topics', () => {
|
|
|
228
228
|
}),
|
|
229
229
|
});
|
|
230
230
|
|
|
231
|
-
const extended = await docs('theme', undefined, {cwd: tmpDir});
|
|
231
|
+
const extended = await docs('theme', undefined, {cwd: tmpDir, full: true});
|
|
232
232
|
expect(extended.data.sections).toHaveLength(index.data.sections.length);
|
|
233
233
|
expect(extended.data.sections.filter(s => s.id === target.id)).toEqual([
|
|
234
234
|
expect.objectContaining({content: [{type: 'prose', text: 'Acme first.'}]}),
|
|
@@ -240,7 +240,7 @@ describe('integration-contributed topics', () => {
|
|
|
240
240
|
it.each(['zh', 'dense'])(
|
|
241
241
|
'replaces translated real sections by their authored titles under --%s',
|
|
242
242
|
async lang => {
|
|
243
|
-
const english = await docs('theme');
|
|
243
|
+
const english = await docs('theme', undefined, {full: true});
|
|
244
244
|
const englishTitles = english.data.sections.map(section => section.title);
|
|
245
245
|
expect(englishTitles).toEqual(
|
|
246
246
|
expect.arrayContaining(['Quick Start', 'Theme Props']),
|
|
@@ -263,11 +263,11 @@ describe('integration-contributed topics', () => {
|
|
|
263
263
|
}),
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
-
const base = await docs('theme', undefined, {lang});
|
|
266
|
+
const base = await docs('theme', undefined, {lang, full: true});
|
|
267
267
|
expect(base.data.sections.map(section => section.title)).not.toEqual(
|
|
268
268
|
englishTitles,
|
|
269
269
|
);
|
|
270
|
-
const extended = await docs('theme', undefined, {cwd: tmpDir, lang});
|
|
270
|
+
const extended = await docs('theme', undefined, {cwd: tmpDir, lang, full: true});
|
|
271
271
|
expect(extended.data.sections).toHaveLength(base.data.sections.length);
|
|
272
272
|
expect(
|
|
273
273
|
extended.data.sections.filter(
|
|
@@ -285,11 +285,11 @@ describe('integration-contributed topics', () => {
|
|
|
285
285
|
|
|
286
286
|
it('offers the contributed topics as suggestions on an unknown one', async () => {
|
|
287
287
|
scaffold({'deploying.doc.mjs': topic()});
|
|
288
|
-
await expect(docs('nope-not-a-topic', undefined, {cwd: tmpDir})).rejects.toBeInstanceOf(
|
|
288
|
+
await expect(docs('nope-not-a-topic', undefined, {cwd: tmpDir, full: true})).rejects.toBeInstanceOf(
|
|
289
289
|
AstryxError,
|
|
290
290
|
);
|
|
291
291
|
try {
|
|
292
|
-
await docs('nope-not-a-topic', undefined, {cwd: tmpDir});
|
|
292
|
+
await docs('nope-not-a-topic', undefined, {cwd: tmpDir, full: true});
|
|
293
293
|
} catch (err) {
|
|
294
294
|
expect(err.suggestions.map(s => s.name)).toContain('deploying');
|
|
295
295
|
}
|