@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
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file swizzle API — copy a component's source into the consumer project for
|
|
5
|
+
* customization, rewriting escaping relative imports to the OWNER package's
|
|
6
|
+
* subpaths.
|
|
7
|
+
*
|
|
8
|
+
* Side-effecting: `swizzle(name, ...)` writes files and returns a
|
|
9
|
+
* `swizzle.copy` receipt describing what it did; with no name (or `list`) it
|
|
10
|
+
* returns `swizzle.list`. Errors throw AstryxError (stable code + suggestions).
|
|
11
|
+
* All human prose / package-manager prefixing lives in the CLI renderer.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as fs from 'node:fs';
|
|
15
|
+
import * as path from 'node:path';
|
|
16
|
+
import {findCoreDir, listComponents} from '../../utils/paths.mjs';
|
|
17
|
+
import {assertWithin, PathSafetyError} from '../../utils/path-safety.mjs';
|
|
18
|
+
import {checkGhCli} from '../../utils/github.mjs';
|
|
19
|
+
import {Project} from '../../lib/project.mjs';
|
|
20
|
+
import {
|
|
21
|
+
CORE_PACKAGE,
|
|
22
|
+
findIntegrationComponentDoc,
|
|
23
|
+
findIntegrationComponentSource,
|
|
24
|
+
} from '../../lib/component-discovery.mjs';
|
|
25
|
+
import {ERROR_CODES} from '../../lib/error-codes.mjs';
|
|
26
|
+
import {AstryxError} from '../error.mjs';
|
|
27
|
+
|
|
28
|
+
/** Default issue tracker for maintainer feedback after swizzling. */
|
|
29
|
+
const DEFAULT_ISSUES_URL = 'https://github.com/facebook/astryx/issues/new';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Rewrite relative imports that point outside the component directory to use
|
|
33
|
+
* the OWNER package's subpaths. Imports within the copied directory (./x) are
|
|
34
|
+
* left untouched.
|
|
35
|
+
*
|
|
36
|
+
* e.g. with ownerPackage '@astryxdesign/core':
|
|
37
|
+
* '../theme/tokens.stylex' -> '@astryxdesign/core/theme'
|
|
38
|
+
* '../utils/mergeProps' -> '@astryxdesign/core/utils'
|
|
39
|
+
*
|
|
40
|
+
* @param {string} content
|
|
41
|
+
* @param {string} [ownerPackage]
|
|
42
|
+
*/
|
|
43
|
+
export function rewriteImports(content, ownerPackage = CORE_PACKAGE) {
|
|
44
|
+
return content.replace(
|
|
45
|
+
/(from\s+['"])(\.\.\/.+?)(['"])/g,
|
|
46
|
+
(match, prefix, importPath, suffix) => {
|
|
47
|
+
const parts = importPath.replace(/^\.\.\//, '').split('/');
|
|
48
|
+
const topDir = parts[0];
|
|
49
|
+
return `${prefix}${ownerPackage}/${topDir}${suffix}`;
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Build the maintainer feedback note for a swizzled component.
|
|
56
|
+
* @param {string} component
|
|
57
|
+
* @param {string|undefined} issuesUrl
|
|
58
|
+
* @returns {{issuesUrl: string, ghCommand?: string} | null}
|
|
59
|
+
*/
|
|
60
|
+
function buildFeedback(component, issuesUrl) {
|
|
61
|
+
if (!issuesUrl) return null;
|
|
62
|
+
/** @type {{issuesUrl: string, ghCommand?: string}} */
|
|
63
|
+
const feedback = {issuesUrl};
|
|
64
|
+
const match = issuesUrl.match(
|
|
65
|
+
/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/issues(?:\/new)?\/?$/,
|
|
66
|
+
);
|
|
67
|
+
if (match && checkGhCli()) {
|
|
68
|
+
const [, owner, repo] = match;
|
|
69
|
+
feedback.ghCommand = `gh issue create --repo ${owner}/${repo} --title "[${component}] Swizzle feedback"`;
|
|
70
|
+
}
|
|
71
|
+
return feedback;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Load the configured integrations + core issues URL for `cwd`, swallowing any
|
|
76
|
+
* config errors so swizzle never hard-fails on a malformed/absent config.
|
|
77
|
+
* @param {string} cwd
|
|
78
|
+
* @returns {Promise<{loadedIntegrations: import('../../lib/integrations.mjs').LoadedIntegration[], issuesUrl: string|undefined, project: Project|null}>}
|
|
79
|
+
*/
|
|
80
|
+
async function loadConfigSafely(cwd) {
|
|
81
|
+
try {
|
|
82
|
+
const project = await Project.load(cwd);
|
|
83
|
+
return {
|
|
84
|
+
loadedIntegrations: project.loadedIntegrations,
|
|
85
|
+
issuesUrl: project.config.issuesUrl,
|
|
86
|
+
project,
|
|
87
|
+
};
|
|
88
|
+
} catch {
|
|
89
|
+
return {loadedIntegrations: [], issuesUrl: undefined, project: null};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build the set of OWNER packages that provide a component named `name` across
|
|
95
|
+
* core + every loaded integration.
|
|
96
|
+
* @param {string} coreDir
|
|
97
|
+
* @param {Array<{name: string, components?: string, issuesUrl?: string}>} loadedIntegrations
|
|
98
|
+
* @param {string} name
|
|
99
|
+
* @param {string|undefined} coreIssuesUrl
|
|
100
|
+
* @returns {Array<{package: string, sourceDir: string|null, ownerPackage: string, issuesUrl: string|undefined}>}
|
|
101
|
+
*/
|
|
102
|
+
function resolveOwners(coreDir, loadedIntegrations, name, coreIssuesUrl) {
|
|
103
|
+
const owners = [];
|
|
104
|
+
const coreComponentDir = path.join(coreDir, 'src', name);
|
|
105
|
+
if (fs.existsSync(coreComponentDir)) {
|
|
106
|
+
owners.push({
|
|
107
|
+
package: CORE_PACKAGE,
|
|
108
|
+
sourceDir: coreComponentDir,
|
|
109
|
+
ownerPackage: CORE_PACKAGE,
|
|
110
|
+
issuesUrl: coreIssuesUrl || DEFAULT_ISSUES_URL,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
for (const integration of loadedIntegrations) {
|
|
114
|
+
const docPath = findIntegrationComponentDoc(integration, name);
|
|
115
|
+
if (!docPath) continue;
|
|
116
|
+
const sourcePath = findIntegrationComponentSource(integration, name);
|
|
117
|
+
owners.push({
|
|
118
|
+
package: integration.name,
|
|
119
|
+
sourceDir: sourcePath ? path.dirname(sourcePath) : null,
|
|
120
|
+
ownerPackage: integration.name,
|
|
121
|
+
issuesUrl: integration.issuesUrl,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return owners;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** @param {string} file */
|
|
128
|
+
function isExcludedFromCopy(file) {
|
|
129
|
+
return (
|
|
130
|
+
file.includes('.test.') || file.includes('.doc.') || file === 'README.md'
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* List swizzlable components, or copy one component's source for customization.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} [component] bare or XDS-prefixed component name; omit to list
|
|
138
|
+
* @param {{cwd?: string, output?: string, package?: string, list?: boolean, overwrite?: boolean}} [options]
|
|
139
|
+
* @returns {Promise<import('../../types/swizzle').SwizzleListResponse | import('../../types/swizzle').SwizzleCopyResponse>}
|
|
140
|
+
*/
|
|
141
|
+
export async function swizzle(component, options = {}) {
|
|
142
|
+
const {
|
|
143
|
+
cwd = process.cwd(),
|
|
144
|
+
output = './components/astryx',
|
|
145
|
+
package: pkg,
|
|
146
|
+
list = false,
|
|
147
|
+
overwrite = false,
|
|
148
|
+
} = options;
|
|
149
|
+
|
|
150
|
+
const coreDir = findCoreDir(cwd);
|
|
151
|
+
if (!coreDir) {
|
|
152
|
+
throw new AstryxError(
|
|
153
|
+
'Could not find @astryxdesign/core package. Make sure you are inside the design system monorepo or have @astryxdesign/core installed.',
|
|
154
|
+
[],
|
|
155
|
+
ERROR_CODES.ERR_CORE_NOT_FOUND,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const components = listComponents(coreDir);
|
|
160
|
+
|
|
161
|
+
if (list || !component) {
|
|
162
|
+
return {type: 'swizzle.list', data: components};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const dirName = component.replace(/^XDS/, '');
|
|
166
|
+
|
|
167
|
+
const {loadedIntegrations, project} = await loadConfigSafely(cwd);
|
|
168
|
+
const coreIssuesUrl = project
|
|
169
|
+
? project.issuesUrl({package: CORE_PACKAGE})
|
|
170
|
+
: undefined;
|
|
171
|
+
const allOwners = resolveOwners(coreDir, loadedIntegrations, dirName, coreIssuesUrl);
|
|
172
|
+
|
|
173
|
+
if (allOwners.length === 0) {
|
|
174
|
+
throw new AstryxError(
|
|
175
|
+
`Component "${component}" not found.`,
|
|
176
|
+
components.slice(0, 10).map(n => ({name: n})),
|
|
177
|
+
ERROR_CODES.ERR_UNKNOWN_COMPONENT,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let owner;
|
|
182
|
+
if (pkg) {
|
|
183
|
+
owner = allOwners.find(o => o.package === pkg);
|
|
184
|
+
if (!owner) {
|
|
185
|
+
throw new AstryxError(
|
|
186
|
+
`Component "${dirName}" is not provided by package "${pkg}".`,
|
|
187
|
+
allOwners.map(o => ({name: o.package, reason: 'provides this component'})),
|
|
188
|
+
ERROR_CODES.ERR_UNKNOWN_COMPONENT,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
} else if (allOwners.length > 1) {
|
|
192
|
+
throw new AstryxError(
|
|
193
|
+
`Component "${dirName}" is provided by multiple packages. Re-run with --package <pkg> to choose one.`,
|
|
194
|
+
allOwners.map(o => ({name: o.package, reason: 'provides this component'})),
|
|
195
|
+
ERROR_CODES.ERR_AMBIGUOUS_COMPONENT,
|
|
196
|
+
);
|
|
197
|
+
} else {
|
|
198
|
+
owner = allOwners[0];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!owner.sourceDir || !fs.existsSync(owner.sourceDir)) {
|
|
202
|
+
throw new AstryxError(
|
|
203
|
+
`No source found for "${dirName}" in package "${owner.package}".`,
|
|
204
|
+
[],
|
|
205
|
+
ERROR_CODES.ERR_NO_SOURCE,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const componentDir = owner.sourceDir;
|
|
210
|
+
|
|
211
|
+
// Path-safety: --output must resolve inside cwd.
|
|
212
|
+
let outputBase;
|
|
213
|
+
try {
|
|
214
|
+
outputBase = assertWithin(output, cwd, {label: 'output directory'});
|
|
215
|
+
} catch (err) {
|
|
216
|
+
if (err instanceof PathSafetyError) {
|
|
217
|
+
throw new AstryxError(err.message, [], ERROR_CODES.ERR_PATH_TRAVERSAL);
|
|
218
|
+
}
|
|
219
|
+
throw err;
|
|
220
|
+
}
|
|
221
|
+
const outputDir = path.join(outputBase, dirName);
|
|
222
|
+
|
|
223
|
+
// Pre-flight overwrite check before any mkdir/writeFile.
|
|
224
|
+
const sourceFiles = fs.readdirSync(componentDir).filter(file => {
|
|
225
|
+
if (isExcludedFromCopy(file)) return false;
|
|
226
|
+
return fs.statSync(path.join(componentDir, file)).isFile();
|
|
227
|
+
});
|
|
228
|
+
const existingFiles = sourceFiles.filter(f =>
|
|
229
|
+
fs.existsSync(path.join(outputDir, f)),
|
|
230
|
+
);
|
|
231
|
+
if (existingFiles.length > 0 && !overwrite) {
|
|
232
|
+
const relOutputForMsg = path.relative(cwd, outputDir) || '.';
|
|
233
|
+
throw new AstryxError(
|
|
234
|
+
`Refusing to overwrite ${existingFiles.length} existing file(s) in ${relOutputForMsg}/. ` +
|
|
235
|
+
`Re-run with --overwrite (or -f) to replace them.`,
|
|
236
|
+
[],
|
|
237
|
+
ERROR_CODES.ERR_FILE_EXISTS,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fs.mkdirSync(outputDir, {recursive: true});
|
|
242
|
+
|
|
243
|
+
const files = fs.readdirSync(componentDir);
|
|
244
|
+
let copied = 0;
|
|
245
|
+
let usesStyleX = false;
|
|
246
|
+
for (const file of files) {
|
|
247
|
+
if (isExcludedFromCopy(file)) continue;
|
|
248
|
+
const srcPath = path.join(componentDir, file);
|
|
249
|
+
if (!fs.statSync(srcPath).isFile()) continue;
|
|
250
|
+
let content = fs.readFileSync(srcPath, 'utf-8');
|
|
251
|
+
if (file.endsWith('.ts') || file.endsWith('.tsx')) {
|
|
252
|
+
content = rewriteImports(content, owner.ownerPackage);
|
|
253
|
+
}
|
|
254
|
+
if (
|
|
255
|
+
(file.endsWith('.ts') || file.endsWith('.tsx')) &&
|
|
256
|
+
content.includes('@stylexjs/stylex')
|
|
257
|
+
) {
|
|
258
|
+
usesStyleX = true;
|
|
259
|
+
}
|
|
260
|
+
fs.writeFileSync(path.join(outputDir, file), content);
|
|
261
|
+
copied++;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const relOutput = path.relative(cwd, outputDir);
|
|
265
|
+
const copiedFiles = files.filter(
|
|
266
|
+
f =>
|
|
267
|
+
!isExcludedFromCopy(f) &&
|
|
268
|
+
fs.statSync(path.join(componentDir, f)).isFile(),
|
|
269
|
+
);
|
|
270
|
+
const feedback = buildFeedback(dirName, owner.issuesUrl);
|
|
271
|
+
|
|
272
|
+
/** @type {import('../../types/swizzle').SwizzleCopyResponse['data']} */
|
|
273
|
+
const data = {
|
|
274
|
+
component: dirName,
|
|
275
|
+
package: owner.package,
|
|
276
|
+
outputDir: relOutput,
|
|
277
|
+
filesCopied: copied,
|
|
278
|
+
files: copiedFiles.map(f => f),
|
|
279
|
+
usesStyleX,
|
|
280
|
+
};
|
|
281
|
+
if (feedback) data.feedback = feedback;
|
|
282
|
+
return {type: 'swizzle.copy', data};
|
|
283
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
3
|
import {describe, it, expect} from 'vitest';
|
|
4
|
-
import
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {rewriteImports, swizzle} from './swizzle.mjs';
|
|
7
|
+
|
|
8
|
+
// api/swizzle/ -> up 3 = packages/cli, up 4 = repo root (has packages/core).
|
|
9
|
+
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
|
|
5
10
|
|
|
6
11
|
describe('rewriteImports', () => {
|
|
7
12
|
it('rewrites ../theme/tokens to @astryxdesign/core/theme', () => {
|
|
@@ -51,3 +56,23 @@ describe('rewriteImports', () => {
|
|
|
51
56
|
);
|
|
52
57
|
});
|
|
53
58
|
});
|
|
59
|
+
|
|
60
|
+
describe('swizzle() API', () => {
|
|
61
|
+
it('no component → swizzle.list of core components', async () => {
|
|
62
|
+
const r = await swizzle(undefined, {cwd: REPO});
|
|
63
|
+
expect(r.type).toBe('swizzle.list');
|
|
64
|
+
expect(Array.isArray(r.data)).toBe(true);
|
|
65
|
+
expect(r.data).toContain('Button');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('--list → swizzle.list even with a component arg', async () => {
|
|
69
|
+
const r = await swizzle('Button', {cwd: REPO, list: true});
|
|
70
|
+
expect(r.type).toBe('swizzle.list');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('unknown component → AstryxError ERR_UNKNOWN_COMPONENT with suggestions', async () => {
|
|
74
|
+
await expect(swizzle('NotARealComponent99', {cwd: REPO})).rejects.toMatchObject({
|
|
75
|
+
code: 'ERR_UNKNOWN_COMPONENT',
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -62,7 +62,7 @@ function writeTemplate(pkgDir, id, {kind, body, withSource = true}) {
|
|
|
62
62
|
fs.writeFileSync(
|
|
63
63
|
docPath,
|
|
64
64
|
body ??
|
|
65
|
-
`import {${create}} from '${CLI_PKG}/
|
|
65
|
+
`import {${create}} from '${CLI_PKG}/authoring/template.mjs';\n` +
|
|
66
66
|
`export default ${create}({name: '${id} name', description: '${id} desc'});\n`,
|
|
67
67
|
);
|
|
68
68
|
if (withSource) {
|
|
@@ -166,7 +166,7 @@ describe('integration template discovery', () => {
|
|
|
166
166
|
);
|
|
167
167
|
fs.writeFileSync(
|
|
168
168
|
path.join(pkg2, 'templates', 'hero.doc.mjs'),
|
|
169
|
-
`import {createBlockTemplate} from '${CLI_PKG}/
|
|
169
|
+
`import {createBlockTemplate} from '${CLI_PKG}/authoring/template.mjs';\n` +
|
|
170
170
|
`export default createBlockTemplate({name: 'Hero block', description: 'b'});\n`,
|
|
171
171
|
);
|
|
172
172
|
fs.writeFileSync(
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
findRelatedBlocks,
|
|
22
22
|
} from './template.mjs';
|
|
23
23
|
|
|
24
|
-
/** Absolute path to the CLI package so fixtures can import /
|
|
24
|
+
/** Absolute path to the CLI package so fixtures can import /authoring/template.mjs. */
|
|
25
25
|
const CLI_PKG = path.resolve(import.meta.dirname, '..', '..');
|
|
26
26
|
|
|
27
27
|
let tmpDir;
|
|
@@ -68,7 +68,7 @@ function writeTemplate(pkgDir, id, {kind, suffix}) {
|
|
|
68
68
|
kind === 'page' ? 'createPageTemplate' : 'createBlockTemplate';
|
|
69
69
|
fs.writeFileSync(
|
|
70
70
|
docPath,
|
|
71
|
-
`import {${create}} from '${CLI_PKG}/
|
|
71
|
+
`import {${create}} from '${CLI_PKG}/authoring/template.mjs';\n` +
|
|
72
72
|
`export default ${create}({name: '${id} name', description: '${id} desc'});\n`,
|
|
73
73
|
);
|
|
74
74
|
fs.writeFileSync(
|
|
@@ -177,7 +177,7 @@ describe('external showcase blocks: .template.* discovered like legacy .doc.*',
|
|
|
177
177
|
fs.mkdirSync(fooDir, {recursive: true});
|
|
178
178
|
fs.writeFileSync(
|
|
179
179
|
path.join(fooDir, 'FooShowcase.template.ts'),
|
|
180
|
-
`import {createBlockTemplate} from '${CLI_PKG}/
|
|
180
|
+
`import {createBlockTemplate} from '${CLI_PKG}/authoring/template.mjs';\n` +
|
|
181
181
|
`export default createBlockTemplate({\n` +
|
|
182
182
|
` name: 'Foo — Showcase',\n` +
|
|
183
183
|
` description: 'Foo showcase.',\n` +
|
|
@@ -7,21 +7,54 @@
|
|
|
7
7
|
import * as fs from 'node:fs';
|
|
8
8
|
import * as path from 'node:path';
|
|
9
9
|
import {createJiti} from 'jiti';
|
|
10
|
-
import {loadModuleWithSchema} from '
|
|
11
|
-
import {TemplateEnvelopeSchema} from '
|
|
12
|
-
import {CLI_ROOT, discoverExternalPackages} from '
|
|
10
|
+
import {loadModuleWithSchema} from '../../lib/module-loader.mjs';
|
|
11
|
+
import {TemplateEnvelopeSchema} from '../../schemas/template-schema.mjs';
|
|
12
|
+
import {CLI_ROOT, discoverExternalPackages} from '../../utils/paths.mjs';
|
|
13
13
|
import {
|
|
14
14
|
assertWithin,
|
|
15
15
|
isFilePathArg,
|
|
16
16
|
PathSafetyError,
|
|
17
|
-
} from '
|
|
18
|
-
import {AstryxError} from '
|
|
19
|
-
import {ERROR_CODES} from '
|
|
20
|
-
import {Project} from '
|
|
17
|
+
} from '../../utils/path-safety.mjs';
|
|
18
|
+
import {AstryxError} from '../error.mjs';
|
|
19
|
+
import {ERROR_CODES} from '../../lib/error-codes.mjs';
|
|
20
|
+
import {Project} from '../../lib/project.mjs';
|
|
21
21
|
|
|
22
22
|
/** Identity used for core (built-in) templates in package-scoped listings. */
|
|
23
23
|
const CORE_PACKAGE = '@astryxdesign/core';
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* A discovered template (page or block), normalized across core, external, and
|
|
27
|
+
* integration sources. Not every source populates every field, so
|
|
28
|
+
* source-specific extras (`aspectRatio`, `isShowcase`, `package`) are optional.
|
|
29
|
+
* @typedef {object} DiscoveredTemplate
|
|
30
|
+
* @property {'page'|'block'} type
|
|
31
|
+
* @property {string} dirName
|
|
32
|
+
* @property {string} name
|
|
33
|
+
* @property {string} description
|
|
34
|
+
* @property {string} [category]
|
|
35
|
+
* @property {boolean} [isReady]
|
|
36
|
+
* @property {boolean} [scaffold]
|
|
37
|
+
* @property {number} [aspectRatio]
|
|
38
|
+
* @property {string[]} [componentsUsed]
|
|
39
|
+
* @property {boolean} [isShowcase]
|
|
40
|
+
* @property {string} filePath
|
|
41
|
+
* @property {string} docPath
|
|
42
|
+
* @property {string} [package]
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An integration-template discovery error.
|
|
47
|
+
* @typedef {object} TemplateDiscoveryError
|
|
48
|
+
* @property {string} package
|
|
49
|
+
* @property {string} [template]
|
|
50
|
+
* @property {string} message
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The loaded metadata object from a template spec (loose — authored shape).
|
|
55
|
+
* @typedef {Record<string, any> | undefined | null} TemplateDocModule
|
|
56
|
+
*/
|
|
57
|
+
|
|
25
58
|
/**
|
|
26
59
|
* Canonical basename suffixes for template-spec files, in precedence order.
|
|
27
60
|
* A template spec exports a `createBlockTemplate`/`createPageTemplate` result
|
|
@@ -60,6 +93,7 @@ function matchedTemplateSuffix(file) {
|
|
|
60
93
|
*/
|
|
61
94
|
const TEMPLATE_SUFFIX_RE = /\.(template|doc)\.(ts|mjs|js)$/;
|
|
62
95
|
|
|
96
|
+
/** @type {ReturnType<typeof createJiti> | undefined} */
|
|
63
97
|
let jitiInstance;
|
|
64
98
|
/** Lazily-created jiti for loading `.ts` template specs (JSX-capable). */
|
|
65
99
|
function getJiti() {
|
|
@@ -69,7 +103,6 @@ function getJiti() {
|
|
|
69
103
|
return jitiInstance;
|
|
70
104
|
}
|
|
71
105
|
|
|
72
|
-
|
|
73
106
|
/**
|
|
74
107
|
* Load an integration template doc module and validate it against the template
|
|
75
108
|
* envelope at the load boundary. Default export only — `.ts` via jiti,
|
|
@@ -145,7 +178,7 @@ export function stripTemplateAssetRefs(source) {
|
|
|
145
178
|
* native dynamic import. Returns null if the file does not exist.
|
|
146
179
|
*
|
|
147
180
|
* @param {string} docPath absolute path to the spec file
|
|
148
|
-
* @returns {Promise<
|
|
181
|
+
* @returns {Promise<TemplateDocModule>}
|
|
149
182
|
*/
|
|
150
183
|
async function loadDocModule(docPath) {
|
|
151
184
|
if (!fs.existsSync(docPath)) return null;
|
|
@@ -159,6 +192,7 @@ async function loadDocModule(docPath) {
|
|
|
159
192
|
export {discoverAll as discoverTemplates};
|
|
160
193
|
|
|
161
194
|
export function listTemplates() {
|
|
195
|
+
/** @type {string[]} */
|
|
162
196
|
const all = [];
|
|
163
197
|
if (fs.existsSync(PAGES_DIR)) {
|
|
164
198
|
all.push(
|
|
@@ -171,7 +205,13 @@ export function listTemplates() {
|
|
|
171
205
|
return all.sort();
|
|
172
206
|
}
|
|
173
207
|
|
|
208
|
+
/**
|
|
209
|
+
* @param {string} dir
|
|
210
|
+
* @param {RegExp} pattern
|
|
211
|
+
* @returns {string[]}
|
|
212
|
+
*/
|
|
174
213
|
function findDocFiles(dir, pattern) {
|
|
214
|
+
/** @type {string[]} */
|
|
175
215
|
const results = [];
|
|
176
216
|
if (!fs.existsSync(dir)) return results;
|
|
177
217
|
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
|
|
@@ -205,6 +245,7 @@ async function discoverPages() {
|
|
|
205
245
|
.readdirSync(PAGES_DIR, {withFileTypes: true})
|
|
206
246
|
.filter(e => e.isDirectory());
|
|
207
247
|
|
|
248
|
+
/** @type {DiscoveredTemplate[]} */
|
|
208
249
|
const templates = [];
|
|
209
250
|
for (const dir of dirs) {
|
|
210
251
|
const dirPath = path.join(PAGES_DIR, dir.name);
|
|
@@ -228,9 +269,11 @@ async function discoverPages() {
|
|
|
228
269
|
|
|
229
270
|
async function discoverBlocks() {
|
|
230
271
|
const docFiles = findDocFiles(BLOCKS_DIR, TEMPLATE_SUFFIX_RE);
|
|
272
|
+
/** @type {DiscoveredTemplate[]} */
|
|
231
273
|
const blocks = [];
|
|
232
274
|
for (const docPath of docFiles) {
|
|
233
275
|
const suffix = matchedTemplateSuffix(docPath);
|
|
276
|
+
if (!suffix) continue;
|
|
234
277
|
const basename = path.basename(docPath, suffix);
|
|
235
278
|
const tsxPath = path.join(path.dirname(docPath), basename + '.tsx');
|
|
236
279
|
if (!fs.existsSync(tsxPath)) continue;
|
|
@@ -262,6 +305,7 @@ async function discoverBlocks() {
|
|
|
262
305
|
*/
|
|
263
306
|
async function discoverExternalBlocks(cwd = process.cwd()) {
|
|
264
307
|
const externals = discoverExternalPackages(cwd);
|
|
308
|
+
/** @type {DiscoveredTemplate[]} */
|
|
265
309
|
const blocks = [];
|
|
266
310
|
|
|
267
311
|
for (const ext of externals) {
|
|
@@ -269,6 +313,7 @@ async function discoverExternalBlocks(cwd = process.cwd()) {
|
|
|
269
313
|
const docFiles = findDocFiles(ext.blocksDir, TEMPLATE_SUFFIX_RE);
|
|
270
314
|
for (const docPath of docFiles) {
|
|
271
315
|
const suffix = matchedTemplateSuffix(docPath);
|
|
316
|
+
if (!suffix) continue;
|
|
272
317
|
const basename = path.basename(docPath, suffix);
|
|
273
318
|
const tsxPath = path.join(path.dirname(docPath), basename + '.tsx');
|
|
274
319
|
if (!fs.existsSync(tsxPath)) continue;
|
|
@@ -297,6 +342,7 @@ async function discoverExternalBlocks(cwd = process.cwd()) {
|
|
|
297
342
|
/**
|
|
298
343
|
* Discover all blocks — core + external packages.
|
|
299
344
|
* @param {string} [cwd]
|
|
345
|
+
* @returns {Promise<DiscoveredTemplate[]>}
|
|
300
346
|
*/
|
|
301
347
|
async function discoverAllBlocks(cwd = process.cwd()) {
|
|
302
348
|
const [core, external] = await Promise.all([
|
|
@@ -306,6 +352,10 @@ async function discoverAllBlocks(cwd = process.cwd()) {
|
|
|
306
352
|
return [...core, ...external];
|
|
307
353
|
}
|
|
308
354
|
|
|
355
|
+
/**
|
|
356
|
+
* @param {string} [cwd]
|
|
357
|
+
* @returns {Promise<DiscoveredTemplate[]>}
|
|
358
|
+
*/
|
|
309
359
|
async function discoverAll(cwd = process.cwd()) {
|
|
310
360
|
const [pages, blocks, integration] = await Promise.all([
|
|
311
361
|
discoverPages(),
|
|
@@ -323,7 +373,7 @@ async function discoverAll(cwd = process.cwd()) {
|
|
|
323
373
|
* when the caller wants to warn about malformed integration templates.
|
|
324
374
|
*
|
|
325
375
|
* @param {string} [cwd]
|
|
326
|
-
* @returns {Promise<{templates:
|
|
376
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
|
|
327
377
|
*/
|
|
328
378
|
async function discoverAllWithErrors(cwd = process.cwd()) {
|
|
329
379
|
const [pages, blocks, integration] = await Promise.all([
|
|
@@ -348,8 +398,10 @@ export {discoverAllWithErrors};
|
|
|
348
398
|
* @returns {string[]}
|
|
349
399
|
*/
|
|
350
400
|
function findIntegrationDocFiles(root) {
|
|
401
|
+
/** @type {string[]} */
|
|
351
402
|
const results = [];
|
|
352
403
|
if (!fs.existsSync(root)) return results;
|
|
404
|
+
/** @param {string} dir */
|
|
353
405
|
const walk = dir => {
|
|
354
406
|
for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
|
|
355
407
|
const full = path.join(dir, entry.name);
|
|
@@ -377,16 +429,21 @@ function findIntegrationDocFiles(root) {
|
|
|
377
429
|
* template is skipped (recorded in `errors`).
|
|
378
430
|
*
|
|
379
431
|
* @param {string} [cwd]
|
|
380
|
-
* @returns {Promise<{templates:
|
|
432
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
|
|
381
433
|
*/
|
|
382
434
|
async function discoverIntegrationTemplates(cwd = process.cwd()) {
|
|
435
|
+
/** @type {DiscoveredTemplate[]} */
|
|
383
436
|
const templates = [];
|
|
437
|
+
/** @type {TemplateDiscoveryError[]} */
|
|
384
438
|
const errors = [];
|
|
385
439
|
|
|
386
440
|
let loadedIntegrations;
|
|
387
441
|
try {
|
|
388
442
|
const project = await Project.load(cwd);
|
|
389
|
-
loadedIntegrations =
|
|
443
|
+
loadedIntegrations =
|
|
444
|
+
/** @type {import('../../lib/integrations.mjs').LoadedIntegration[]} */ (
|
|
445
|
+
project.loadedIntegrations
|
|
446
|
+
);
|
|
390
447
|
} catch {
|
|
391
448
|
// Config load failures are surfaced elsewhere (discover/doctor); here we
|
|
392
449
|
// simply contribute no integration templates.
|
|
@@ -409,10 +466,12 @@ async function discoverIntegrationTemplates(cwd = process.cwd()) {
|
|
|
409
466
|
* than thrown. Exposed for `validate-integration`.
|
|
410
467
|
*
|
|
411
468
|
* @param {{name?: string, __spec?: string, templates?: string}} integration
|
|
412
|
-
* @returns {Promise<{templates:
|
|
469
|
+
* @returns {Promise<{templates: DiscoveredTemplate[], errors: TemplateDiscoveryError[]}>}
|
|
413
470
|
*/
|
|
414
471
|
export async function discoverIntegrationTemplatesForOne(integration) {
|
|
472
|
+
/** @type {DiscoveredTemplate[]} */
|
|
415
473
|
const templates = [];
|
|
474
|
+
/** @type {TemplateDiscoveryError[]} */
|
|
416
475
|
const errors = [];
|
|
417
476
|
|
|
418
477
|
const root = integration?.templates;
|
|
@@ -421,6 +480,7 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
421
480
|
|
|
422
481
|
for (const docPath of findIntegrationDocFiles(root)) {
|
|
423
482
|
const suffix = matchedTemplateSuffix(docPath);
|
|
483
|
+
if (!suffix) continue;
|
|
424
484
|
const id = path
|
|
425
485
|
.relative(root, docPath)
|
|
426
486
|
.slice(0, -suffix.length)
|
|
@@ -444,7 +504,7 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
444
504
|
errors.push({
|
|
445
505
|
package: pkgLabel,
|
|
446
506
|
template: id,
|
|
447
|
-
message: `Template "${id}" failed to load: ${err.message}`,
|
|
507
|
+
message: `Template "${id}" failed to load: ${/** @type {any} */ (err).message}`,
|
|
448
508
|
});
|
|
449
509
|
continue;
|
|
450
510
|
}
|
|
@@ -480,10 +540,15 @@ export async function discoverIntegrationTemplatesForOne(integration) {
|
|
|
480
540
|
return {templates, errors};
|
|
481
541
|
}
|
|
482
542
|
|
|
543
|
+
/**
|
|
544
|
+
* @param {string} componentName
|
|
545
|
+
* @param {string} [cwd]
|
|
546
|
+
* @returns {Promise<DiscoveredTemplate[]>}
|
|
547
|
+
*/
|
|
483
548
|
export async function findRelatedBlocks(componentName, cwd) {
|
|
484
549
|
const blocks = await discoverAllBlocks(cwd);
|
|
485
550
|
return blocks.filter(b =>
|
|
486
|
-
b.componentsUsed.some(c => c.toLowerCase() === componentName.toLowerCase()),
|
|
551
|
+
(b.componentsUsed ?? []).some(c => c.toLowerCase() === componentName.toLowerCase()),
|
|
487
552
|
);
|
|
488
553
|
}
|
|
489
554
|
|
|
@@ -506,7 +571,7 @@ export async function findShowcase(componentName, cwd, options) {
|
|
|
506
571
|
return true;
|
|
507
572
|
});
|
|
508
573
|
|
|
509
|
-
const toResult = b => ({
|
|
574
|
+
const toResult = (/** @type {DiscoveredTemplate} */ b) => ({
|
|
510
575
|
name: b.name,
|
|
511
576
|
aspectRatio: b.aspectRatio,
|
|
512
577
|
filePath: b.filePath,
|
|
@@ -515,14 +580,14 @@ export async function findShowcase(componentName, cwd, options) {
|
|
|
515
580
|
|
|
516
581
|
// Priority 1: own directory (components/Badge/ for "Badge")
|
|
517
582
|
const dirMatch = showcases.find(b => {
|
|
518
|
-
const catDir = path.basename(b.category).toLowerCase();
|
|
583
|
+
const catDir = path.basename(b.category ?? '').toLowerCase();
|
|
519
584
|
return catDir === lc;
|
|
520
585
|
});
|
|
521
586
|
if (dirMatch) return toResult(dirMatch);
|
|
522
587
|
|
|
523
588
|
// Priority 2: componentsUsed in any directory (ClickableCard in Card/)
|
|
524
589
|
const usedMatch = showcases.find(b =>
|
|
525
|
-
b.componentsUsed.some(c => c.toLowerCase() === lc),
|
|
590
|
+
(b.componentsUsed ?? []).some(c => c.toLowerCase() === lc),
|
|
526
591
|
);
|
|
527
592
|
if (usedMatch) return toResult(usedMatch);
|
|
528
593
|
|
|
@@ -540,6 +605,10 @@ const UBIQUITOUS = new Set([
|
|
|
540
605
|
'Icon',
|
|
541
606
|
]);
|
|
542
607
|
|
|
608
|
+
/**
|
|
609
|
+
* @param {string} pagePath
|
|
610
|
+
* @returns {string[]}
|
|
611
|
+
*/
|
|
543
612
|
export function extractComponents(pagePath) {
|
|
544
613
|
const src = fs.readFileSync(pagePath, 'utf-8');
|
|
545
614
|
// Match JSX opening tags, e.g. `<Section` or the legacy `<XDSSection`.
|
|
@@ -548,6 +617,7 @@ export function extractComponents(pagePath) {
|
|
|
548
617
|
// JSX-tag boundary keeps this precise (avoids matching imports/comments/
|
|
549
618
|
// identifiers) while remaining prefix-agnostic.
|
|
550
619
|
const tagRegex = /<(XDS)?([A-Z]\w+)/g;
|
|
620
|
+
/** @type {string[]} */
|
|
551
621
|
const matches = [];
|
|
552
622
|
let m;
|
|
553
623
|
while ((m = tagRegex.exec(src)) !== null) {
|
|
@@ -617,10 +687,11 @@ const SPATIAL_PROPS = [
|
|
|
617
687
|
* @returns {string[]}
|
|
618
688
|
*/
|
|
619
689
|
function extractSpatialAttrs(tagText) {
|
|
690
|
+
/** @type {string[]} */
|
|
620
691
|
const attrs = [];
|
|
621
692
|
for (const name of SPATIAL_PROPS) {
|
|
622
693
|
const eqMatch = tagText.match(new RegExp(`\\b${name}\\s*=\\s*`));
|
|
623
|
-
if (eqMatch) {
|
|
694
|
+
if (eqMatch && eqMatch.index != null) {
|
|
624
695
|
const start = eqMatch.index;
|
|
625
696
|
let i = eqMatch.index + eqMatch[0].length;
|
|
626
697
|
const rest = tagText.slice(i);
|
|
@@ -662,13 +733,19 @@ function extractSpatialAttrs(tagText) {
|
|
|
662
733
|
return attrs;
|
|
663
734
|
}
|
|
664
735
|
|
|
736
|
+
/**
|
|
737
|
+
* @param {string} source
|
|
738
|
+
* @returns {string}
|
|
739
|
+
*/
|
|
665
740
|
function extractSkeleton(source) {
|
|
666
741
|
const lines = source.split('\n');
|
|
742
|
+
/** @type {string[]} */
|
|
667
743
|
const out = [];
|
|
668
744
|
let depth = 0;
|
|
669
745
|
let capturing = false;
|
|
670
746
|
let inDefaultExport = false;
|
|
671
747
|
const MAX_LINES = 35;
|
|
748
|
+
/** @type {string[]} */
|
|
672
749
|
const depthStack = [];
|
|
673
750
|
|
|
674
751
|
for (let i = 0; i < lines.length; i++) {
|