@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/src/commands/swizzle.mjs
DELETED
|
@@ -1,440 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file swizzle command — Copy component source for customization
|
|
5
|
-
*
|
|
6
|
-
* Resolves a component's owning package (core or a configured integration),
|
|
7
|
-
* copies its non-test/non-doc source files to the output directory, and
|
|
8
|
-
* rewrites escaping relative imports to use the OWNER package's subpaths.
|
|
9
|
-
*
|
|
10
|
-
* After swizzling, prints a short maintainer feedback note pointing users at
|
|
11
|
-
* the owner's issue tracker so they can let the team know what gap led them to
|
|
12
|
-
* customize the component. The core feedback URL is routed through app config
|
|
13
|
-
* (`config.issuesUrl`); integration components use their manifest `issuesUrl`.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import * as fs from 'node:fs';
|
|
17
|
-
import * as path from 'node:path';
|
|
18
|
-
import * as p from '@clack/prompts';
|
|
19
|
-
import {findCoreDir, listComponents} from '../utils/paths.mjs';
|
|
20
|
-
import {
|
|
21
|
-
assertWithin,
|
|
22
|
-
PathSafetyError,
|
|
23
|
-
isNonInteractive,
|
|
24
|
-
} from '../utils/path-safety.mjs';
|
|
25
|
-
import {jsonOut, humanLog} from '../lib/json.mjs';
|
|
26
|
-
import {cliError} from '../lib/cli-error.mjs';
|
|
27
|
-
import {ERROR_CODES} from '../lib/error-codes.mjs';
|
|
28
|
-
import {checkGhCli} from '../utils/github.mjs';
|
|
29
|
-
import {getRunPrefix} from '../utils/package-manager.mjs';
|
|
30
|
-
import {Project} from '../lib/project.mjs';
|
|
31
|
-
import {
|
|
32
|
-
CORE_PACKAGE,
|
|
33
|
-
findIntegrationComponentDoc,
|
|
34
|
-
findIntegrationComponentSource,
|
|
35
|
-
} from '../lib/component-discovery.mjs';
|
|
36
|
-
|
|
37
|
-
/** Default issue tracker for maintainer feedback after swizzling. */
|
|
38
|
-
const DEFAULT_ISSUES_URL = 'https://github.com/facebook/astryx/issues/new';
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Rewrite relative imports that point outside the component directory to use
|
|
42
|
-
* the OWNER package's subpaths. Imports within the copied directory (./x) are
|
|
43
|
-
* left untouched.
|
|
44
|
-
*
|
|
45
|
-
* e.g. with ownerPackage '@astryxdesign/core':
|
|
46
|
-
* '../theme/tokens.stylex' -> '@astryxdesign/core/theme'
|
|
47
|
-
* '../utils/mergeProps' -> '@astryxdesign/core/utils'
|
|
48
|
-
*
|
|
49
|
-
* `ownerPackage` defaults to '@astryxdesign/core' so existing core behavior is
|
|
50
|
-
* unchanged; integration components pass their own owning package.
|
|
51
|
-
*
|
|
52
|
-
* @param {string} content
|
|
53
|
-
* @param {string} [ownerPackage]
|
|
54
|
-
*/
|
|
55
|
-
export function rewriteImports(content, ownerPackage = CORE_PACKAGE) {
|
|
56
|
-
// Match import/export from statements with relative paths going up
|
|
57
|
-
return content.replace(
|
|
58
|
-
/(from\s+['"])(\.\.\/.+?)(['"])/g,
|
|
59
|
-
(match, prefix, importPath, suffix) => {
|
|
60
|
-
// Extract the top-level directory from the relative path
|
|
61
|
-
// e.g. '../theme/tokens.stylex' -> 'theme'
|
|
62
|
-
// e.g. '../utils/mergeProps' -> 'utils'
|
|
63
|
-
const parts = importPath.replace(/^\.\.\//, '').split('/');
|
|
64
|
-
const topDir = parts[0];
|
|
65
|
-
|
|
66
|
-
// Map to the owner package subpath
|
|
67
|
-
return `${prefix}${ownerPackage}/${topDir}${suffix}`;
|
|
68
|
-
},
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Build the maintainer feedback note for a swizzled component.
|
|
74
|
-
*
|
|
75
|
-
* Returns { issuesUrl, ghCommand? } or null when no issues URL is available
|
|
76
|
-
* (an integration that ships no `issuesUrl`). When the issues URL is a GitHub
|
|
77
|
-
* issues URL and the `gh` CLI is available, a ready-to-run `gh issue create`
|
|
78
|
-
* command is included so the user can file feedback without leaving the
|
|
79
|
-
* terminal.
|
|
80
|
-
*
|
|
81
|
-
* @param {string} component bare component name (used in the issue title)
|
|
82
|
-
* @param {string|undefined} issuesUrl owner's issue tracker URL
|
|
83
|
-
* @returns {{issuesUrl: string, ghCommand?: string} | null}
|
|
84
|
-
*/
|
|
85
|
-
function buildFeedback(component, issuesUrl) {
|
|
86
|
-
if (!issuesUrl) return null;
|
|
87
|
-
|
|
88
|
-
const feedback = {issuesUrl};
|
|
89
|
-
|
|
90
|
-
// Accept any github.com/<owner>/<repo>/issues(/new)? form.
|
|
91
|
-
const match = issuesUrl.match(
|
|
92
|
-
/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/issues(?:\/new)?\/?$/,
|
|
93
|
-
);
|
|
94
|
-
if (match && checkGhCli()) {
|
|
95
|
-
const [, owner, repo] = match;
|
|
96
|
-
feedback.ghCommand = `gh issue create --repo ${owner}/${repo} --title "[${component}] Swizzle feedback"`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return feedback;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function isCancel(value) {
|
|
103
|
-
if (p.isCancel(value)) {
|
|
104
|
-
p.cancel('Cancelled.');
|
|
105
|
-
process.exit(0);
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Load the configured integrations + core issues URL for `cwd`, swallowing any
|
|
112
|
-
* config errors so swizzle never hard-fails on a malformed/absent config. An
|
|
113
|
-
* empty list means "core only". The core issues URL is routed through the
|
|
114
|
-
* Project (config.issuesUrl, falling back to the default core tracker).
|
|
115
|
-
* @param {string} cwd
|
|
116
|
-
* @returns {Promise<{loadedIntegrations: Array<object>, issuesUrl: string|undefined, project: Project|null}>}
|
|
117
|
-
*/
|
|
118
|
-
async function loadConfigSafely(cwd) {
|
|
119
|
-
try {
|
|
120
|
-
const project = await Project.load(cwd);
|
|
121
|
-
return {
|
|
122
|
-
loadedIntegrations: project.loadedIntegrations,
|
|
123
|
-
issuesUrl: project.config.issuesUrl,
|
|
124
|
-
project,
|
|
125
|
-
};
|
|
126
|
-
} catch {
|
|
127
|
-
return {loadedIntegrations: [], issuesUrl: undefined, project: null};
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Build the set of OWNER packages that provide a component with `name` across
|
|
133
|
-
* core + every loaded integration.
|
|
134
|
-
*
|
|
135
|
-
* Core ownership is determined by the source directory existing on disk
|
|
136
|
-
* (`coreDir/src/<name>`) so plain core components without a `.doc.mjs` still
|
|
137
|
-
* resolve as today. Integration ownership is determined by a same-stem doc file
|
|
138
|
-
* (`<name>.doc.{ts,mjs,js}`) via findIntegrationComponentDoc.
|
|
139
|
-
*
|
|
140
|
-
* @param {string} coreDir
|
|
141
|
-
* @param {Array<{name: string, components?: string, issuesUrl?: string}>} loadedIntegrations
|
|
142
|
-
* @param {string} name bare component name (no XDS/Astryx prefix)
|
|
143
|
-
* @param {string|undefined} coreIssuesUrl
|
|
144
|
-
* @returns {Array<{package: string, sourceDir: string|null, ownerPackage: string, issuesUrl: string|undefined}>}
|
|
145
|
-
*/
|
|
146
|
-
function resolveOwners(coreDir, loadedIntegrations, name, coreIssuesUrl) {
|
|
147
|
-
const owners = [];
|
|
148
|
-
|
|
149
|
-
const coreComponentDir = path.join(coreDir, 'src', name);
|
|
150
|
-
if (fs.existsSync(coreComponentDir)) {
|
|
151
|
-
owners.push({
|
|
152
|
-
package: CORE_PACKAGE,
|
|
153
|
-
sourceDir: coreComponentDir,
|
|
154
|
-
ownerPackage: CORE_PACKAGE,
|
|
155
|
-
issuesUrl: coreIssuesUrl || DEFAULT_ISSUES_URL,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
for (const integration of loadedIntegrations) {
|
|
160
|
-
const docPath = findIntegrationComponentDoc(integration, name);
|
|
161
|
-
if (!docPath) continue;
|
|
162
|
-
const sourcePath = findIntegrationComponentSource(integration, name);
|
|
163
|
-
owners.push({
|
|
164
|
-
package: integration.name,
|
|
165
|
-
// The component's own folder — the directory containing its source.
|
|
166
|
-
sourceDir: sourcePath ? path.dirname(sourcePath) : null,
|
|
167
|
-
ownerPackage: integration.name,
|
|
168
|
-
issuesUrl: integration.issuesUrl,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return owners;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/** Whether a filename should be excluded from the swizzle copy. */
|
|
176
|
-
function isExcludedFromCopy(file) {
|
|
177
|
-
return (
|
|
178
|
-
file.includes('.test.') || file.includes('.doc.') || file === 'README.md'
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export function registerSwizzle(program) {
|
|
183
|
-
program
|
|
184
|
-
.command('swizzle [component]')
|
|
185
|
-
.description('Copy component source for customization')
|
|
186
|
-
.option('--output <dir>', 'Output directory', './components/astryx')
|
|
187
|
-
.option('--package <pkg>', 'Scope to a specific owning package')
|
|
188
|
-
.option('--list', 'List available components')
|
|
189
|
-
.option('-f, --overwrite', 'Overwrite existing files without prompting')
|
|
190
|
-
.action(async (component, options) => {
|
|
191
|
-
const coreDir = findCoreDir(process.cwd());
|
|
192
|
-
const json = program.opts().json || false;
|
|
193
|
-
|
|
194
|
-
if (!coreDir) {
|
|
195
|
-
cliError(
|
|
196
|
-
'Could not find @astryxdesign/core package. Make sure you are inside the design system monorepo or have @astryxdesign/core installed.',
|
|
197
|
-
{code: ERROR_CODES.ERR_CORE_NOT_FOUND},
|
|
198
|
-
);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const components = listComponents(coreDir);
|
|
203
|
-
|
|
204
|
-
if (options.list || !component) {
|
|
205
|
-
if (json) return jsonOut('swizzle.list', components);
|
|
206
|
-
humanLog('\nAvailable components:\n');
|
|
207
|
-
for (const name of components) {
|
|
208
|
-
humanLog(` ${name}`);
|
|
209
|
-
}
|
|
210
|
-
humanLog(`\nUsage: astryx swizzle <component>\n`);
|
|
211
|
-
humanLog('Example: astryx swizzle Button');
|
|
212
|
-
humanLog(
|
|
213
|
-
' astryx swizzle XDSButton (XDS prefix also works)\n',
|
|
214
|
-
);
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const dirName = component.replace(/^XDS/, '');
|
|
219
|
-
|
|
220
|
-
// Resolve the component's owning package(s) across core + integrations.
|
|
221
|
-
const {loadedIntegrations, project} = await loadConfigSafely(
|
|
222
|
-
process.cwd(),
|
|
223
|
-
);
|
|
224
|
-
// Core feedback URL is routed through the Project (config.issuesUrl,
|
|
225
|
-
// falling back to the default core tracker). When config load failed,
|
|
226
|
-
// resolveOwners applies the same default fallback.
|
|
227
|
-
const coreIssuesUrl = project
|
|
228
|
-
? project.issuesUrl({package: CORE_PACKAGE})
|
|
229
|
-
: undefined;
|
|
230
|
-
const allOwners = resolveOwners(
|
|
231
|
-
coreDir,
|
|
232
|
-
loadedIntegrations,
|
|
233
|
-
dirName,
|
|
234
|
-
coreIssuesUrl,
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
if (allOwners.length === 0) {
|
|
238
|
-
cliError(`Component "${component}" not found.`, {
|
|
239
|
-
suggestions: components.slice(0, 10).map(n => ({name: n})),
|
|
240
|
-
code: ERROR_CODES.ERR_UNKNOWN_COMPONENT,
|
|
241
|
-
});
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
let owner;
|
|
246
|
-
if (options.package) {
|
|
247
|
-
owner = allOwners.find(o => o.package === options.package);
|
|
248
|
-
if (!owner) {
|
|
249
|
-
cliError(
|
|
250
|
-
`Component "${dirName}" is not provided by package "${options.package}".`,
|
|
251
|
-
{
|
|
252
|
-
suggestions: allOwners.map(o => ({
|
|
253
|
-
name: o.package,
|
|
254
|
-
reason: 'provides this component',
|
|
255
|
-
})),
|
|
256
|
-
code: ERROR_CODES.ERR_UNKNOWN_COMPONENT,
|
|
257
|
-
},
|
|
258
|
-
);
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
} else if (allOwners.length > 1) {
|
|
262
|
-
cliError(
|
|
263
|
-
`Component "${dirName}" is provided by multiple packages. Re-run with --package <pkg> to choose one.`,
|
|
264
|
-
{
|
|
265
|
-
suggestions: allOwners.map(o => ({
|
|
266
|
-
name: o.package,
|
|
267
|
-
reason: 'provides this component',
|
|
268
|
-
})),
|
|
269
|
-
code: ERROR_CODES.ERR_AMBIGUOUS_COMPONENT,
|
|
270
|
-
},
|
|
271
|
-
);
|
|
272
|
-
return;
|
|
273
|
-
} else {
|
|
274
|
-
owner = allOwners[0];
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (!owner.sourceDir || !fs.existsSync(owner.sourceDir)) {
|
|
278
|
-
cliError(
|
|
279
|
-
`No source found for "${dirName}" in package "${owner.package}".`,
|
|
280
|
-
{code: ERROR_CODES.ERR_NO_SOURCE},
|
|
281
|
-
);
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const componentDir = owner.sourceDir;
|
|
286
|
-
|
|
287
|
-
// Path-safety: --output must resolve inside cwd. Reject absolute
|
|
288
|
-
// paths and `..` traversal up front, before any directory is created.
|
|
289
|
-
let outputBase;
|
|
290
|
-
try {
|
|
291
|
-
outputBase = assertWithin(options.output, process.cwd(), {
|
|
292
|
-
label: 'output directory',
|
|
293
|
-
});
|
|
294
|
-
} catch (err) {
|
|
295
|
-
if (err instanceof PathSafetyError) {
|
|
296
|
-
cliError(err.message, {code: ERROR_CODES.ERR_PATH_TRAVERSAL});
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
throw err;
|
|
300
|
-
}
|
|
301
|
-
const outputDir = path.join(outputBase, dirName);
|
|
302
|
-
|
|
303
|
-
// Pre-flight overwrite check: collect files we'd write and detect
|
|
304
|
-
// collisions before mkdir/writeFile so we never half-clobber.
|
|
305
|
-
const sourceFiles = fs.readdirSync(componentDir).filter(file => {
|
|
306
|
-
if (isExcludedFromCopy(file)) return false;
|
|
307
|
-
const stat = fs.statSync(path.join(componentDir, file));
|
|
308
|
-
return stat.isFile();
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
const existingFiles = sourceFiles.filter(f =>
|
|
312
|
-
fs.existsSync(path.join(outputDir, f)),
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
if (existingFiles.length > 0 && !options.overwrite) {
|
|
316
|
-
const relOutputForMsg = path.relative(process.cwd(), outputDir) || '.';
|
|
317
|
-
if (json || isNonInteractive({json})) {
|
|
318
|
-
const msg =
|
|
319
|
-
`Refusing to overwrite ${existingFiles.length} existing file(s) in ${relOutputForMsg}/. ` +
|
|
320
|
-
`Re-run with --overwrite (or -f) to replace them.`;
|
|
321
|
-
cliError(msg, {code: ERROR_CODES.ERR_FILE_EXISTS});
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
const confirmed = isCancel(
|
|
325
|
-
await p.confirm({
|
|
326
|
-
message:
|
|
327
|
-
`Overwrite ${existingFiles.length} existing file(s) in ${relOutputForMsg}/? ` +
|
|
328
|
-
`(${existingFiles.slice(0, 3).join(', ')}${existingFiles.length > 3 ? ', …' : ''})`,
|
|
329
|
-
initialValue: false,
|
|
330
|
-
}),
|
|
331
|
-
);
|
|
332
|
-
if (!confirmed) {
|
|
333
|
-
humanLog('Aborted. Re-run with --overwrite to replace files.');
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
fs.mkdirSync(outputDir, {recursive: true});
|
|
339
|
-
|
|
340
|
-
// Copy all non-test, non-doc, non-README files
|
|
341
|
-
const files = fs.readdirSync(componentDir);
|
|
342
|
-
let copied = 0;
|
|
343
|
-
// Track whether any copied source uses StyleX. Swizzled StyleX source
|
|
344
|
-
// needs a build-time StyleX compiler in the consumer's app or it renders
|
|
345
|
-
// unstyled with no error — so we surface a setup note after copying.
|
|
346
|
-
let usesStyleX = false;
|
|
347
|
-
|
|
348
|
-
for (const file of files) {
|
|
349
|
-
// Skip test files, doc files, and README
|
|
350
|
-
if (isExcludedFromCopy(file)) continue;
|
|
351
|
-
|
|
352
|
-
const srcPath = path.join(componentDir, file);
|
|
353
|
-
const stat = fs.statSync(srcPath);
|
|
354
|
-
if (!stat.isFile()) continue;
|
|
355
|
-
|
|
356
|
-
let content = fs.readFileSync(srcPath, 'utf-8');
|
|
357
|
-
|
|
358
|
-
// Rewrite escaping imports for .ts/.tsx files to the owner package.
|
|
359
|
-
if (file.endsWith('.ts') || file.endsWith('.tsx')) {
|
|
360
|
-
content = rewriteImports(content, owner.ownerPackage);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (
|
|
364
|
-
(file.endsWith('.ts') || file.endsWith('.tsx')) &&
|
|
365
|
-
content.includes('@stylexjs/stylex')
|
|
366
|
-
) {
|
|
367
|
-
usesStyleX = true;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
fs.writeFileSync(path.join(outputDir, file), content);
|
|
371
|
-
copied++;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const relOutput = path.relative(process.cwd(), outputDir);
|
|
375
|
-
const copiedFiles = files.filter(
|
|
376
|
-
f =>
|
|
377
|
-
!isExcludedFromCopy(f) &&
|
|
378
|
-
fs.statSync(path.join(componentDir, f)).isFile(),
|
|
379
|
-
);
|
|
380
|
-
|
|
381
|
-
const feedback = buildFeedback(dirName, owner.issuesUrl);
|
|
382
|
-
|
|
383
|
-
if (json) {
|
|
384
|
-
/** @type {Record<string, unknown>} */
|
|
385
|
-
const payload = {
|
|
386
|
-
component: dirName,
|
|
387
|
-
package: owner.package,
|
|
388
|
-
outputDir: relOutput,
|
|
389
|
-
filesCopied: copied,
|
|
390
|
-
files: copiedFiles.map(f => f),
|
|
391
|
-
usesStyleX,
|
|
392
|
-
};
|
|
393
|
-
if (feedback) payload.feedback = feedback;
|
|
394
|
-
return jsonOut('swizzle.copy', payload);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
humanLog(`\n✓ Copied ${copied} files to ${relOutput}/\n`);
|
|
398
|
-
humanLog(
|
|
399
|
-
`Relative imports have been rewritten to use ${owner.ownerPackage}.`,
|
|
400
|
-
);
|
|
401
|
-
humanLog('You can now customize the component source freely.\n');
|
|
402
|
-
|
|
403
|
-
// StyleX build requirement. Swizzled components ship raw StyleX source,
|
|
404
|
-
// which needs a build-time StyleX compiler in the consumer's app to
|
|
405
|
-
// produce atomic CSS. Without it the component compiles but renders
|
|
406
|
-
// unstyled, with no error — a confusing silent failure, so call it out.
|
|
407
|
-
if (usesStyleX) {
|
|
408
|
-
humanLog(
|
|
409
|
-
'⚠ These components use StyleX and require a StyleX compiler in your build.',
|
|
410
|
-
);
|
|
411
|
-
humanLog(
|
|
412
|
-
' Without one they render unstyled (no error). See setup per framework:',
|
|
413
|
-
);
|
|
414
|
-
humanLog(` ${getRunPrefix()} astryx docs styling`);
|
|
415
|
-
humanLog(
|
|
416
|
-
' Next.js note: the StyleX Babel plugin disables SWC and breaks next/font —',
|
|
417
|
-
);
|
|
418
|
-
humanLog(
|
|
419
|
-
' use an SWC-based StyleX transform instead (covered in the guide).',
|
|
420
|
-
);
|
|
421
|
-
humanLog('');
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Maintainer feedback note. If we couldn't swizzle cleanly, the team
|
|
425
|
-
// wants to know — point users at the issue tracker. Skipped when the
|
|
426
|
-
// owning package ships no issues URL.
|
|
427
|
-
if (feedback) {
|
|
428
|
-
humanLog(
|
|
429
|
-
'Customizing a component often signals a gap in the design system.',
|
|
430
|
-
);
|
|
431
|
-
humanLog('Let the maintainers know what you needed:');
|
|
432
|
-
if (feedback.ghCommand) {
|
|
433
|
-
humanLog(` ${feedback.ghCommand}`);
|
|
434
|
-
} else {
|
|
435
|
-
humanLog(` ${feedback.issuesUrl}`);
|
|
436
|
-
}
|
|
437
|
-
humanLog('');
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
|
|
4
|
-
import * as fs from 'node:fs';
|
|
5
|
-
import * as path from 'node:path';
|
|
6
|
-
import * as os from 'node:os';
|
|
7
|
-
import {Command} from 'commander';
|
|
8
|
-
import {registerUpgrade} from './upgrade.mjs';
|
|
9
|
-
|
|
10
|
-
let tmpDir;
|
|
11
|
-
let originalCwd;
|
|
12
|
-
let logCalls;
|
|
13
|
-
let stdoutCalls;
|
|
14
|
-
let exitCode;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-upgrade-test-'));
|
|
18
|
-
originalCwd = process.cwd();
|
|
19
|
-
process.chdir(tmpDir);
|
|
20
|
-
logCalls = [];
|
|
21
|
-
stdoutCalls = [];
|
|
22
|
-
exitCode = undefined;
|
|
23
|
-
vi.spyOn(console, 'log').mockImplementation((...args) => {
|
|
24
|
-
logCalls.push(args.join(' '));
|
|
25
|
-
});
|
|
26
|
-
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
27
|
-
// @clack/prompts writes directly to process.stdout — capture that too.
|
|
28
|
-
vi.spyOn(process.stdout, 'write').mockImplementation((chunk) => {
|
|
29
|
-
stdoutCalls.push(typeof chunk === 'string' ? chunk : chunk.toString());
|
|
30
|
-
return true;
|
|
31
|
-
});
|
|
32
|
-
// jsonError() calls process.exit(1) directly. Trap it so tests can assert.
|
|
33
|
-
vi.spyOn(process, 'exit').mockImplementation((code) => {
|
|
34
|
-
exitCode = code;
|
|
35
|
-
throw new Error(`__exit ${code}`);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
afterEach(() => {
|
|
40
|
-
process.chdir(originalCwd);
|
|
41
|
-
fs.rmSync(tmpDir, {recursive: true, force: true});
|
|
42
|
-
vi.restoreAllMocks();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
function createProgram() {
|
|
46
|
-
const program = new Command();
|
|
47
|
-
program.exitOverride();
|
|
48
|
-
// Mirror the global --json flag from src/index.mjs so program.opts().json
|
|
49
|
-
// resolves the same way in tests.
|
|
50
|
-
program.option('--json', 'Output as typed JSON');
|
|
51
|
-
registerUpgrade(program);
|
|
52
|
-
return program;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function writePkg(deps = {}) {
|
|
56
|
-
fs.writeFileSync(
|
|
57
|
-
path.join(tmpDir, 'package.json'),
|
|
58
|
-
JSON.stringify({name: 'fixture', dependencies: deps}, null, 2),
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function writeInstalledCore(version, packageName = '@astryxdesign/core') {
|
|
63
|
-
const parts = packageName.split('/');
|
|
64
|
-
const dir = path.join(tmpDir, 'node_modules', ...parts);
|
|
65
|
-
fs.mkdirSync(dir, {recursive: true});
|
|
66
|
-
fs.writeFileSync(
|
|
67
|
-
path.join(dir, 'package.json'),
|
|
68
|
-
JSON.stringify({name: packageName, version}, null, 2),
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function writeSourceFile() {
|
|
73
|
-
fs.mkdirSync(path.join(tmpDir, 'src'), {recursive: true});
|
|
74
|
-
fs.writeFileSync(path.join(tmpDir, 'src', 'index.ts'), 'const x = 1;\n');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/** Run a command and capture the parsed JSON response (last printed JSON line). */
|
|
78
|
-
async function runJson(args) {
|
|
79
|
-
const program = createProgram();
|
|
80
|
-
try {
|
|
81
|
-
await program.parseAsync(['node', 'astryx', ...args]);
|
|
82
|
-
} catch (err) {
|
|
83
|
-
if (!String(err?.message || '').startsWith('__exit')) throw err;
|
|
84
|
-
}
|
|
85
|
-
// Find the most recent stringified JSON envelope in console.log output.
|
|
86
|
-
for (let i = logCalls.length - 1; i >= 0; i--) {
|
|
87
|
-
const line = logCalls[i];
|
|
88
|
-
if (line.startsWith('{')) {
|
|
89
|
-
try {
|
|
90
|
-
return JSON.parse(line);
|
|
91
|
-
} catch {
|
|
92
|
-
// not JSON, keep looking
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
describe('upgrade gate (semver comparison)', () => {
|
|
100
|
-
it('does NOT block an upgrade from 0.0.9 to installed 0.0.10 (regression)', async () => {
|
|
101
|
-
// The original bug: string compare said '0.0.9' >= '0.0.10', so the
|
|
102
|
-
// gate told users "Already up to date" without --force.
|
|
103
|
-
writePkg();
|
|
104
|
-
writeInstalledCore('0.0.10');
|
|
105
|
-
writeSourceFile();
|
|
106
|
-
|
|
107
|
-
const result = await runJson(['--json', 'upgrade', '--from', '0.0.9', '--path', 'src']);
|
|
108
|
-
expect(result).not.toBeNull();
|
|
109
|
-
expect(result.type === 'upgrade.run' || result.error || logCalls.some(l => l.includes('No codemods'))).toBeTruthy();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('blocks when --from >= installed target by semver (e.g. 0.0.10 → 0.0.9)', async () => {
|
|
113
|
-
writePkg();
|
|
114
|
-
writeInstalledCore('0.0.9');
|
|
115
|
-
const program = createProgram();
|
|
116
|
-
await program.parseAsync(['node', 'astryx', 'upgrade', '--from', '0.0.10']);
|
|
117
|
-
const output = stdoutCalls.join('') + logCalls.join('\n');
|
|
118
|
-
expect(output).toMatch(/up to date|Already/i);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
describe('upgrade argument validation', () => {
|
|
123
|
-
it('requires --from for upgrade runs', async () => {
|
|
124
|
-
writePkg();
|
|
125
|
-
writeInstalledCore('0.0.15');
|
|
126
|
-
const result = await runJson(['--json', 'upgrade']);
|
|
127
|
-
expect(result).not.toBeNull();
|
|
128
|
-
expect(result.error).toMatch(/Missing required --from/);
|
|
129
|
-
expect(exitCode).toBe(1);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('rejects bogus --from values', async () => {
|
|
133
|
-
writePkg();
|
|
134
|
-
writeInstalledCore('0.0.15');
|
|
135
|
-
const result = await runJson(['--json', 'upgrade', '--from', 'not-a-version']);
|
|
136
|
-
expect(result).not.toBeNull();
|
|
137
|
-
expect(result.error).toMatch(/Invalid --from/);
|
|
138
|
-
expect(exitCode).toBe(1);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('detects the installed target version from @astryxdesign/core', async () => {
|
|
142
|
-
writePkg();
|
|
143
|
-
writeInstalledCore('0.0.15');
|
|
144
|
-
writeSourceFile();
|
|
145
|
-
const result = await runJson(['--json', 'upgrade', '--from', '0.0.14', '--path', 'src']);
|
|
146
|
-
expect(result?.error || '').not.toMatch(/Could not find installed/);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('upgrade --list dedup', () => {
|
|
151
|
-
it('lists each codemod exactly once', async () => {
|
|
152
|
-
const result = await runJson(['--json', 'upgrade', '--list']);
|
|
153
|
-
expect(result).not.toBeNull();
|
|
154
|
-
expect(result.type).toBe('upgrade.list');
|
|
155
|
-
const names = result.data.map((c) => c.name);
|
|
156
|
-
const unique = new Set(names);
|
|
157
|
-
// The bug: 31 unique codemods printed 9× → 201 entries.
|
|
158
|
-
expect(names.length).toBe(unique.size);
|
|
159
|
-
});
|
|
160
|
-
});
|
package/src/types/codemod.d.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Public type surface for the file-based codemod-authoring API exported from
|
|
5
|
-
* `@astryxdesign/cli/codemod`.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/** A single source file presented to a codemod's transform. */
|
|
9
|
-
export interface AstryxCodemodFile {
|
|
10
|
-
/** Absolute path to the file being transformed. */
|
|
11
|
-
path: string;
|
|
12
|
-
/** The current source contents of the file. */
|
|
13
|
-
source: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Helpers and context passed to a codemod's transform as the second argument. */
|
|
17
|
-
export interface AstryxCodemodApi {
|
|
18
|
-
/** A jscodeshift instance configured with a parser for the file. */
|
|
19
|
-
jscodeshift: unknown;
|
|
20
|
-
/** Report a statistic (no-op-friendly; provided for jscodeshift parity). */
|
|
21
|
-
stats: (...args: unknown[]) => void;
|
|
22
|
-
/** Report progress (no-op-friendly; provided for jscodeshift parity). */
|
|
23
|
-
report: (...args: unknown[]) => void;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A codemod's transform. Return the new source to rewrite the file, or
|
|
28
|
-
* `null`/`undefined` to leave the file unchanged.
|
|
29
|
-
*/
|
|
30
|
-
export type AstryxCodemodTransform = (
|
|
31
|
-
file: AstryxCodemodFile,
|
|
32
|
-
api: AstryxCodemodApi,
|
|
33
|
-
) => string | null | undefined;
|
|
34
|
-
|
|
35
|
-
/** Definition accepted by {@link createCodemod}. */
|
|
36
|
-
export interface AstryxCodemodDef {
|
|
37
|
-
/** Short, human-readable title shown in upgrade output. */
|
|
38
|
-
title: string;
|
|
39
|
-
/** Optional longer description. */
|
|
40
|
-
description?: string;
|
|
41
|
-
/** When true, the codemod runs only when explicitly requested. */
|
|
42
|
-
isOptional?: boolean;
|
|
43
|
-
/** File extensions this codemod applies to (e.g. ['.tsx', '.ts']). */
|
|
44
|
-
fileExtensions?: string[];
|
|
45
|
-
/** The transform function. */
|
|
46
|
-
transform: AstryxCodemodTransform;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Result of {@link createCodemod}. */
|
|
50
|
-
export interface AstryxCodemod extends AstryxCodemodDef {
|
|
51
|
-
isOptional: boolean;
|
|
52
|
-
type: 'code';
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Definition accepted by {@link createConfigCodemod}. */
|
|
56
|
-
export interface AstryxConfigCodemodDef {
|
|
57
|
-
/** Short, human-readable title shown in upgrade output. */
|
|
58
|
-
title: string;
|
|
59
|
-
/** Optional longer description. */
|
|
60
|
-
description?: string;
|
|
61
|
-
/** When true, the codemod runs only when explicitly requested. */
|
|
62
|
-
isOptional?: boolean;
|
|
63
|
-
/** The transform function applied to the astryx.config.* file. */
|
|
64
|
-
transform: AstryxCodemodTransform;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Result of {@link createConfigCodemod}. */
|
|
68
|
-
export interface AstryxConfigCodemod extends AstryxConfigCodemodDef {
|
|
69
|
-
isOptional: boolean;
|
|
70
|
-
type: 'config';
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Define a file-transforming codemod. */
|
|
74
|
-
export declare function createCodemod<T extends AstryxCodemodDef>(
|
|
75
|
-
def: T,
|
|
76
|
-
): AstryxCodemod;
|
|
77
|
-
|
|
78
|
-
/** Define a codemod that targets the consumer's astryx.config.* file. */
|
|
79
|
-
export declare function createConfigCodemod<T extends AstryxConfigCodemodDef>(
|
|
80
|
-
def: T,
|
|
81
|
-
): AstryxConfigCodemod;
|