@astryxdesign/cli 0.1.7 → 0.1.8-canary.07362a9
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 +40 -0
- package/README.md +115 -19
- package/{src/api → api/blog}/blog.mjs +42 -6
- package/{src/api → api/blog}/blog.test.mjs +2 -2
- package/api/build/build.mjs +92 -0
- package/api/build/build.test.mjs +92 -0
- package/{src/api → api/component}/component.mjs +49 -26
- package/{src/api → api/discover}/discover.mjs +55 -18
- package/{src/api → api/docs}/docs.mjs +47 -21
- package/{src/api → api/doctor}/doctor.mjs +13 -10
- package/{src/api → api}/error.mjs +2 -2
- package/{src/api → api/hook}/hook.mjs +11 -11
- package/{src/api → api}/index.mjs +16 -9
- package/{src/api → api/integration}/validate-integration.mjs +48 -15
- package/{src/api → api/integration}/validate-integration.test.mjs +1 -1
- package/{src/api → api/layout}/layout.mjs +66 -17
- package/{src/api → api/layout}/layout.test.mjs +1 -1
- package/{src/api → api/search}/search.mjs +71 -12
- package/api/swizzle/swizzle.mjs +283 -0
- package/{src/commands → api/swizzle}/swizzle.test.mjs +26 -1
- package/{src/api → api/template}/template-integration.test.mjs +2 -2
- package/{src/api → api/template}/template-suffix.test.mjs +3 -3
- package/{src/api → api/template}/template.mjs +96 -19
- package/{src/api → api/theme}/theme-add.mjs +35 -7
- package/{src → authoring}/codemod.mjs +6 -6
- package/{src → authoring}/config.test.mjs +1 -1
- package/{src → authoring}/doc.mjs +1 -1
- package/{src → authoring}/doc.test.mjs +1 -1
- package/{src → authoring}/template.mjs +1 -1
- package/bin/astryx.mjs +12 -10
- package/{src → cli}/commands/blog.mjs +48 -7
- package/{src → cli}/commands/build-theme.mjs +133 -115
- package/cli/commands/build.mjs +172 -0
- package/cli/commands/cli-postinstall.test.mjs +42 -0
- package/{src → cli}/commands/component/index.mjs +52 -25
- package/{src → cli}/commands/component-ownership.test.mjs +3 -3
- package/{src → cli}/commands/component-package.test.mjs +1 -1
- package/{src → cli}/commands/component-resolution.test.mjs +2 -2
- package/{src → cli}/commands/discover.mjs +23 -12
- package/{src → cli}/commands/docs.mjs +42 -10
- package/{src → cli}/commands/docs.test.mjs +1 -1
- package/{src → cli}/commands/doctor.mjs +3 -3
- package/{src → cli}/commands/doctor.test.mjs +2 -2
- package/{src → cli}/commands/ensure-core-built.mjs +3 -2
- package/{src → cli}/commands/external-showcase.test.mjs +1 -1
- package/{src → cli}/commands/hook/index.mjs +40 -17
- package/{src → cli}/commands/import-hint-correctness.test.mjs +1 -1
- package/cli/commands/init.mjs +195 -0
- package/{src → cli}/commands/init.next-steps.test.mjs +1 -1
- package/cli/commands/interactive-guard.test.mjs +66 -0
- package/{src → cli}/commands/json-contract.test.mjs +2 -2
- package/{src → cli}/commands/layout.mjs +59 -19
- package/{src → cli}/commands/search.mjs +22 -15
- package/{src → cli}/commands/search.test.mjs +1 -1
- package/cli/commands/setup-nudge.test.mjs +108 -0
- package/cli/commands/swizzle.mjs +103 -0
- package/{src → cli}/commands/template.mjs +84 -53
- package/{src → cli}/commands/template.path-safety.test.mjs +2 -2
- package/{src → cli}/commands/upgrade.integration-policy.test.mjs +1 -1
- package/{src → cli}/commands/upgrade.mjs +291 -77
- package/cli/commands/upgrade.test.mjs +314 -0
- package/{src → cli}/commands/validate-integration.mjs +17 -37
- package/{src → cli}/index.mjs +55 -19
- package/{src/codemods → codemods}/__tests__/registry.test.mjs +1 -0
- package/{src/codemods → codemods}/ensure-jscodeshift.mjs +12 -28
- package/{src/codemods → codemods}/integration-discovery.mjs +6 -3
- package/{src/codemods → codemods}/integration-discovery.test.mjs +1 -1
- package/{src/codemods → codemods}/integration-runner.mjs +8 -3
- package/{src/codemods → codemods}/registry.mjs +4 -1
- package/{src/codemods → codemods}/run-codemod.mjs +18 -11
- package/{src/codemods → codemods}/runner.mjs +24 -14
- package/{src/codemods → codemods}/transforms/v0.0.10/remove-size-props.mjs +9 -4
- package/{src/codemods → codemods}/transforms/v0.0.12/add-is-icon-only.mjs +27 -22
- package/{src/codemods → codemods}/transforms/v0.0.13/icon-name-deprecations.mjs +12 -5
- package/{src/codemods → codemods}/transforms/v0.0.13/rename-attachments-to-drawer.mjs +15 -8
- package/{src/codemods → codemods}/transforms/v0.0.13/toolbar-density-to-size.mjs +10 -4
- package/{src/codemods → codemods}/transforms/v0.0.14/rename-action-props.mjs +11 -5
- package/{src/codemods → codemods}/transforms/v0.0.14/rename-section-wash-to-muted.mjs +11 -6
- package/{src/codemods → codemods}/transforms/v0.0.14/rename-status-variants.mjs +15 -10
- package/{src/codemods → codemods}/transforms/v0.0.15/migrate-item-children-to-endcontent.mjs +13 -8
- package/{src/codemods → codemods}/transforms/v0.0.15/migrate-selector-children-to-render-option.mjs +18 -13
- package/{src/codemods → codemods}/transforms/v0.0.15/migrate-theme-selectors-to-data-attrs.mjs +8 -4
- package/{src/codemods → codemods}/transforms/v0.0.15/rename-date-picker-to-input.mjs +11 -6
- package/{src/codemods → codemods}/transforms/v0.0.15/rename-imperative-ref-to-handleRef.mjs +6 -1
- package/{src/codemods → codemods}/transforms/v0.0.15/rename-isStreaming-to-isStopShown.mjs +6 -1
- package/{src/codemods → codemods}/transforms/v0.0.15/rename-stack-element-to-as.mjs +8 -3
- package/{src/codemods → codemods}/transforms/v0.0.2/migrate-badge-dot-to-statusdot.mjs +17 -10
- package/{src/codemods → codemods}/transforms/v0.0.2/migrate-gap-to-numeric.mjs +7 -1
- package/{src/codemods → codemods}/transforms/v0.0.2/migrate-isFullBleed-to-padding.mjs +9 -4
- package/{src/codemods → codemods}/transforms/v0.0.2/migrate-useXDSIcon-to-getIcon.mjs +12 -7
- package/{src/codemods → codemods}/transforms/v0.0.2/rename-banner-endButton-to-endContent.mjs +7 -2
- package/{src/codemods → codemods}/transforms/v0.0.2/rename-form-tooltip-startIcon.mjs +8 -2
- package/{src/codemods → codemods}/transforms/v0.0.2/rename-isShown-to-isOpen.mjs +7 -2
- package/{src/codemods → codemods}/transforms/v0.0.2/rename-selector-items-to-options.mjs +7 -2
- package/{src/codemods → codemods}/transforms/v0.0.2/rename-sidenav-header-to-heading.mjs +11 -4
- package/{src/codemods → codemods}/transforms/v0.0.2/rename-topnav-title-to-heading.mjs +10 -4
- package/{src/codemods → codemods}/transforms/v0.0.2/unify-uncontrolled-to-defaultX.mjs +7 -2
- package/{src/codemods → codemods}/transforms/v0.0.2/unify-visibility-to-onOpenChange.mjs +10 -5
- package/{src/codemods → codemods}/transforms/v0.0.6/migrate-badge-children-to-label.mjs +7 -2
- package/{src/codemods → codemods}/transforms/v0.0.6/migrate-collapse-to-collapsible.mjs +9 -3
- package/{src/codemods → codemods}/transforms/v0.0.6/migrate-radius-tokens.mjs +11 -5
- package/{src/codemods → codemods}/transforms/v0.0.6/migrate-shadow-tokens.mjs +13 -6
- package/{src/codemods → codemods}/transforms/v0.0.6/migrate-skeleton-radius.mjs +7 -1
- package/{src/codemods → codemods}/transforms/v0.0.6/migrate-token-names.mjs +11 -5
- package/{src/codemods → codemods}/transforms/v0.0.7/rename-banner-variant-to-container.mjs +8 -3
- package/{src/codemods → codemods}/transforms/v0.0.8/migrate-token-renames.mjs +15 -8
- package/{src/codemods → codemods}/transforms/v0.0.8/rename-endslot-to-endcontent.mjs +7 -2
- package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/drop-xds-prefix-imports.test.mjs +42 -7
- package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +43 -0
- package/{src/codemods → codemods}/transforms/v0.1.0/drop-xds-prefix-imports.mjs +117 -13
- package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-css-surfaces.mjs +21 -17
- package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-declare-module.mjs +7 -2
- package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +86 -15
- package/{src/codemods → codemods}/transforms/v0.1.2/rename-text-color-active-to-accent.mjs +15 -9
- package/{src/codemods → codemods}/transforms/v0.1.3/migrate-layout-components-to-experimental.mjs +17 -12
- package/{src/codemods → codemods}/transforms/v0.1.5/rename-switch-label-spacing-default-to-hug.mjs +15 -9
- package/{src/codemods → codemods}/transforms/v0.1.7/migrate-table-tableprops-to-direct-props.mjs +17 -10
- package/{src/codemods → codemods}/transforms/v0.1.7/rename-table-renderprops-styles-to-xstyle.mjs +18 -13
- package/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +161 -0
- package/codemods/transforms/v0.1.8/index.mjs +19 -0
- package/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +239 -0
- package/docs/cli-integrations.doc.mjs +150 -0
- package/docs/elevation.doc.mjs +79 -1
- package/docs/getting-started.doc.mjs +9 -9
- package/docs/internationalization.doc.mjs +92 -46
- package/docs/migration.doc.mjs +18 -18
- package/docs/principles.doc.dense.mjs +1 -1
- package/docs/principles.doc.mjs +6 -6
- package/docs/principles.doc.zh.mjs +1 -1
- package/docs/styling-libraries.doc.mjs +3 -3
- package/docs/styling.doc.mjs +4 -4
- package/docs/theme.doc.dense.mjs +2 -2
- package/docs/theme.doc.mjs +7 -7
- package/docs/theme.doc.zh.mjs +1 -1
- package/docs/tokens.doc.mjs +1 -1
- package/docs/working-with-ai.doc.mjs +19 -19
- package/{src/commands → lib/agent-docs}/agent-docs.mjs +164 -29
- package/{src/commands → lib/agent-docs}/agent-docs.path-safety.test.mjs +1 -1
- package/{src/commands → lib/agent-docs}/agent-docs.test.mjs +123 -10
- package/{src/lib → lib}/cli-error.mjs +2 -2
- package/{src/lib → lib}/component-discovery.mjs +44 -2
- package/{src/lib → lib}/component-format.mjs +67 -25
- package/{src/lib → lib}/component-loader.mjs +27 -13
- package/{src/lib → lib}/componentDocOverlay.test.mjs +0 -1
- package/{src/lib → lib}/error-codes.test.mjs +1 -1
- package/{src/lib → lib}/hook-discovery.mjs +14 -1
- package/{src/lib → lib}/hook-format.mjs +29 -5
- package/{src/lib → lib}/integration-warnings.mjs +2 -2
- package/{src/lib → lib}/integrations.mjs +20 -0
- package/{src/lib → lib}/integrations.test.mjs +1 -1
- package/{src/lib → lib}/json-contract.test.mjs +1 -1
- package/{src/lib → lib}/json-shim.mjs +11 -6
- package/{src/lib → lib}/json-shim.test.mjs +1 -1
- package/{src/lib → lib}/json.mjs +3 -3
- package/{src/lib → lib}/levenshtein.mjs +6 -0
- package/{src/lib → lib}/manifest.mjs +7 -5
- package/{src/lib → lib}/manifest.test.mjs +2 -2
- package/{src/lib → lib}/module-loader.mjs +1 -0
- package/{src/lib → lib}/package-scanner.mjs +47 -0
- package/{src/lib → lib}/project.mjs +48 -18
- package/{src/lib → lib}/project.test.mjs +1 -1
- package/{src/lib → lib}/resolve-theme.mjs +5 -0
- package/{src/lib → lib}/string-utils.mjs +15 -0
- package/lib/term-log.mjs +57 -0
- package/{src/lib → lib}/xle/browser.mjs +18 -8
- package/{src/lib → lib}/xle/expand.mjs +155 -22
- package/{src/lib → lib}/xle/parse.mjs +119 -13
- package/{src/lib → lib}/xle/print.mjs +54 -14
- package/{src/lib → lib}/xle/registry-core.mjs +28 -3
- package/{src/lib → lib}/xle/registry.mjs +9 -4
- package/{src/lib → lib}/xle/splice.mjs +13 -2
- package/{src/lib → lib}/xle/validate.mjs +95 -14
- package/lib/xle/xle-ast.d.ts +214 -0
- package/package.json +37 -28
- package/scripts/postinstall.mjs +74 -0
- package/templates/blocks/components/Avatar/AvatarFallbackChain.tsx +4 -4
- package/templates/blocks/components/Avatar/AvatarGroup.tsx +2 -2
- package/templates/blocks/components/Avatar/AvatarInitialsFallback.tsx +1 -1
- package/templates/blocks/components/Avatar/AvatarShowcase.tsx +5 -5
- package/templates/blocks/components/Avatar/AvatarUserCard.tsx +1 -1
- package/templates/blocks/components/Avatar/AvatarWithImage.tsx +4 -4
- package/templates/blocks/components/Avatar/AvatarWithStatus.tsx +3 -3
- package/templates/blocks/components/AvatarGroup/AvatarGroupShowcase.tsx +2 -2
- package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowCustomText.tsx +1 -1
- package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowDefault.tsx +1 -1
- package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowShowcase.tsx +2 -2
- package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotShowcase.tsx +3 -3
- package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotVariants.tsx +3 -3
- package/templates/blocks/components/Banner/BannerFloating.doc.mjs +14 -0
- package/templates/blocks/components/Banner/BannerFloating.tsx +16 -0
- package/templates/blocks/components/Button/ButtonFloating.doc.mjs +14 -0
- package/templates/blocks/components/Button/ButtonFloating.tsx +37 -0
- package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.doc.mjs +14 -0
- package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.tsx +23 -0
- package/templates/blocks/components/Card/CardElevations.doc.mjs +14 -0
- package/templates/blocks/components/Card/CardElevations.tsx +32 -0
- package/templates/blocks/components/Card/ClickableCardElevated.doc.mjs +14 -0
- package/templates/blocks/components/Card/ClickableCardElevated.tsx +21 -0
- package/templates/blocks/components/Card/SelectableCardElevated.doc.mjs +14 -0
- package/templates/blocks/components/Card/SelectableCardElevated.tsx +37 -0
- package/templates/blocks/components/Carousel/CarouselSnap.tsx +1 -1
- package/templates/blocks/components/ChatComposer/ChatComposerFlat.doc.mjs +14 -0
- package/templates/blocks/components/ChatComposer/ChatComposerFlat.tsx +75 -0
- package/templates/blocks/components/ChatMessage/ChatMessageAvatarName.tsx +2 -2
- package/templates/blocks/components/ChatMessage/ChatMessageMultiBubble.tsx +1 -1
- package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleGrouping.tsx +1 -1
- package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleMetadata.tsx +1 -1
- package/templates/blocks/components/ChatMessageList/ChatMessageListDensity.tsx +5 -9
- package/templates/blocks/components/ChatMessageList/ChatMessageListFullFeatured.tsx +1 -1
- package/templates/blocks/components/CodeBlock/CodeBlockTerminal.tsx +1 -1
- package/templates/blocks/components/HoverCard/HoverCardShowcase.tsx +1 -1
- package/templates/blocks/components/IconButton/IconButtonFloating.doc.mjs +14 -0
- package/templates/blocks/components/IconButton/IconButtonFloating.tsx +37 -0
- package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.doc.mjs +19 -0
- package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +46 -0
- package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider02Overrides.doc.mjs +14 -0
- package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider02Overrides.tsx +33 -0
- package/templates/blocks/components/Item/ItemWithMedia.tsx +2 -2
- package/templates/blocks/components/ListItem/ListItemWithMedia.tsx +2 -2
- package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.doc.mjs +14 -0
- package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.tsx +39 -0
- package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.doc.mjs +14 -0
- package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.tsx +44 -0
- package/templates/blocks/components/Pagination/PaginationDotsCarousel.tsx +2 -6
- package/templates/blocks/components/Stack/StackFillItem.tsx +2 -6
- package/templates/blocks/components/TabList/TabListTabsWithActions.doc.mjs +1 -1
- package/templates/blocks/components/TabList/TabListTabsWithActions.tsx +2 -7
- package/templates/blocks/components/Table/TableRowStatusTable.doc.mjs +14 -0
- package/templates/blocks/components/Table/TableRowStatusTable.tsx +59 -0
- package/templates/blocks/components/Thumbnail/ThumbnailDisabled.tsx +6 -1
- package/templates/blocks/components/Thumbnail/ThumbnailGallery.tsx +1 -0
- package/templates/blocks/components/Thumbnail/ThumbnailRemovable.tsx +1 -0
- package/templates/blocks/components/TypeaheadItem/TypeaheadItemShowcase.tsx +1 -1
- package/templates/pages/ai-chat/page.tsx +4 -4
- package/templates/pages/dashboard-portfolio/page.tsx +3 -11
- package/templates/pages/detail-page/page.tsx +5 -12
- package/templates/pages/documentation-design/page.tsx +1 -1
- package/templates/pages/messaging-shell/page.tsx +6 -8
- package/templates/pages/table-grouped/page.tsx +9 -14
- package/templates/pages/table-page/page.tsx +7 -9
- package/templates/pages/table-page-heatmap-status/page.tsx +5 -13
- package/{src/types → types}/api.contract.assert.ts +17 -8
- package/{src/types → types}/api.d.ts +110 -9
- package/{src/types → types}/base.d.ts +40 -4
- package/types/build.d.ts +53 -0
- package/types/codemod.d.ts +166 -0
- package/{src/types → types}/component.d.ts +1 -1
- package/{src/types → types}/discover.d.ts +1 -1
- package/{src/types → types}/docs.d.ts +1 -4
- package/{src/types → types}/hook.d.ts +1 -1
- package/{src/types → types}/index.d.ts +1 -0
- package/types/jscodeshift.d.ts +19 -0
- package/types/layout.d.ts +74 -0
- package/{src/types → types}/swizzle.d.ts +4 -0
- package/{src/types → types}/template-api.d.ts +4 -1
- package/types/theme.d.ts +53 -0
- package/types/upgrade.d.ts +108 -0
- package/utils/package-manager.mjs +178 -0
- package/utils/package-manager.test.mjs +220 -0
- package/{src/utils → utils}/path-safety.mjs +0 -18
- package/{src/utils → utils}/paths.mjs +4 -1
- package/{src/utils → utils}/update-check.mjs +5 -4
- package/{src/utils → utils}/update-check.test.mjs +3 -3
- package/docs/integration-authoring.md +0 -105
- package/src/commands/build.mjs +0 -196
- package/src/commands/init.mjs +0 -288
- package/src/commands/interactive-guard.test.mjs +0 -69
- package/src/commands/swizzle.mjs +0 -440
- package/src/commands/upgrade.test.mjs +0 -160
- package/src/types/codemod.d.ts +0 -81
- package/src/types/theme.d.ts +0 -23
- package/src/types/upgrade.d.ts +0 -64
- package/src/utils/interactive.mjs +0 -76
- package/src/utils/interactive.test.mjs +0 -70
- package/src/utils/package-manager.mjs +0 -74
- package/src/utils/package-manager.test.mjs +0 -113
- /package/{src/api → api/docs}/docOverlays.test.mjs +0 -0
- /package/{src/api → api/integration}/integration-block-exports.test.mjs +0 -0
- /package/{src/api → api/template}/template.test.mjs +0 -0
- /package/{src → authoring}/codemod.test.mjs +0 -0
- /package/{src → authoring}/config.mjs +0 -0
- /package/{src → authoring}/integration.mjs +0 -0
- /package/{src → authoring}/template.test.mjs +0 -0
- /package/{src → cli}/cli-exit-codes.test.mjs +0 -0
- /package/{src → cli}/commands/build-theme.color-scheme.test.mjs +0 -0
- /package/{src → cli}/commands/build-theme.import-path.test.mjs +0 -0
- /package/{src → cli}/commands/build-theme.path-safety.test.mjs +0 -0
- /package/{src → cli}/commands/build-theme.prose.test.mjs +0 -0
- /package/{src → cli}/commands/build-theme.variants.test.mjs +0 -0
- /package/{src → cli}/commands/build-theme.watch.test.mjs +0 -0
- /package/{src → cli}/commands/component.test.mjs +0 -0
- /package/{src → cli}/commands/detail-levels.test.mjs +0 -0
- /package/{src → cli}/commands/swizzle.path-safety.test.mjs +0 -0
- /package/{src → cli}/commands/swizzle.routing.test.mjs +0 -0
- /package/{src → cli}/commands/template.test.mjs +0 -0
- /package/{src → cli}/commands/upgrade.config-ordering.test.mjs +0 -0
- /package/{src → cli}/commands/validate-integration.test.mjs +0 -0
- /package/{src → cli}/update-hint-commands.test.mjs +0 -0
- /package/{src/codemods → codemods}/__tests__/rename-imperative-ref-to-handleRef.test.mjs +0 -0
- /package/{src/codemods → codemods}/__tests__/rename-isStreaming-to-isStopShown.test.mjs +0 -0
- /package/{src/codemods → codemods}/__tests__/rename-section-wash-to-muted.test.mjs +0 -0
- /package/{src/codemods → codemods}/__tests__/runner.test.mjs +0 -0
- /package/{src/codemods → codemods}/__tests__/toolbar-density-to-size.test.mjs +0 -0
- /package/{src/codemods → codemods}/__tests__/validation.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.10/__tests__/remove-size-props.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.10/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.12/__tests__/add-is-icon-only.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.12/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.13/__tests__/icon-name-deprecations.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.13/__tests__/rename-attachments-to-drawer.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.13/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.14/__tests__/rename-action-props.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.14/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-item-children-to-endcontent.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-selector-children-to-render-option.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-theme-selectors-to-data-attrs.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/rename-date-picker-to-input.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/rename-stack-element-to-as.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.15/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-badge-dot-to-statusdot.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-gap-to-numeric.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-isFullBleed-to-padding.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-useXDSIcon-to-getIcon.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-banner-endButton-to-endContent.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-form-tooltip-startIcon.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-isShown-to-isOpen.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-selector-items-to-options.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-sidenav-header-to-heading.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-topnav-title-to-heading.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/unify-uncontrolled-to-defaultX.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/unify-visibility-to-onOpenChange.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.2/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-collapse-to-collapsible.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-radius-tokens.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-shadow-tokens.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-skeleton-radius.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-token-names.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.6/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.7/__tests__/rename-banner-variant-to-container.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.7/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.8/__tests__/migrate-token-renames.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.8/__tests__/rename-endslot-to-endcontent.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.0.8/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-css-surfaces.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-declare-module.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.0/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.2/__tests__/rename-text-color-active-to-accent.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.2/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.3/__tests__/migrate-layout-components-to-experimental.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.3/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.5/__tests__/rename-switch-label-spacing-default-to-hug.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.5/index.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.7/__tests__/migrate-table-tableprops-to-direct-props.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.7/__tests__/rename-table-renderprops-styles-to-xstyle.test.mjs +0 -0
- /package/{src/codemods → codemods}/transforms/v0.1.7/index.mjs +0 -0
- /package/{src/lib → lib}/cli-error.test.mjs +0 -0
- /package/{src/lib → lib}/component-discovery.importpath.test.mjs +0 -0
- /package/{src/lib → lib}/component-format.test.mjs +0 -0
- /package/{src/lib → lib}/component-loader.test.mjs +0 -0
- /package/{src/lib → lib}/config-cache.mjs +0 -0
- /package/{src/lib → lib}/config-cache.test.mjs +0 -0
- /package/{src/lib → lib}/config-schema.mjs +0 -0
- /package/{src/lib → lib}/error-codes.mjs +0 -0
- /package/{src/lib → lib}/integration-warnings.test.mjs +0 -0
- /package/{src/lib → lib}/module-loader.test.mjs +0 -0
- /package/{src/lib → lib}/node-version.mjs +0 -0
- /package/{src/lib → lib}/node-version.test.mjs +0 -0
- /package/{src/lib → lib}/parse.mjs +0 -0
- /package/{src/lib → lib}/site.mjs +0 -0
- /package/{src/lib → lib}/xle/browser.d.ts +0 -0
- /package/{src/lib → lib}/xle/xle.test.mjs +0 -0
- /package/{src/schemas → schemas}/doc-schema.mjs +0 -0
- /package/{src/schemas → schemas}/template-schema.mjs +0 -0
- /package/{src/types → types}/config.d.ts +0 -0
- /package/{src/types → types}/doc.d.ts +0 -0
- /package/{src/types → types}/doctor.d.ts +0 -0
- /package/{src/types → types}/error-codes.d.ts +0 -0
- /package/{src/types → types}/integration.d.ts +0 -0
- /package/{src/types → types}/manifest.d.ts +0 -0
- /package/{src/types → types}/search.d.ts +0 -0
- /package/{src/types → types}/template.d.ts +0 -0
- /package/{src/types → types}/validate-integration.d.ts +0 -0
- /package/{src/utils → utils}/github.mjs +0 -0
- /package/{src/utils → utils}/path-safety.test.mjs +0 -0
- /package/{src/utils → utils}/paths.test.mjs +0 -0
- /package/{src/utils → utils}/semver.mjs +0 -0
- /package/{src/utils → utils}/semver.test.mjs +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# @xds/cli
|
|
2
2
|
|
|
3
|
+
# 0.1.8
|
|
4
|
+
|
|
5
|
+
#### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- Avatar and AvatarGroup adopt Icon's abbreviated size scale — `size` now takes `xsm`/`sm`/`md`/`lg`/`xl` instead of `tiny`/`xsmall`/`small`/`medium`/`large`. Pixel values are unchanged (20/24/36/48/128px) and the default is now `md` (still 36px, formerly `small`). Avatar's tiers stay larger than Icon's because avatars align with media rather than glyphs. Run `astryx upgrade` to migrate call sites. (#2672)
|
|
8
|
+
|
|
9
|
+
#### New Features
|
|
10
|
+
|
|
11
|
+
- `astryx init --features agents` now defaults to creating root `AGENTS.md` — the tool-agnostic standard that Codex/Copilot, Cursor, and most agents read — instead of the Claude-specific `.claude/CLAUDE.md`. Claude output is now opt-in via `--agent claude` (→ `.claude/CLAUDE.md`), and `--agent all` still writes both. Projects with existing agent-doc files are unaffected: init still discovers and updates every file already present, so this only changes the from-scratch default. (#4216)
|
|
12
|
+
- "Foolproof init": both `@astryxdesign/core` and `@astryxdesign/cli` now print a postinstall nudge pointing you to `npx @astryxdesign/cli init`, `astryx` commands nudge you to finish setup until init has run, and `astryx init` runs non-interactively (no TTY required) so it works in CI and agent environments. (#4147, #4153, #4154, #4155)
|
|
13
|
+
|
|
14
|
+
#### Fixes
|
|
15
|
+
|
|
16
|
+
- Stop suggesting bare `npx astryx` before the CLI is installed — it resolves to an unrelated package on the npm registry.
|
|
17
|
+
The CLI now emits an install-aware invocation everywhere it prints a command:
|
|
18
|
+
- Extend the v0.1.0 upgrade codemods to cover test files that mock `@xds/core` modules, which were previously left half-migrated and broke after upgrade:
|
|
19
|
+
- `astryx upgrade` now keeps the managed agent-docs block (`<!-- ASTRYX:START --> … <!-- ASTRYX:END -->`) in sync with the installed version on **every** path — including the up-to-date and no-codemods short-circuits that previously returned before any refresh, leaving AI agents reading a stale component index and superseded rules. The block documents the installed library, so it's now refreshed up front (independent of codemods) and reported in the `--json` receipt as `agentDocs`. One detection pass covers three cases: a stale block is rewritten (`--apply`) or reported as a pending change (dry-run, which no longer writes); a project with core installed but no managed block is nudged to run `astryx init --features agents`; an already-current block stays silent. (#4168, #4169)
|
|
20
|
+
|
|
21
|
+
#### Documentation
|
|
22
|
+
|
|
23
|
+
- Add a `cli-integrations` CLI docs topic (`astryx docs cli-integrations`) so the integration-authoring guide (originally written by @ejhammond) is discoverable through the CLI and docsite instead of an unreferenced markdown file. Rewrite the CLI README's Configuration section to match the current strict config schema (`integrations`, `issuesUrl`, `hooks.postCodemod`, `experimental.xle`) and reframe the Integrations section around the two-file API.
|
|
24
|
+
|
|
25
|
+
#### Other Changes
|
|
26
|
+
|
|
27
|
+
- Installed / global / dev runs suggest `<pm> astryx <cmd>` (e.g. `pnpm exec astryx …`), unchanged.
|
|
28
|
+
- One-off runs (launched via `npx`/`pnpm dlx`/`yarn dlx`/`bunx`) suggest the scoped package `<dlx> @astryxdesign/cli <cmd>`, which always resolves to us.
|
|
29
|
+
- **migrate-xds-module-specifiers**: rewrite the mocked-module path in `vi.mock`/`vi.doMock`/`jest.mock`/`jest.doMock` (and bare `mock`) calls, plus `import(...)` specifiers used in TS type positions (`typeof import('@xds/core/Text')`), so the mock still intercepts the renamed `@astryxdesign/*` import.
|
|
30
|
+
- **drop-xds-prefix-imports**: un-prefix partial-mock override keys inside an `@xds/core` mock factory (e.g. `useXDSTruncation` → `useTruncation`) so the override matches the renamed export instead of silently overriding nothing. Scoped to recognized `@xds/core` mock factories only; unrelated object keys are untouched.
|
|
31
|
+
|
|
32
|
+
#### Contributors
|
|
33
|
+
|
|
34
|
+
Thanks to everyone who contributed to this release:
|
|
35
|
+
|
|
36
|
+
- @cixzhang
|
|
37
|
+
- @ejhammond
|
|
38
|
+
- @joeyfarina
|
|
39
|
+
- @josephfarina
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
3
43
|
# 0.1.7
|
|
4
44
|
|
|
5
45
|
#### New Features
|
package/README.md
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
The CLI is the primary interface for working with the design system, for humans and machines alike. It provides component documentation, design tokens, page templates, theming tools, and upgrade codemods, all accessible via terminal commands, a typed JSON API, or programmatic imports. AI agents and build tools use the same API that powers the CLI, enabling end-to-end frontend development loops.
|
|
4
4
|
|
|
5
|
+
Run it one-off with the scoped package (works whether or not it's installed):
|
|
6
|
+
|
|
5
7
|
```bash
|
|
6
|
-
npx
|
|
7
|
-
npx
|
|
8
|
-
npx
|
|
9
|
-
npx
|
|
10
|
-
npx
|
|
11
|
-
npx
|
|
8
|
+
npx @astryxdesign/cli --help
|
|
9
|
+
npx @astryxdesign/cli search button
|
|
10
|
+
npx @astryxdesign/cli component Button
|
|
11
|
+
npx @astryxdesign/cli docs tokens
|
|
12
|
+
npx @astryxdesign/cli docs migration
|
|
13
|
+
npx @astryxdesign/cli template --list
|
|
12
14
|
```
|
|
13
15
|
|
|
16
|
+
Once it's a project dependency (`npm install -D @astryxdesign/cli`), drop the scope and use the shorter `astryx` — e.g. `npx astryx component Button` or `pnpm exec astryx component Button`. Bare `astryx` resolves to an unrelated npm package until the CLI is installed, so prefer the scoped form above for first-run/one-off use.
|
|
17
|
+
|
|
14
18
|
## Finding things: `astryx search`
|
|
15
19
|
|
|
16
20
|
When you don't know whether what you need is a component, a hook, a docs topic,
|
|
@@ -20,27 +24,29 @@ fuzzy matching for typos) and tagged with their domain plus the follow-up
|
|
|
20
24
|
command to run:
|
|
21
25
|
|
|
22
26
|
```bash
|
|
23
|
-
$
|
|
27
|
+
$ astryx search button
|
|
24
28
|
|
|
25
29
|
Results for "button" (20):
|
|
26
30
|
|
|
27
31
|
[component] Button
|
|
28
32
|
Button triggers an action when clicked. Use it for form submissions…
|
|
29
|
-
→
|
|
33
|
+
→ astryx component Button
|
|
30
34
|
|
|
31
35
|
[component] IconButton
|
|
32
36
|
A button that shows only an icon with no visible text…
|
|
33
|
-
→
|
|
37
|
+
→ astryx component IconButton
|
|
34
38
|
|
|
35
39
|
[hook] useClickableContainer
|
|
36
40
|
Makes a container element clickable while preserving nested…
|
|
37
|
-
→
|
|
41
|
+
→ astryx hook useClickableContainer
|
|
38
42
|
|
|
39
43
|
[template] Banner — Collapsible
|
|
40
44
|
Combine an action button, dismiss control, and expandable detail area…
|
|
41
|
-
→
|
|
45
|
+
→ astryx template BannerCollapsibleContent
|
|
42
46
|
```
|
|
43
47
|
|
|
48
|
+
(The CLI prints the follow-up commands with your actual runner — `npx astryx …` when installed, or `npx @astryxdesign/cli …` when run one-off.)
|
|
49
|
+
|
|
44
50
|
Options:
|
|
45
51
|
|
|
46
52
|
- `--type <component|hook|doc|template>`: restrict to a single domain
|
|
@@ -415,7 +421,7 @@ failures (warnings are fine) and `1` when any check fails. That makes it
|
|
|
415
421
|
usable directly as a CI step:
|
|
416
422
|
|
|
417
423
|
```yaml
|
|
418
|
-
- run: npx
|
|
424
|
+
- run: npx @astryxdesign/cli doctor
|
|
419
425
|
```
|
|
420
426
|
|
|
421
427
|
Use `--json` for a structured envelope (`{ apiVersion, type: "doctor",
|
|
@@ -423,13 +429,103 @@ data: { checks, summary } }`) that AI agents and scripts can parse.
|
|
|
423
429
|
|
|
424
430
|
## Configuration
|
|
425
431
|
|
|
426
|
-
The CLI reads
|
|
432
|
+
The CLI reads an optional `astryx.config.{ts,mjs,js}` from your project root
|
|
433
|
+
(a sibling of `package.json`). Every field is optional; with no config file the
|
|
434
|
+
CLI runs on defaults.
|
|
427
435
|
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
436
|
+
```typescript
|
|
437
|
+
import {createConfig} from '@astryxdesign/core/config';
|
|
438
|
+
|
|
439
|
+
export default createConfig({
|
|
440
|
+
integrations: ['@acme/astryx-widgets'],
|
|
433
441
|
issuesUrl: 'https://github.com/your-org/your-repo/issues',
|
|
434
|
-
};
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
`createConfig` is a type-preserving helper: it returns its argument unchanged
|
|
446
|
+
and exists only to give the config file editor autocomplete and type-checking. A
|
|
447
|
+
plain `export default {}` object works identically. It's exported from
|
|
448
|
+
`@astryxdesign/core` (not the CLI) so your config file gets type feedback
|
|
449
|
+
without depending on the CLI; the same helper is re-exported from
|
|
450
|
+
`@astryxdesign/cli/config` for back-compat.
|
|
451
|
+
|
|
452
|
+
| Field | Type | Purpose |
|
|
453
|
+
| ----------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
454
|
+
| `integrations` | `string[]` | Integration package names to load (see [Integrations](#integrations)). |
|
|
455
|
+
| `issuesUrl` | `string` | Where "report an issue" links point for your project. Defaults to the core issue tracker. |
|
|
456
|
+
| `hooks.postCodemod` | `PostCodemodHook[]` | Commands to run after `astryx upgrade` applies codemods (e.g. reinstall, rebuild, reformat). |
|
|
457
|
+
| `experimental.xle.components` | `Record<string, XleComponent>` | Register app-local components so layout (XLE) expressions can reference them by name. Unstable. |
|
|
458
|
+
|
|
459
|
+
The config is validated against a strict schema when the CLI loads it, so an
|
|
460
|
+
unknown field is a hard error rather than a silent no-op. `astryx doctor`
|
|
461
|
+
reports whether the config loads cleanly.
|
|
462
|
+
|
|
463
|
+
## Integrations
|
|
464
|
+
|
|
465
|
+
An **integration** is any npm package that contributes its own components,
|
|
466
|
+
templates, and upgrade codemods to Astryx. The CLI surfaces them next to core's,
|
|
467
|
+
through the same commands, so a consumer can `astryx component`,
|
|
468
|
+
`astryx template`, and `astryx upgrade` across core and every integration
|
|
469
|
+
uniformly. Use it to ship a first-party add-on, publish a third-party component
|
|
470
|
+
library, or share an internal design-system package across apps.
|
|
471
|
+
|
|
472
|
+
The system runs on two files, each with a small typed API:
|
|
473
|
+
|
|
474
|
+
| File | Written by | Role |
|
|
475
|
+
| -------------------------------- | ---------- | ----------------------------------------- |
|
|
476
|
+
| `astryx.config.{ts,mjs,js}` | Consumer | Lists which integration packages to load. |
|
|
477
|
+
| `astryx.integration.{ts,mjs,js}` | Author | Declares what a package contributes. |
|
|
478
|
+
|
|
479
|
+
The consumer side is the `integrations` field of [`astryx.config`](#configuration).
|
|
480
|
+
The author side is the integration manifest below.
|
|
481
|
+
|
|
482
|
+
### The integration manifest
|
|
483
|
+
|
|
484
|
+
A package becomes an integration by exporting a manifest from
|
|
485
|
+
`astryx.integration.{ts,mjs,js}` at its root (a sibling of `package.json`). The
|
|
486
|
+
manifest points at where each kind of contribution lives; identity (name,
|
|
487
|
+
version) comes from `package.json`, not the manifest.
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
import {createIntegration} from '@astryxdesign/core/authoring';
|
|
491
|
+
|
|
492
|
+
export default createIntegration({
|
|
493
|
+
components: './components',
|
|
494
|
+
templates: './templates',
|
|
495
|
+
codemods: './codemods',
|
|
496
|
+
issuesUrl: 'https://github.com/acme/widgets/issues',
|
|
497
|
+
});
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
| Field | Type | Purpose |
|
|
501
|
+
| ------------ | -------- | --------------------------------------------------------------------- |
|
|
502
|
+
| `components` | `string` | Directory holding the package's components and their `.doc.*` files. |
|
|
503
|
+
| `templates` | `string` | Directory holding the package's page/block templates. |
|
|
504
|
+
| `codemods` | `string` | Directory holding upgrade codemods run by `astryx upgrade`. |
|
|
505
|
+
| `issuesUrl` | `string` | Where "report an issue" links for this package's contributions point. |
|
|
506
|
+
|
|
507
|
+
Every field is optional; declare only the roots the package ships.
|
|
508
|
+
`createIntegration` is a type-preserving helper (editor autocomplete and
|
|
509
|
+
type-checking); it lives in `@astryxdesign/core/authoring` and is re-exported
|
|
510
|
+
from `@astryxdesign/cli/integration` for back-compat.
|
|
511
|
+
|
|
512
|
+
### How it works
|
|
513
|
+
|
|
514
|
+
Every command loads the consumer's `astryx.config`, resolves each listed
|
|
515
|
+
integration's manifest from `node_modules`, and discovers its contributions.
|
|
516
|
+
Everything is validated against one strict schema at the load boundary, so the
|
|
517
|
+
CLI presents core and integration contributions through a single, uniform
|
|
518
|
+
surface.
|
|
519
|
+
|
|
520
|
+
Discovery is resilient: a broken or misconfigured integration is skipped with a
|
|
521
|
+
one-line warning on stderr instead of crashing the CLI, and it never corrupts a
|
|
522
|
+
`--json` envelope. To inspect problems, run
|
|
523
|
+
`astryx validate-integration <package>` for a detailed report on one package, or
|
|
524
|
+
`astryx doctor` for an overall health check.
|
|
525
|
+
|
|
526
|
+
For the full authoring walkthrough (component doc format, template packaging
|
|
527
|
+
and `exports` requirements, and codemod authoring), see the guide:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
astryx docs cli-integrations
|
|
435
531
|
```
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* blog's structure can change freely without touching the CLI.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {AstryxError} from '
|
|
14
|
-
import {ERROR_CODES} from '
|
|
15
|
-
import {SITE_URL, SITE_ORIGIN} from '
|
|
13
|
+
import {AstryxError} from '../error.mjs';
|
|
14
|
+
import {ERROR_CODES} from '../../lib/error-codes.mjs';
|
|
15
|
+
import {SITE_URL, SITE_ORIGIN} from '../../lib/site.mjs';
|
|
16
16
|
|
|
17
17
|
/** Abort a feed/post fetch that hangs, and cap how much we'll read. */
|
|
18
18
|
const FETCH_TIMEOUT_MS = 15000;
|
|
@@ -20,6 +20,10 @@ const MAX_BYTES = 5 * 1024 * 1024; // 5 MB — a blog feed is never larger.
|
|
|
20
20
|
|
|
21
21
|
const FEED_URL = new URL('/rss.xml', SITE_URL).toString();
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} url
|
|
25
|
+
* @returns {Promise<string>}
|
|
26
|
+
*/
|
|
23
27
|
async function fetchText(url) {
|
|
24
28
|
const controller = new AbortController();
|
|
25
29
|
const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
@@ -31,7 +35,12 @@ async function fetchText(url) {
|
|
|
31
35
|
});
|
|
32
36
|
} catch (e) {
|
|
33
37
|
clearTimeout(timer);
|
|
34
|
-
const reason =
|
|
38
|
+
const reason =
|
|
39
|
+
e instanceof Error
|
|
40
|
+
? e.name === 'AbortError'
|
|
41
|
+
? 'timed out'
|
|
42
|
+
: e.message
|
|
43
|
+
: String(e);
|
|
35
44
|
throw new AstryxError(
|
|
36
45
|
`Could not reach ${url}: ${reason}`,
|
|
37
46
|
[],
|
|
@@ -61,6 +70,7 @@ async function fetchText(url) {
|
|
|
61
70
|
* Defense in depth: a post's plaintext URL comes from feed content. Require it
|
|
62
71
|
* to live on the canonical origin so even a tampered feed can't redirect the
|
|
63
72
|
* CLI to another host.
|
|
73
|
+
* @param {string} target
|
|
64
74
|
*/
|
|
65
75
|
function assertCanonicalOrigin(target) {
|
|
66
76
|
let targetOrigin;
|
|
@@ -82,6 +92,10 @@ function assertCanonicalOrigin(target) {
|
|
|
82
92
|
}
|
|
83
93
|
}
|
|
84
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} value
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
85
99
|
function unescapeXml(value) {
|
|
86
100
|
return value
|
|
87
101
|
.replace(/</g, '<')
|
|
@@ -91,12 +105,23 @@ function unescapeXml(value) {
|
|
|
91
105
|
.replace(/&/g, '&');
|
|
92
106
|
}
|
|
93
107
|
|
|
108
|
+
/**
|
|
109
|
+
* @param {string} item
|
|
110
|
+
* @param {string} name
|
|
111
|
+
* @returns {string}
|
|
112
|
+
*/
|
|
94
113
|
function tag(item, name) {
|
|
95
114
|
const m = item.match(new RegExp(`<${name}[^>]*>([\\s\\S]*?)</${name}>`));
|
|
96
115
|
return m ? unescapeXml(m[1].trim()) : '';
|
|
97
116
|
}
|
|
98
117
|
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} item
|
|
120
|
+
* @param {string} name
|
|
121
|
+
* @returns {string[]}
|
|
122
|
+
*/
|
|
99
123
|
function tagAll(item, name) {
|
|
124
|
+
/** @type {string[]} */
|
|
100
125
|
const out = [];
|
|
101
126
|
const re = new RegExp(`<${name}[^>]*>([\\s\\S]*?)</${name}>`, 'g');
|
|
102
127
|
let m;
|
|
@@ -104,7 +129,11 @@ function tagAll(item, name) {
|
|
|
104
129
|
return out;
|
|
105
130
|
}
|
|
106
131
|
|
|
107
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Extract the plaintext alternate href from an <item>.
|
|
134
|
+
* @param {string} item
|
|
135
|
+
* @returns {string | null}
|
|
136
|
+
*/
|
|
108
137
|
function textHref(item) {
|
|
109
138
|
// Match the atom:link alternate regardless of attribute order/quoting; then
|
|
110
139
|
// confirm it's the text/plain alternate before trusting the href.
|
|
@@ -121,7 +150,11 @@ function textHref(item) {
|
|
|
121
150
|
return null;
|
|
122
151
|
}
|
|
123
152
|
|
|
124
|
-
/**
|
|
153
|
+
/**
|
|
154
|
+
* Derive a slug from a post link (last path segment).
|
|
155
|
+
* @param {string} link
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
125
158
|
function slugFromLink(link) {
|
|
126
159
|
try {
|
|
127
160
|
const path = new URL(link).pathname.replace(/\/$/, '');
|
|
@@ -131,6 +164,9 @@ function slugFromLink(link) {
|
|
|
131
164
|
}
|
|
132
165
|
}
|
|
133
166
|
|
|
167
|
+
/**
|
|
168
|
+
* @param {string} xml
|
|
169
|
+
*/
|
|
134
170
|
function parseFeed(xml) {
|
|
135
171
|
const items = [];
|
|
136
172
|
const re = /<item>([\s\S]*?)<\/item>/g;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
|
|
11
11
|
import {blog} from './blog.mjs';
|
|
12
|
-
import {AstryxError} from '
|
|
13
|
-
import {SITE_URL} from '
|
|
12
|
+
import {AstryxError} from '../error.mjs';
|
|
13
|
+
import {SITE_URL} from '../../lib/site.mjs';
|
|
14
14
|
|
|
15
15
|
const FEED = `<?xml version="1.0" encoding="UTF-8"?>
|
|
16
16
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file build API — the "assemble a page" assistant, as data.
|
|
5
|
+
*
|
|
6
|
+
* `build()` with no query signals the workflow playbook (`build.help`). With a
|
|
7
|
+
* query it runs the unified search and groups the results into a composition
|
|
8
|
+
* KIT (`build.kit`): the closest page templates, the blocks that cover parts,
|
|
9
|
+
* and the domain components to fill gaps, plus the always-on frame + foundation.
|
|
10
|
+
*
|
|
11
|
+
* The kit carries RAW `SearchResultEntry` objects and static name arrays only —
|
|
12
|
+
* never pre-formatted command strings. All CLI prefixing (formatCliCommand /
|
|
13
|
+
* getCliInvocation) and the section prose live in the command renderer, so the
|
|
14
|
+
* JSON shape stays package-manager-agnostic and stable across environments.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import {search} from '../search/search.mjs';
|
|
18
|
+
|
|
19
|
+
/** A page at/above this score is a confident direct match. */
|
|
20
|
+
const PAGE_DIRECT = 95;
|
|
21
|
+
/** Below this a page is too weak to offer even as a layout reference. */
|
|
22
|
+
const PAGE_FLOOR = 50;
|
|
23
|
+
/** Below this a block/domain-component match is incidental noise. */
|
|
24
|
+
const DOMAIN_FLOOR = 55;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Always-surfaced primitives. Every page needs a shell + layout/typography/
|
|
28
|
+
* action atoms, but these never keyword-match an idea ("dashboard" != "Stack"),
|
|
29
|
+
* so search alone never returns them. Kept here (not the renderer) because they
|
|
30
|
+
* are ALSO used to exclude these names from the idea-specific `domain` group.
|
|
31
|
+
*/
|
|
32
|
+
const FRAME = ['AppShell', 'TopNav', 'SideNav', 'Layout'];
|
|
33
|
+
const FOUNDATION = [
|
|
34
|
+
'VStack', 'HStack', 'Grid', 'StackItem', 'Card', 'Section',
|
|
35
|
+
'Text', 'Heading', 'Button', 'Icon', 'Badge', 'Divider',
|
|
36
|
+
];
|
|
37
|
+
const ALWAYS = new Set([...FRAME, ...FOUNDATION]);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The page-building assistant. No query → the playbook signal; a query → the
|
|
41
|
+
* grouped composition kit.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} [query] what you're building (e.g. "analytics dashboard")
|
|
44
|
+
* @param {{cwd?: string, type?: import('../../types/search').SearchDomain, limit?: number}} [options]
|
|
45
|
+
* @returns {Promise<import('../../types/build').BuildHelpResponse | import('../../types/build').BuildKitResponse>}
|
|
46
|
+
*/
|
|
47
|
+
export async function build(query, options = {}) {
|
|
48
|
+
if (!query || !String(query).trim()) {
|
|
49
|
+
return {type: 'build.help', data: {playbook: true}};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const {cwd = process.cwd(), type, limit = 60} = options;
|
|
53
|
+
// search()'s JSDoc @returns widens results to object[]; the SearchResponse
|
|
54
|
+
// shape is the contract (api.d.ts). Cast locally rather than tightening the
|
|
55
|
+
// search @returns (a separate follow-up).
|
|
56
|
+
const result = /** @type {import('../../types/search').SearchResponse} */ (
|
|
57
|
+
await search(query, {cwd, type, limit})
|
|
58
|
+
);
|
|
59
|
+
const results = result.data.results;
|
|
60
|
+
|
|
61
|
+
const pages = results
|
|
62
|
+
.filter(r => r.domain === 'template' && r.kind !== 'block' && r.score >= PAGE_FLOOR)
|
|
63
|
+
.slice(0, 3);
|
|
64
|
+
const blocks = results
|
|
65
|
+
.filter(r => r.domain === 'template' && r.kind === 'block' && r.score >= DOMAIN_FLOOR)
|
|
66
|
+
.slice(0, 5);
|
|
67
|
+
const domain = results
|
|
68
|
+
.filter(
|
|
69
|
+
r =>
|
|
70
|
+
(r.domain === 'component' || r.domain === 'hook') &&
|
|
71
|
+
r.score >= DOMAIN_FLOOR &&
|
|
72
|
+
!ALWAYS.has(r.name),
|
|
73
|
+
)
|
|
74
|
+
.slice(0, 6);
|
|
75
|
+
const directMatch = pages.length > 0 && pages[0].score >= PAGE_DIRECT;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
type: 'build.kit',
|
|
79
|
+
data: {
|
|
80
|
+
query: result.data.query,
|
|
81
|
+
// Distinguishes "search found nothing" (renderer shows "No matches")
|
|
82
|
+
// from a weak-but-non-empty result set (renderer still shows the kit).
|
|
83
|
+
hasResults: results.length > 0,
|
|
84
|
+
directMatch,
|
|
85
|
+
pages,
|
|
86
|
+
blocks,
|
|
87
|
+
domain,
|
|
88
|
+
frame: FRAME,
|
|
89
|
+
foundation: FOUNDATION,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Tests for the build API (playbook signal + composition kit).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {describe, it, expect, vi} from 'vitest';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import {fileURLToPath} from 'node:url';
|
|
10
|
+
import {build} from './build.mjs';
|
|
11
|
+
|
|
12
|
+
// api/build/ -> up 3 = packages/cli, up 4 = repo root (has packages/core).
|
|
13
|
+
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
|
|
14
|
+
|
|
15
|
+
// build() delegates to search(), whose first (cold) call is slow under vitest.
|
|
16
|
+
vi.setConfig({testTimeout: 30000});
|
|
17
|
+
|
|
18
|
+
describe('build API', () => {
|
|
19
|
+
it('no query → build.help playbook signal', async () => {
|
|
20
|
+
const r = await build();
|
|
21
|
+
expect(r.type).toBe('build.help');
|
|
22
|
+
expect(r.data).toEqual({playbook: true});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('query → build.kit with raw entries + static frame/foundation', async () => {
|
|
26
|
+
const r = await build('dashboard', {cwd: REPO});
|
|
27
|
+
expect(r.type).toBe('build.kit');
|
|
28
|
+
if (r.type !== 'build.kit') return;
|
|
29
|
+
expect(r.data.query).toBe('dashboard');
|
|
30
|
+
expect(r.data.hasResults).toBe(true);
|
|
31
|
+
expect(r.data.frame).toContain('AppShell');
|
|
32
|
+
expect(r.data.foundation).toContain('Button');
|
|
33
|
+
expect(Array.isArray(r.data.pages)).toBe(true);
|
|
34
|
+
|
|
35
|
+
// Entries are RAW SearchResultEntry objects — never package-manager-prefixed
|
|
36
|
+
// command strings (that formatting is the CLI renderer's job).
|
|
37
|
+
for (const e of [...r.data.pages, ...r.data.blocks, ...r.data.domain]) {
|
|
38
|
+
expect(typeof e.name).toBe('string');
|
|
39
|
+
expect(typeof e.score).toBe('number');
|
|
40
|
+
expect(e.command).not.toMatch(/^(pnpm|npm|yarn|bun|npx)\b/);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('excludes always-on frame/foundation names from the domain group', async () => {
|
|
45
|
+
const r = await build('dashboard', {cwd: REPO});
|
|
46
|
+
if (r.type !== 'build.kit') throw new Error('expected build.kit');
|
|
47
|
+
const names = r.data.domain.map(d => d.name);
|
|
48
|
+
expect(names).not.toContain('Button');
|
|
49
|
+
expect(names).not.toContain('AppShell');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('applies grouping caps, score floors, and directMatch threshold', async () => {
|
|
53
|
+
const r = await build('dashboard', {cwd: REPO});
|
|
54
|
+
if (r.type !== 'build.kit') throw new Error('expected build.kit');
|
|
55
|
+
const {pages, blocks, domain, directMatch} = r.data;
|
|
56
|
+
|
|
57
|
+
// Caps: pages ≤ 3, blocks ≤ 5, domain ≤ 6.
|
|
58
|
+
expect(pages.length).toBeLessThanOrEqual(3);
|
|
59
|
+
expect(blocks.length).toBeLessThanOrEqual(5);
|
|
60
|
+
expect(domain.length).toBeLessThanOrEqual(6);
|
|
61
|
+
|
|
62
|
+
// Score floors: pages ≥ PAGE_FLOOR(50); blocks/domain ≥ DOMAIN_FLOOR(55).
|
|
63
|
+
for (const p of pages) expect(p.score).toBeGreaterThanOrEqual(50);
|
|
64
|
+
for (const b of blocks) expect(b.score).toBeGreaterThanOrEqual(55);
|
|
65
|
+
for (const d of domain) expect(d.score).toBeGreaterThanOrEqual(55);
|
|
66
|
+
|
|
67
|
+
// directMatch iff the top page is a confident match (PAGE_DIRECT = 95).
|
|
68
|
+
expect(directMatch).toBe(pages.length > 0 && pages[0].score >= 95);
|
|
69
|
+
|
|
70
|
+
// Domain partitioning: pages = non-block templates, blocks = block templates,
|
|
71
|
+
// domain = components/hooks.
|
|
72
|
+
for (const p of pages) {
|
|
73
|
+
expect(p.domain).toBe('template');
|
|
74
|
+
expect(p.kind).not.toBe('block');
|
|
75
|
+
}
|
|
76
|
+
for (const b of blocks) {
|
|
77
|
+
expect(b.domain).toBe('template');
|
|
78
|
+
expect(b.kind).toBe('block');
|
|
79
|
+
}
|
|
80
|
+
for (const d of domain) expect(['component', 'hook']).toContain(d.domain);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('no matches → build.kit with hasResults=false and empty groups', async () => {
|
|
84
|
+
const r = await build('zzznomatch99', {cwd: REPO});
|
|
85
|
+
expect(r.type).toBe('build.kit');
|
|
86
|
+
if (r.type !== 'build.kit') return;
|
|
87
|
+
expect(r.data.hasResults).toBe(false);
|
|
88
|
+
expect(r.data.pages).toHaveLength(0);
|
|
89
|
+
expect(r.data.blocks).toHaveLength(0);
|
|
90
|
+
expect(r.data.domain).toHaveLength(0);
|
|
91
|
+
});
|
|
92
|
+
});
|