@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db52d98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -15
- package/api/blog/blog.doc.mjs +1 -0
- package/api/build/_adapter.d.mts +50 -0
- package/api/build/_adapter.mjs +60 -0
- package/api/build/build.doc.mjs +14 -7
- package/api/build/build.test.mjs +184 -6
- package/api/build/build.type.d.mts +57 -2
- package/api/build/build.type.mjs +30 -7
- package/api/build/help/help.d.mts +4 -0
- package/api/build/help/help.mjs +24 -11
- package/api/build/kit/kit.d.mts +4 -1
- package/api/build/kit/kit.mjs +165 -49
- package/api/build/kit/rank.d.mts +44 -0
- package/api/build/kit/rank.mjs +432 -0
- package/api/build/kit/rank.test.mjs +196 -0
- package/api/component/_adapter.d.mts +6 -12
- package/api/component/_adapter.mjs +20 -10
- package/api/component/component.doc.mjs +1 -0
- package/api/component/component.mjs +60 -12
- package/api/component/list/list.mjs +3 -2
- package/api/discover/discover.doc.mjs +1 -0
- package/api/docs/_adapter.d.mts +252 -34
- package/api/docs/_adapter.mjs +923 -132
- package/api/docs/detail/detail.mjs +11 -3
- package/api/docs/detail/section/section.mjs +24 -13
- package/api/docs/detail/section/section.test.mjs +15 -6
- package/api/docs/docs.d.mts +5 -3
- package/api/docs/docs.doc.mjs +39 -17
- package/api/docs/docs.mjs +44 -8
- package/api/docs/docs.test.mjs +158 -4
- package/api/docs/docs.type.d.mts +181 -2
- package/api/docs/docs.type.mjs +117 -3
- package/api/docs/index/index.mjs +11 -3
- package/api/docs/index/index.test.mjs +1 -1
- package/api/docs/integration-tree.test.mjs +555 -0
- package/api/docs/integrationDocs.test.mjs +14 -14
- package/api/docs/list/list.mjs +28 -12
- package/api/docs/node/node.d.mts +43 -0
- package/api/docs/node/node.mjs +192 -0
- package/api/docs/reference-blocks.test.mjs +406 -0
- package/api/doctor/doctor.d.mts +54 -4
- package/api/doctor/doctor.doc.mjs +1 -0
- package/api/doctor/doctor.mjs +331 -16
- package/api/doctor/doctor.test.mjs +420 -7
- package/api/gap-report/gap-report.doc.mjs +1 -0
- package/api/hook/_adapter.mjs +19 -5
- package/api/hook/hook.doc.mjs +1 -0
- package/api/hook/list/list.d.mts +1 -1
- package/api/hook/list/list.mjs +69 -17
- package/api/index.d.mts +1 -1
- package/api/index.mjs +1 -0
- package/api/init/init.doc.mjs +2 -1
- package/api/integration/add-contribution.d.mts +2 -1
- package/api/integration/add-contribution.mjs +114 -12
- package/api/integration/add-contribution.test.mjs +174 -3
- package/api/integration/add-theme.mjs +34 -64
- package/api/integration/add-theme.test.mjs +105 -21
- package/api/integration/authoring-checks.mjs +138 -28
- package/api/integration/authoring-checks.test.mjs +179 -7
- package/api/integration/authoring-checks.type.mjs +6 -1
- package/api/integration/integration-authoring.type.d.mts +2 -0
- package/api/integration/integration-authoring.type.mjs +2 -0
- package/api/integration/integration-block-exports.test.mjs +10 -6
- package/api/integration/integrationAdd.doc.mjs +7 -0
- package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
- package/api/integration/integrationAddCodemod.doc.mjs +1 -0
- package/api/integration/integrationAddComponent.doc.mjs +1 -0
- package/api/integration/integrationAddDoc.doc.mjs +8 -1
- package/api/integration/integrationAddTemplate.doc.mjs +1 -0
- package/api/integration/integrationAddTheme.doc.mjs +6 -5
- package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
- package/api/integration/integrationDocConflicts.doc.mjs +2 -1
- package/api/integration/integrationPackCheck.doc.mjs +2 -1
- package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
- package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
- package/api/integration/pack-check.mjs +34 -0
- package/api/integration/pack-check.test.mjs +138 -47
- package/api/integration/pack-check.type.d.mts +26 -2
- package/api/integration/pack-check.type.mjs +14 -1
- package/api/integration/summarizeIssues.doc.mjs +1 -0
- package/api/integration/template-conflict-compatibility.test.mjs +73 -0
- package/api/integration/validate-integration-fixes.test.mjs +1389 -0
- package/api/integration/validate-integration.mjs +50 -102
- package/api/integration/validate-integration.test.mjs +88 -26
- package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
- package/api/integration/validateIntegration.doc.mjs +2 -1
- package/api/json/assertResponse.doc.mjs +1 -0
- package/api/json/index.ts +2 -0
- package/api/json/isError.doc.mjs +1 -0
- package/api/json/parseResponse.doc.mjs +1 -0
- package/api/layout/_adapter.mjs +20 -5
- package/api/layout/layoutCheck.doc.mjs +1 -0
- package/api/layout/layoutExpand.doc.mjs +1 -0
- package/api/layout/layoutGrammar.doc.mjs +1 -0
- package/api/search/search.d.mts +59 -1
- package/api/search/search.doc.mjs +5 -3
- package/api/search/search.mjs +458 -77
- package/api/search/search.test.mjs +91 -2
- package/api/search/search.type.d.mts +13 -1
- package/api/search/search.type.mjs +4 -1
- package/api/swizzle/swizzle.doc.mjs +1 -0
- package/api/template/list/list.mjs +1 -0
- package/api/template/table-floating-bulk-actions.test.mjs +66 -0
- package/api/template/template-integration.test.mjs +1072 -3
- package/api/template/template-suffix.test.mjs +41 -21
- package/api/template/template.doc.mjs +28 -7
- package/api/template/template.mjs +45 -8
- package/api/template/template.type.d.mts +6 -8
- package/api/template/template.type.mjs +3 -2
- package/api/theme/_adapter.d.mts +2 -3
- package/api/theme/_adapter.mjs +4 -5
- package/api/theme/add/add.binary.test.mjs +10 -17
- package/api/theme/add/add.test.mjs +14 -1
- package/api/theme/generateTonalPalette.doc.mjs +1 -0
- package/api/theme/integration-themes.test.mjs +39 -28
- package/api/theme/list/list.test.mjs +19 -20
- package/api/theme/listThemes.doc.mjs +6 -5
- package/api/theme/template/template.test.mjs +5 -0
- package/api/theme/themeAdd.doc.mjs +4 -3
- package/api/theme/themeBuild.doc.mjs +1 -0
- package/api/theme/themeList.doc.mjs +6 -3
- package/api/theme/themeListAvailable.doc.mjs +5 -3
- package/api/theme/themePaletteGenerate.doc.mjs +1 -0
- package/api/theme/themeTargets.doc.mjs +1 -0
- package/api/theme/themeTemplate.doc.mjs +2 -1
- package/api/upgrade/_adapter.d.mts +32 -5
- package/api/upgrade/_adapter.mjs +97 -69
- package/api/upgrade/provider-agreement.test.mjs +152 -0
- package/api/upgrade/run/run.mjs +356 -59
- package/api/upgrade/upgrade.doc.mjs +6 -1
- package/api/upgrade/upgrade.type.d.mts +34 -0
- package/api/upgrade/upgrade.type.mjs +15 -0
- package/assets/codemods/__tests__/runner.test.mjs +330 -8
- package/assets/codemods/integration-discovery.mjs +8 -2
- package/assets/codemods/integration-discovery.test.mjs +15 -0
- package/assets/codemods/integration-runner.mjs +56 -4
- package/assets/codemods/integration-runner.protection.test.mjs +153 -0
- package/assets/codemods/run-codemod.mjs +177 -34
- package/assets/codemods/runner.mjs +350 -102
- package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
- package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
- package/assets/codemods/transforms/next/index.mjs +19 -1
- package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
- package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
- package/assets/docs/getting-started.doc.mjs +2 -2
- package/assets/docs/layout.doc.dense.mjs +2 -2
- package/assets/docs/layout.doc.mjs +1 -1
- package/assets/docs/principles.doc.mjs +6 -6
- package/assets/docs/styling-libraries.doc.mjs +3 -3
- package/assets/docs/styling.doc.mjs +4 -4
- package/assets/docs/theme.doc.mjs +5 -5
- package/assets/docs/tokens.doc.mjs +1 -1
- package/assets/docs/tree/api.doc.mjs +30 -0
- package/assets/docs/tree/cli.doc.mjs +23 -0
- package/assets/docs/tree/commands.doc.mjs +25 -0
- package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +66 -38
- package/assets/docs/tree/integrations.test.mjs +62 -0
- package/assets/docs/tree/writing-docs.doc.mjs +286 -0
- package/assets/docs/working-with-ai.doc.mjs +3 -3
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
- package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
- package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
- package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
- package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
- package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
- package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
- package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
- package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
- package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
- package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
- package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
- package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
- package/authoring/codemod/codemod.doc.mjs +1 -1
- package/authoring/codemod/type.ts +12 -0
- package/authoring/config/config.doc.mjs +1 -1
- package/authoring/debug/debug.doc.d.mts +11 -0
- package/authoring/debug/debug.doc.mjs +182 -0
- package/authoring/debug/parse.d.mts +3 -3
- package/authoring/doctypes/_schema.d.mts +119 -117
- package/authoring/doctypes/_schema.mjs +54 -3
- package/authoring/doctypes/base/graph-fields.doc.mjs +8 -6
- package/authoring/doctypes/base/type.ts +6 -5
- package/authoring/doctypes/command/command.doc.mjs +1 -1
- package/authoring/doctypes/command/type.ts +2 -2
- package/authoring/doctypes/component/type.ts +2 -2
- package/authoring/doctypes/doctypes-new.test.mjs +48 -6
- package/authoring/doctypes/enum/enum.doc.mjs +1 -1
- package/authoring/doctypes/enum/type.ts +1 -1
- package/authoring/doctypes/function/function.doc.mjs +3 -2
- package/authoring/doctypes/function/type.ts +3 -2
- package/authoring/doctypes/hook/type.ts +2 -2
- package/authoring/doctypes/load-contract.test.mjs +28 -2
- package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
- package/authoring/doctypes/namespace/parse.test.mjs +23 -25
- package/authoring/doctypes/namespace/type.ts +5 -2
- package/authoring/doctypes/parse.d.mts +4 -2
- package/authoring/doctypes/parse.mjs +10 -5
- package/authoring/doctypes/reference/reference.doc.mjs +35 -6
- package/authoring/doctypes/reference/type.ts +30 -13
- package/authoring/doctypes/schema/schema.doc.mjs +1 -1
- package/authoring/doctypes/schema/type.ts +1 -2
- package/authoring/doctypes/template/parse.d.mts +2 -0
- package/authoring/doctypes/template/parse.mjs +4 -0
- package/authoring/doctypes/template/parse.test.mjs +18 -0
- package/authoring/doctypes/template/template.doc.mjs +9 -3
- package/authoring/doctypes/template/type.ts +8 -0
- package/authoring/doctypes/theme/parse.d.mts +35 -0
- package/authoring/doctypes/theme/parse.mjs +76 -0
- package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
- package/authoring/doctypes/theme/theme.doc.mjs +79 -0
- package/authoring/doctypes/theme/type.ts +42 -0
- package/authoring/doctypes/types.ts +2 -1
- package/authoring/gap-report/gap-report.doc.d.mts +12 -0
- package/authoring/gap-report/gap-report.doc.mjs +183 -0
- package/authoring/identity/identity.doc.mjs +2 -2
- package/authoring/index.d.mts +1 -0
- package/authoring/index.d.ts +7 -4
- package/authoring/index.mjs +2 -1
- package/authoring/integration/integration.doc.mjs +2 -2
- package/authoring/integration/type.ts +4 -10
- package/clients/cli/__tests__/cliManifest.test.ts +27 -29
- package/clients/cli/commands/blog.doc.mjs +1 -1
- package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
- package/clients/cli/commands/build-theme.mjs +11 -45
- package/clients/cli/commands/build.doc.mjs +12 -7
- package/clients/cli/commands/build.mjs +105 -67
- package/clients/cli/commands/build.text-fields.test.mjs +41 -0
- package/clients/cli/commands/component/index.mjs +1 -1
- package/clients/cli/commands/component-ownership.test.mjs +3 -3
- package/clients/cli/commands/component.doc.mjs +1 -1
- package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
- package/clients/cli/commands/discover.doc.mjs +1 -1
- package/clients/cli/commands/discover.mjs +1 -1
- package/clients/cli/commands/docs.doc.mjs +21 -9
- package/clients/cli/commands/docs.mjs +184 -70
- package/clients/cli/commands/docs.test.mjs +120 -16
- package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
- package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
- package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
- package/clients/cli/commands/doctor-integration.test.mjs +90 -8
- package/clients/cli/commands/doctor.doc.mjs +1 -1
- package/clients/cli/commands/doctor.mjs +53 -16
- package/clients/cli/commands/gap-report.doc.mjs +1 -1
- package/clients/cli/commands/hook.doc.mjs +1 -1
- package/clients/cli/commands/init.doc.mjs +1 -1
- package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
- package/clients/cli/commands/integration-add.doc.mjs +11 -1
- package/clients/cli/commands/integration-pack.doc.mjs +1 -1
- package/clients/cli/commands/integration-real-world.test.mjs +3 -9
- package/clients/cli/commands/integration.doc.mjs +1 -1
- package/clients/cli/commands/integration.mjs +1 -0
- package/clients/cli/commands/layout-check.doc.mjs +1 -1
- package/clients/cli/commands/layout-expand.doc.mjs +1 -1
- package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
- package/clients/cli/commands/layout.doc.mjs +1 -1
- package/clients/cli/commands/manifest.doc.mjs +1 -1
- package/clients/cli/commands/search.doc.mjs +1 -1
- package/clients/cli/commands/search.mjs +11 -2
- package/clients/cli/commands/setup-nudge.test.mjs +6 -0
- package/clients/cli/commands/swizzle.doc.mjs +1 -1
- package/clients/cli/commands/template.doc.mjs +24 -7
- package/clients/cli/commands/template.mjs +4 -91
- package/clients/cli/commands/text-json-parity.test.mjs +719 -0
- package/clients/cli/commands/theme-add.doc.mjs +2 -2
- package/clients/cli/commands/theme-build.doc.mjs +1 -1
- package/clients/cli/commands/theme-list.doc.mjs +2 -2
- package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
- package/clients/cli/commands/theme-palette.doc.mjs +1 -1
- package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
- package/clients/cli/commands/theme-targets.doc.mjs +1 -1
- package/clients/cli/commands/theme-template.doc.mjs +1 -1
- package/clients/cli/commands/theme.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.doc.mjs +3 -2
- package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
- package/clients/cli/commands/upgrade.mjs +29 -7
- package/clients/cli/formatters/index.mjs +2 -0
- package/clients/cli/formatters/index.test.mjs +6 -0
- package/clients/cli/index.mjs +8 -0
- package/clients/cli/lib/hook-format.mjs +14 -5
- package/clients/cli/lib/manifest.mjs +9 -2
- package/foundation/agent-docs/agent-docs.d.mts +3 -2
- package/foundation/agent-docs/agent-docs.mjs +13 -9
- package/foundation/agent-docs/agent-docs.test.mjs +19 -1
- package/foundation/config/project-themes.test.mjs +11 -19
- package/foundation/config/project.d.mts +20 -11
- package/foundation/config/project.mjs +123 -80
- package/foundation/config/project.test.mjs +144 -21
- package/foundation/discovery/authoring-self-docs.d.mts +18 -0
- package/foundation/discovery/authoring-self-docs.mjs +37 -15
- package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
- package/foundation/discovery/authoring-surface.d.mts +74 -0
- package/foundation/discovery/authoring-surface.mjs +525 -0
- package/foundation/discovery/authoring-surface.test.mjs +392 -0
- package/foundation/discovery/cli-self-docs.d.mts +119 -0
- package/foundation/discovery/cli-self-docs.mjs +490 -0
- package/foundation/discovery/cli-self-docs.test.mjs +375 -0
- package/foundation/discovery/component-discovery.d.mts +38 -0
- package/foundation/discovery/component-discovery.mjs +48 -0
- package/foundation/discovery/component-loader.d.mts +35 -38
- package/foundation/discovery/component-loader.mjs +53 -222
- package/foundation/discovery/docs-discovery.d.mts +112 -11
- package/foundation/discovery/docs-discovery.mjs +231 -36
- package/foundation/discovery/docs-discovery.test.mjs +111 -32
- package/foundation/discovery/docs-output-budget.d.mts +2 -2
- package/foundation/discovery/docs-output-budget.mjs +1 -1
- package/foundation/discovery/docs-section-key.d.mts +28 -10
- package/foundation/discovery/docs-section-key.mjs +134 -33
- package/foundation/discovery/docs-section-key.test.mjs +41 -19
- package/foundation/discovery/template-adapter.d.mts +99 -6
- package/foundation/discovery/template-adapter.mjs +480 -57
- package/foundation/discovery/template-adapter.test.mjs +57 -0
- package/foundation/discovery/template-conflict-release.d.mts +13 -0
- package/foundation/discovery/template-conflict-release.mjs +40 -0
- package/foundation/discovery/template-conflict-release.test.mjs +40 -0
- package/foundation/discovery/theme-discovery.d.mts +67 -7
- package/foundation/discovery/theme-discovery.mjs +916 -186
- package/foundation/discovery/theme-discovery.test.mjs +613 -219
- package/foundation/doc-compiler/bundle.d.mts +47 -0
- package/foundation/doc-compiler/bundle.mjs +278 -0
- package/foundation/doc-compiler/bundle.test.mjs +266 -0
- package/foundation/doc-compiler/compile.d.mts +220 -39
- package/foundation/doc-compiler/compile.mjs +311 -15
- package/foundation/doc-compiler/diagnostics.d.mts +126 -0
- package/foundation/doc-compiler/diagnostics.mjs +305 -0
- package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
- package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
- package/foundation/doc-compiler/import.d.mts +24 -0
- package/foundation/doc-compiler/import.mjs +59 -0
- package/foundation/doc-compiler/inputs.d.mts +102 -0
- package/foundation/doc-compiler/inputs.mjs +291 -0
- package/foundation/doc-compiler/inputs.test.mjs +299 -0
- package/foundation/doc-compiler/ir.d.mts +13 -0
- package/foundation/doc-compiler/ir.mjs +196 -12
- package/foundation/doc-compiler/lenses.d.mts +8 -5
- package/foundation/doc-compiler/lenses.mjs +50 -4
- package/foundation/doc-compiler/links.d.mts +162 -0
- package/foundation/doc-compiler/links.mjs +294 -0
- package/foundation/doc-compiler/links.test.mjs +192 -0
- package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
- package/foundation/doc-compiler/overlays.d.mts +37 -0
- package/foundation/doc-compiler/overlays.mjs +206 -0
- package/foundation/doc-compiler/parse-readable.d.mts +9 -0
- package/foundation/doc-compiler/parse-readable.mjs +29 -0
- package/foundation/doc-compiler/read.d.mts +127 -0
- package/foundation/doc-compiler/read.mjs +325 -0
- package/foundation/doc-compiler/read.test.mjs +313 -0
- package/foundation/doc-compiler/source.d.mts +33 -0
- package/foundation/doc-compiler/source.mjs +128 -0
- package/foundation/doc-compiler/tree.d.mts +288 -0
- package/foundation/doc-compiler/tree.mjs +876 -0
- package/foundation/doc-compiler/tree.test.mjs +598 -0
- package/foundation/fs/file-protection.d.mts +33 -0
- package/foundation/fs/file-protection.mjs +825 -0
- package/foundation/fs/file-protection.test.mjs +250 -0
- package/foundation/integrations/autolink.d.mts +58 -1
- package/foundation/integrations/autolink.mjs +143 -57
- package/foundation/integrations/autolink.test.mjs +1 -1
- package/foundation/integrations/cli-requirement.d.mts +45 -0
- package/foundation/integrations/cli-requirement.mjs +154 -0
- package/foundation/integrations/cli-requirement.test.mjs +84 -0
- package/foundation/integrations/contribution-fixes.d.mts +145 -0
- package/foundation/integrations/contribution-fixes.mjs +1284 -0
- package/foundation/integrations/contribution-inventory.d.mts +3 -2
- package/foundation/integrations/contribution-inventory.mjs +27 -24
- package/foundation/integrations/contribution-inventory.test.mjs +86 -27
- package/foundation/integrations/integration-warnings.d.mts +9 -2
- package/foundation/integrations/integration-warnings.mjs +51 -26
- package/foundation/integrations/integration-warnings.test.mjs +74 -1
- package/foundation/integrations/integrations.d.mts +3 -0
- package/foundation/integrations/integrations.mjs +11 -97
- package/foundation/integrations/provider-ledger.test.mjs +275 -0
- package/foundation/integrations/provider-resolution.d.mts +152 -0
- package/foundation/integrations/provider-resolution.mjs +576 -0
- package/foundation/integrations/provider-resolution.test.mjs +369 -0
- package/foundation/integrations/theme-descriptor.d.mts +8 -0
- package/foundation/integrations/theme-descriptor.mjs +44 -0
- package/foundation/integrations/validate-contributions.mjs +121 -29
- package/foundation/response/error-codes.d.mts +3 -1
- package/foundation/response/error-codes.d.ts +2 -0
- package/foundation/response/error-codes.doc.mjs +12 -2
- package/foundation/response/error-codes.mjs +7 -1
- package/foundation/response/error-codes.test.mjs +55 -11
- package/foundation/response/response-types.doc.d.mts +5 -1
- package/foundation/response/response-types.doc.mjs +20 -11
- package/foundation/response/response.doc.mjs +1 -1
- package/foundation/text/string-utils.d.mts +8 -0
- package/foundation/text/string-utils.mjs +40 -10
- package/foundation/xle/expand.mjs +1 -1
- package/foundation/xle/xle.test.mjs +13 -0
- package/package.json +10 -9
- package/assets/templates/themes/manifest.json +0 -95
|
@@ -12,7 +12,8 @@ import * as fs from 'node:fs';
|
|
|
12
12
|
import * as path from 'node:path';
|
|
13
13
|
import * as p from './term-log.mjs';
|
|
14
14
|
import {humanLog} from '../../foundation/response/json.mjs';
|
|
15
|
-
import {runConfigCodemod} from './run-codemod.mjs';
|
|
15
|
+
import {runCodeCodemod, runConfigCodemod} from './run-codemod.mjs';
|
|
16
|
+
import {createFileProtectionResolver} from '../../foundation/fs/file-protection.mjs';
|
|
16
17
|
|
|
17
18
|
// Known corruption patterns that indicate a broken transform.
|
|
18
19
|
// Each entry: [regex, human-readable description]
|
|
@@ -46,20 +47,30 @@ export function fixDirectiveCorruption(code) {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/**
|
|
49
|
-
* Directories a source scan must never descend into
|
|
50
|
-
*
|
|
50
|
+
* Directories a source scan must never descend into. Generated-looking names
|
|
51
|
+
* are intentionally absent: protection comes from checkout declarations, not a
|
|
52
|
+
* directory-name heuristic.
|
|
51
53
|
*/
|
|
52
54
|
const IGNORED_DIRS = new Set([
|
|
53
55
|
'node_modules',
|
|
56
|
+
'bower_components',
|
|
57
|
+
'jspm_packages',
|
|
54
58
|
'.git',
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'out',
|
|
58
|
-
'.next',
|
|
59
|
-
'coverage',
|
|
59
|
+
'.hg',
|
|
60
|
+
'.sl',
|
|
60
61
|
]);
|
|
61
62
|
export {IGNORED_DIRS};
|
|
62
63
|
|
|
64
|
+
/** @param {string} root @param {string} fullPath @param {string} name */
|
|
65
|
+
export function isIgnoredDirectory(root, fullPath, name) {
|
|
66
|
+
if (IGNORED_DIRS.has(name)) return true;
|
|
67
|
+
const parts = path.relative(root, fullPath).split(path.sep);
|
|
68
|
+
return (
|
|
69
|
+
parts[0] === '.yarn' &&
|
|
70
|
+
['__virtual__', 'cache', 'sdks', 'unplugged'].includes(parts[1])
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
63
74
|
/**
|
|
64
75
|
* Recursively find all source files in a directory.
|
|
65
76
|
* @param {string} dir
|
|
@@ -81,8 +92,17 @@ function findSourceFiles(dir) {
|
|
|
81
92
|
'.less',
|
|
82
93
|
]);
|
|
83
94
|
|
|
84
|
-
/** @param {string} currentDir */
|
|
85
|
-
function walk(currentDir) {
|
|
95
|
+
/** @param {string} currentDir @param {Set<string>} [ancestors] */
|
|
96
|
+
function walk(currentDir, ancestors = new Set()) {
|
|
97
|
+
let realDirectory;
|
|
98
|
+
try {
|
|
99
|
+
realDirectory = fs.realpathSync(currentDir);
|
|
100
|
+
} catch {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (ancestors.has(realDirectory)) return;
|
|
104
|
+
const nextAncestors = new Set(ancestors);
|
|
105
|
+
nextAncestors.add(realDirectory);
|
|
86
106
|
let entries;
|
|
87
107
|
try {
|
|
88
108
|
entries = fs.readdirSync(currentDir, {withFileTypes: true});
|
|
@@ -91,16 +111,27 @@ function findSourceFiles(dir) {
|
|
|
91
111
|
}
|
|
92
112
|
for (const entry of entries) {
|
|
93
113
|
const fullPath = path.join(currentDir, entry.name);
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
114
|
+
// Symlinked files and descendants remain read-only candidates so a
|
|
115
|
+
// required change can be reported. Real-path ancestors prevent cycles.
|
|
116
|
+
if (entry.isSymbolicLink()) {
|
|
117
|
+
try {
|
|
118
|
+
const target = fs.statSync(fullPath);
|
|
119
|
+
if (target.isFile() && extensions.has(path.extname(entry.name))) {
|
|
120
|
+
results.push(fullPath);
|
|
121
|
+
} else if (target.isDirectory()) {
|
|
122
|
+
walk(fullPath, nextAncestors);
|
|
123
|
+
}
|
|
124
|
+
} catch {
|
|
125
|
+
// Broken links have no source bytes to evaluate.
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
99
129
|
if (entry.isDirectory()) {
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
|
|
103
|
-
|
|
130
|
+
// Only hard boundaries are skipped by name. Generated and vendored
|
|
131
|
+
// content is discovered, transformed in memory, and classified by the
|
|
132
|
+
// shared protection resolver so required changes can be reported.
|
|
133
|
+
if (isIgnoredDirectory(dir, fullPath, entry.name)) continue;
|
|
134
|
+
walk(fullPath, nextAncestors);
|
|
104
135
|
} else if (extensions.has(path.extname(entry.name))) {
|
|
105
136
|
results.push(fullPath);
|
|
106
137
|
}
|
|
@@ -194,6 +225,187 @@ function toUnifiedEntry(transformEntry, version) {
|
|
|
194
225
|
};
|
|
195
226
|
}
|
|
196
227
|
|
|
228
|
+
/**
|
|
229
|
+
* What a core PROJECT codemod plans: whole files to write and delete under the
|
|
230
|
+
* project root, or the problems that stop it. Paths in `writes` and `deletes`
|
|
231
|
+
* are absolute; `problems` name package-relative files.
|
|
232
|
+
*
|
|
233
|
+
* @typedef {object} ProjectCodemodPlan
|
|
234
|
+
* @property {Array<{path: string, contents: string}>} writes
|
|
235
|
+
* @property {string[]} deletes
|
|
236
|
+
* @property {Array<{file: string, message: string}>} problems
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Run one core PROJECT codemod (`meta.codemodType === 'project'`): instead of
|
|
241
|
+
* rewriting the files it is handed, it reads the project and plans whole-file
|
|
242
|
+
* writes and deletions, which this applies (or previews) as one unit. A plan
|
|
243
|
+
* that names a problem changes nothing. Core-only: integration codemods keep
|
|
244
|
+
* the file contract.
|
|
245
|
+
*
|
|
246
|
+
* @param {{name: string, transform: unknown}} transformEntry
|
|
247
|
+
* @param {{apply: boolean, root: string, protection: {root: string, classify: (file: string) => import('../../foundation/fs/file-protection.mjs').FileProtection[]}, log: {success: (m: string) => void, warn: (m: string) => void, error: (m: string) => void}}} options
|
|
248
|
+
* @returns {Promise<import('../../authoring/codemod/type').CodemodRunResult & {stagedOverrides?: Map<string, string|null>}>}
|
|
249
|
+
*/
|
|
250
|
+
async function runProjectCodemod(
|
|
251
|
+
{name, transform},
|
|
252
|
+
{apply, root, protection, log},
|
|
253
|
+
) {
|
|
254
|
+
/** @param {string} file */
|
|
255
|
+
const rel = file => path.relative(root, file).split(path.sep).join('/');
|
|
256
|
+
/** @type {ProjectCodemodPlan} */
|
|
257
|
+
let plan;
|
|
258
|
+
try {
|
|
259
|
+
plan = await /** @type {(root: string) => Promise<ProjectCodemodPlan>} */ (
|
|
260
|
+
transform
|
|
261
|
+
)(root);
|
|
262
|
+
} catch (err) {
|
|
263
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
264
|
+
log.error(` ✗ ${message}`);
|
|
265
|
+
return {
|
|
266
|
+
filesChanged: 0,
|
|
267
|
+
changedFiles: [],
|
|
268
|
+
writtenFiles: [],
|
|
269
|
+
protectedFiles: [],
|
|
270
|
+
errors: [{file: '.', codemod: name, error: message}],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (plan.problems.length > 0) {
|
|
274
|
+
for (const {file, message} of plan.problems) {
|
|
275
|
+
log.error(` ✗ ${file} — ${message}`);
|
|
276
|
+
}
|
|
277
|
+
return {
|
|
278
|
+
filesChanged: 0,
|
|
279
|
+
changedFiles: [],
|
|
280
|
+
writtenFiles: [],
|
|
281
|
+
protectedFiles: [],
|
|
282
|
+
errors: plan.problems.map(({file, message}) => ({
|
|
283
|
+
file,
|
|
284
|
+
codemod: name,
|
|
285
|
+
error: message,
|
|
286
|
+
})),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const candidates = [
|
|
291
|
+
...plan.writes.map(write => ({
|
|
292
|
+
file: write.path,
|
|
293
|
+
existing: fs.existsSync(write.path),
|
|
294
|
+
})),
|
|
295
|
+
...plan.deletes.map(file => ({file, existing: true})),
|
|
296
|
+
];
|
|
297
|
+
/** @param {{file: string, existing: boolean}} candidate */
|
|
298
|
+
const currentDeclarations = candidate =>
|
|
299
|
+
protection
|
|
300
|
+
.classify(candidate.file)
|
|
301
|
+
.filter(
|
|
302
|
+
item =>
|
|
303
|
+
candidate.existing ||
|
|
304
|
+
['outside-root', 'dependency', 'vcs', 'symlink'].includes(
|
|
305
|
+
item.reason,
|
|
306
|
+
),
|
|
307
|
+
);
|
|
308
|
+
const currentlyBlocked = new Set(
|
|
309
|
+
candidates
|
|
310
|
+
.filter(candidate => currentDeclarations(candidate).length > 0)
|
|
311
|
+
.map(candidate => candidate.file),
|
|
312
|
+
);
|
|
313
|
+
/** @type {Map<string, string|null>} */
|
|
314
|
+
const stagedOverrides = new Map();
|
|
315
|
+
for (const write of plan.writes) {
|
|
316
|
+
if (!currentlyBlocked.has(write.path)) {
|
|
317
|
+
stagedOverrides.set(write.path, write.contents);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
for (const file of plan.deletes) {
|
|
321
|
+
if (!currentlyBlocked.has(file)) stagedOverrides.set(file, null);
|
|
322
|
+
}
|
|
323
|
+
// A plan cannot introduce a protection declaration and then overwrite the
|
|
324
|
+
// file it protects in the same transaction. Only declarations whose own
|
|
325
|
+
// writes are allowed participate in the staged view.
|
|
326
|
+
const stagedProtection = createFileProtectionResolver(root, {
|
|
327
|
+
overrides: stagedOverrides,
|
|
328
|
+
});
|
|
329
|
+
/** @type {import('../../authoring/codemod/type').CodemodRunResult['protectedFiles']} */
|
|
330
|
+
const protectedFiles = [];
|
|
331
|
+
const blocked = new Set();
|
|
332
|
+
for (const candidate of candidates) {
|
|
333
|
+
const file = candidate.file;
|
|
334
|
+
const declarations = [
|
|
335
|
+
...currentDeclarations(candidate),
|
|
336
|
+
...stagedProtection.classify(file),
|
|
337
|
+
]
|
|
338
|
+
.filter(
|
|
339
|
+
item =>
|
|
340
|
+
candidate.existing ||
|
|
341
|
+
['outside-root', 'dependency', 'vcs', 'symlink'].includes(
|
|
342
|
+
item.reason,
|
|
343
|
+
),
|
|
344
|
+
)
|
|
345
|
+
.filter(
|
|
346
|
+
(item, index, all) =>
|
|
347
|
+
all.findIndex(
|
|
348
|
+
candidate =>
|
|
349
|
+
candidate.file === item.file &&
|
|
350
|
+
candidate.reason === item.reason &&
|
|
351
|
+
candidate.declaration === item.declaration,
|
|
352
|
+
) === index,
|
|
353
|
+
);
|
|
354
|
+
if (declarations.length === 0) continue;
|
|
355
|
+
blocked.add(file);
|
|
356
|
+
const rows = declarations.map(item => ({
|
|
357
|
+
file: item.file,
|
|
358
|
+
codemod: name,
|
|
359
|
+
reason: item.reason,
|
|
360
|
+
declaration: item.declaration,
|
|
361
|
+
generated: item.reason === 'generated',
|
|
362
|
+
...(item.command ? {command: item.command} : {}),
|
|
363
|
+
}));
|
|
364
|
+
protectedFiles.push(...rows);
|
|
365
|
+
log.warn(
|
|
366
|
+
` ! ${rel(file)} — protected by ${rows.map(item => item.declaration).join('; ')}`,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const writes = plan.writes.filter(write => !blocked.has(write.path));
|
|
371
|
+
const deletes = plan.deletes.filter(file => !blocked.has(file));
|
|
372
|
+
const changedFiles = [...writes.map(write => write.path), ...deletes];
|
|
373
|
+
/** @type {string[]} */
|
|
374
|
+
const writtenFiles = [];
|
|
375
|
+
for (const write of writes) {
|
|
376
|
+
if (apply) {
|
|
377
|
+
fs.mkdirSync(path.dirname(write.path), {recursive: true});
|
|
378
|
+
fs.writeFileSync(write.path, write.contents, 'utf-8');
|
|
379
|
+
writtenFiles.push(write.path);
|
|
380
|
+
log.success(` ✓ ${rel(write.path)}`);
|
|
381
|
+
} else {
|
|
382
|
+
log.warn(` ~ ${rel(write.path)} (would write)`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Deletions go last, so a failed write leaves a state a rerun completes.
|
|
386
|
+
for (const file of deletes) {
|
|
387
|
+
if (apply) {
|
|
388
|
+
fs.rmSync(file);
|
|
389
|
+
writtenFiles.push(file);
|
|
390
|
+
log.success(` ✓ ${rel(file)} (removed)`);
|
|
391
|
+
} else {
|
|
392
|
+
log.warn(` ~ ${rel(file)} (would remove)`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/** @type {Map<string, string|null>} */
|
|
396
|
+
const appliedOverrides = new Map();
|
|
397
|
+
for (const write of writes) appliedOverrides.set(write.path, write.contents);
|
|
398
|
+
for (const file of deletes) appliedOverrides.set(file, null);
|
|
399
|
+
return {
|
|
400
|
+
filesChanged: changedFiles.length,
|
|
401
|
+
changedFiles,
|
|
402
|
+
writtenFiles,
|
|
403
|
+
protectedFiles,
|
|
404
|
+
stagedOverrides: appliedOverrides,
|
|
405
|
+
errors: [],
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
197
409
|
/**
|
|
198
410
|
* Run codemods against source files.
|
|
199
411
|
*
|
|
@@ -204,11 +416,21 @@ function toUnifiedEntry(transformEntry, version) {
|
|
|
204
416
|
* @param {string|undefined} options.codemod - Run only this specific transform
|
|
205
417
|
* @param {Set<string>} [options.skipCodemods] - Transform names to exclude
|
|
206
418
|
* @param {boolean} [options.silent] - Suppress all human-facing output (for --json)
|
|
207
|
-
* @
|
|
419
|
+
* @param {string} [options.root] - Project root a project codemod reads (default: the process cwd)
|
|
420
|
+
* @param {{root: string, classify: (file: string) => import('../../foundation/fs/file-protection.mjs').FileProtection[]}} [options.protection] - Preloaded protection resolver
|
|
421
|
+
* @returns {Promise<{totalFilesChanged: number, totalTransformsApplied: number, totalValidationBlocked: number, changedFiles: string[], writtenFiles: string[], stagedContents: Map<string, string>, protectedFiles: import('../../authoring/codemod/type').CodemodRunResult['protectedFiles'], errors: Array<{file: string, codemod: string, error: string}>, skippedOptional: Array<{name: string, meta: {title: string, description?: string, fileExtensions?: string[], codemodType?: string}, version: string}>} | {ok: false, reason: string, resolvedPath: string}>}
|
|
208
422
|
*/
|
|
209
423
|
export async function runCodemods(
|
|
210
424
|
versionManifests,
|
|
211
|
-
{
|
|
425
|
+
{
|
|
426
|
+
apply,
|
|
427
|
+
path: srcPath,
|
|
428
|
+
codemod,
|
|
429
|
+
skipCodemods,
|
|
430
|
+
silent = false,
|
|
431
|
+
root = process.cwd(),
|
|
432
|
+
protection: providedProtection,
|
|
433
|
+
},
|
|
212
434
|
) {
|
|
213
435
|
// No-op stub object so silent mode skips log output entirely without
|
|
214
436
|
// littering the body with `if (!silent)` guards.
|
|
@@ -220,12 +442,22 @@ export async function runCodemods(
|
|
|
220
442
|
};
|
|
221
443
|
|
|
222
444
|
const resolvedPath = path.resolve(srcPath);
|
|
223
|
-
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
445
|
+
// Eager construction is the fail-closed barrier: every working-tree
|
|
446
|
+
// declaration is read and parsed before the first codemod can write.
|
|
447
|
+
let protection =
|
|
448
|
+
providedProtection ?? createFileProtectionResolver(path.resolve(root));
|
|
449
|
+
/** @type {Map<string, string|null>} */
|
|
450
|
+
const protectionOverrides = new Map();
|
|
451
|
+
let protectionWriteCount = 0;
|
|
452
|
+
|
|
453
|
+
// Config and project codemods never read the files under --path, so a
|
|
454
|
+
// missing --path should not block them. Only hard-fail on a missing source
|
|
455
|
+
// path when there is at least one CODE codemod to run.
|
|
227
456
|
const hasCodeCodemod = versionManifests.some(({transforms}) =>
|
|
228
|
-
transforms.some(
|
|
457
|
+
transforms.some(
|
|
458
|
+
t =>
|
|
459
|
+
t.meta?.codemodType !== 'config' && t.meta?.codemodType !== 'project',
|
|
460
|
+
),
|
|
229
461
|
);
|
|
230
462
|
const sourcePathExists = fs.existsSync(resolvedPath);
|
|
231
463
|
|
|
@@ -259,8 +491,14 @@ export async function runCodemods(
|
|
|
259
491
|
let totalValidationBlocked = 0;
|
|
260
492
|
/** @type {Array<{file: string, codemod: string, error: string}>} */
|
|
261
493
|
const errors = [];
|
|
494
|
+
/** In-memory pipeline state keeps ordered dry-runs equivalent to apply. */
|
|
495
|
+
const virtualContents = new Map();
|
|
496
|
+
/** @type {string[]} */
|
|
497
|
+
const changedFiles = [];
|
|
262
498
|
/** @type {string[]} */
|
|
263
499
|
const writtenFiles = [];
|
|
500
|
+
/** @type {import('../../authoring/codemod/type').CodemodRunResult['protectedFiles']} */
|
|
501
|
+
const protectedFiles = [];
|
|
264
502
|
/** @type {Array<{name: string, meta: {title: string, description?: string, fileExtensions?: string[], codemodType?: string}, version: string}>} */
|
|
265
503
|
const skippedOptional = [];
|
|
266
504
|
for (const {version, transforms} of versionManifests) {
|
|
@@ -272,10 +510,7 @@ export async function runCodemods(
|
|
|
272
510
|
// Exclude explicitly skipped codemods (by transform name).
|
|
273
511
|
if (skipCodemods?.has(transformEntry.name)) continue;
|
|
274
512
|
|
|
275
|
-
const {name,
|
|
276
|
-
const transformExtensions = new Set(
|
|
277
|
-
meta.fileExtensions ?? ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.cjs'],
|
|
278
|
-
);
|
|
513
|
+
const {name, meta, optional} = transformEntry;
|
|
279
514
|
|
|
280
515
|
// Skip optional codemods unless explicitly requested via --codemod
|
|
281
516
|
if (optional && !codemod) {
|
|
@@ -283,6 +518,13 @@ export async function runCodemods(
|
|
|
283
518
|
continue;
|
|
284
519
|
}
|
|
285
520
|
|
|
521
|
+
if (apply && writtenFiles.length !== protectionWriteCount) {
|
|
522
|
+
protection = createFileProtectionResolver(path.resolve(root), {
|
|
523
|
+
overrides: protectionOverrides,
|
|
524
|
+
});
|
|
525
|
+
protectionWriteCount = writtenFiles.length;
|
|
526
|
+
}
|
|
527
|
+
|
|
286
528
|
log.info(` ${meta.title}`);
|
|
287
529
|
|
|
288
530
|
// Config codemods are routed through the SAME shared runner that
|
|
@@ -290,90 +532,78 @@ export async function runCodemods(
|
|
|
290
532
|
// `(file, api)` contract and targets the consumer's astryx.config.*.
|
|
291
533
|
// A core entry signals "config" via `meta.codemodType === 'config'`
|
|
292
534
|
// (see toUnifiedEntry).
|
|
293
|
-
if (meta?.codemodType === '
|
|
294
|
-
const result =
|
|
535
|
+
if (meta?.codemodType === 'project') {
|
|
536
|
+
const result = await runProjectCodemod(transformEntry, {
|
|
295
537
|
apply,
|
|
538
|
+
root,
|
|
539
|
+
protection,
|
|
296
540
|
log,
|
|
297
|
-
jscodeshift,
|
|
298
541
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
542
|
+
errors.push(...result.errors);
|
|
543
|
+
protectedFiles.push(...result.protectedFiles);
|
|
544
|
+
changedFiles.push(...result.changedFiles);
|
|
545
|
+
writtenFiles.push(...result.writtenFiles);
|
|
546
|
+
if (result.stagedOverrides) {
|
|
547
|
+
for (const [file, contents] of result.stagedOverrides) {
|
|
548
|
+
protectionOverrides.set(file, contents);
|
|
549
|
+
if (contents !== null) virtualContents.set(file, contents);
|
|
550
|
+
}
|
|
551
|
+
protection = createFileProtectionResolver(path.resolve(root), {
|
|
552
|
+
overrides: protectionOverrides,
|
|
553
|
+
});
|
|
554
|
+
protectionWriteCount = writtenFiles.length;
|
|
555
|
+
}
|
|
556
|
+
if (result.filesChanged > 0) {
|
|
302
557
|
totalFilesChanged += result.filesChanged;
|
|
303
|
-
totalTransformsApplied +=
|
|
304
|
-
writtenFiles.push(...result.writtenFiles);
|
|
558
|
+
totalTransformsApplied += 1;
|
|
305
559
|
}
|
|
306
560
|
continue;
|
|
307
561
|
}
|
|
308
562
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
};
|
|
329
|
-
const file = {source, path: filePath};
|
|
330
|
-
|
|
331
|
-
let result = transform(file, api);
|
|
332
|
-
|
|
333
|
-
if (result != null && result !== source) {
|
|
334
|
-
// Fix known jscodeshift output corruption before validation
|
|
335
|
-
result = fixDirectiveCorruption(result);
|
|
336
|
-
|
|
337
|
-
// Validate output before writing
|
|
338
|
-
const validation = validateOutput(result, source, j, {
|
|
339
|
-
parse: ['.tsx', '.ts', '.jsx', '.js', '.mjs', '.cjs'].includes(
|
|
340
|
-
ext,
|
|
341
|
-
),
|
|
342
|
-
});
|
|
343
|
-
if (!validation.valid) {
|
|
344
|
-
totalValidationBlocked++;
|
|
345
|
-
log.error(` ✗ ${relativePath} — ${validation.reason}`);
|
|
346
|
-
errors.push({
|
|
347
|
-
file: relativePath,
|
|
348
|
-
codemod: name,
|
|
349
|
-
error: validation.reason,
|
|
350
|
-
});
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
filesChanged++;
|
|
355
|
-
totalFilesChanged++;
|
|
356
|
-
totalTransformsApplied++;
|
|
357
|
-
|
|
358
|
-
if (apply) {
|
|
359
|
-
fs.writeFileSync(filePath, result, 'utf-8');
|
|
360
|
-
writtenFiles.push(filePath);
|
|
361
|
-
log.success(` ✓ ${relativePath}`);
|
|
362
|
-
} else {
|
|
363
|
-
log.warn(` ~ ${relativePath} (would change)`);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
} catch (err) {
|
|
367
|
-
const message = /** @type {any} */ (err).message;
|
|
368
|
-
log.error(` ✗ ${relativePath} — ${message}`);
|
|
369
|
-
errors.push({file: relativePath, codemod: name, error: message});
|
|
563
|
+
if (meta?.codemodType === 'config') {
|
|
564
|
+
const result = runConfigCodemod(
|
|
565
|
+
toUnifiedEntry(transformEntry, version),
|
|
566
|
+
{
|
|
567
|
+
apply,
|
|
568
|
+
log,
|
|
569
|
+
jscodeshift,
|
|
570
|
+
root,
|
|
571
|
+
protection,
|
|
572
|
+
contents: virtualContents,
|
|
573
|
+
},
|
|
574
|
+
);
|
|
575
|
+
errors.push(...result.errors);
|
|
576
|
+
protectedFiles.push(...result.protectedFiles);
|
|
577
|
+
changedFiles.push(...result.changedFiles);
|
|
578
|
+
writtenFiles.push(...result.writtenFiles);
|
|
579
|
+
if (result.filesChanged > 0) {
|
|
580
|
+
totalFilesChanged += result.filesChanged;
|
|
581
|
+
totalTransformsApplied += result.filesChanged;
|
|
370
582
|
}
|
|
583
|
+
continue;
|
|
371
584
|
}
|
|
372
585
|
|
|
373
|
-
|
|
586
|
+
const result = runCodeCodemod(
|
|
587
|
+
toUnifiedEntry(transformEntry, version),
|
|
588
|
+
files,
|
|
589
|
+
{apply, log, jscodeshift, root, protection, contents: virtualContents},
|
|
590
|
+
);
|
|
591
|
+
errors.push(...result.errors);
|
|
592
|
+
protectedFiles.push(...result.protectedFiles);
|
|
593
|
+
changedFiles.push(...result.changedFiles);
|
|
594
|
+
writtenFiles.push(...result.writtenFiles);
|
|
595
|
+
totalFilesChanged += result.filesChanged;
|
|
596
|
+
totalTransformsApplied += result.filesChanged;
|
|
597
|
+
totalValidationBlocked += result.errors.filter(
|
|
598
|
+
error =>
|
|
599
|
+
error.error.startsWith('transform produced unparseable output') ||
|
|
600
|
+
error.error.startsWith('detected corruption:'),
|
|
601
|
+
).length;
|
|
602
|
+
|
|
603
|
+
if (result.filesChanged > 0) {
|
|
374
604
|
const verb = apply ? 'Updated' : 'Would update';
|
|
375
605
|
log.info(
|
|
376
|
-
` ${verb} ${filesChanged} file${filesChanged === 1 ? '' : 's'}`,
|
|
606
|
+
` ${verb} ${result.filesChanged} file${result.filesChanged === 1 ? '' : 's'}`,
|
|
377
607
|
);
|
|
378
608
|
}
|
|
379
609
|
}
|
|
@@ -400,8 +630,23 @@ export async function runCodemods(
|
|
|
400
630
|
);
|
|
401
631
|
}
|
|
402
632
|
|
|
403
|
-
if (
|
|
633
|
+
if (protectedFiles.length > 0) {
|
|
634
|
+
const files = [...new Set(protectedFiles.map(item => item.file))];
|
|
635
|
+
log.warn(
|
|
636
|
+
`${files.length} protected file${files.length === 1 ? '' : 's'} require${files.length === 1 ? 's' : ''} regeneration or manual review.`,
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (
|
|
641
|
+
totalFilesChanged === 0 &&
|
|
642
|
+
errors.length === 0 &&
|
|
643
|
+
protectedFiles.length === 0
|
|
644
|
+
) {
|
|
404
645
|
log.success('No changes needed — your code is already up to date!');
|
|
646
|
+
} else if (apply && protectedFiles.length > 0) {
|
|
647
|
+
log.warn(
|
|
648
|
+
`Applied ${totalTransformsApplied} owned change${totalTransformsApplied === 1 ? '' : 's'} across ${totalFilesChanged} file${totalFilesChanged === 1 ? '' : 's'}; protected changes remain.`,
|
|
649
|
+
);
|
|
405
650
|
} else if (apply) {
|
|
406
651
|
log.success(
|
|
407
652
|
`Done! Applied ${totalTransformsApplied} change${totalTransformsApplied === 1 ? '' : 's'} across ${totalFilesChanged} file${totalFilesChanged === 1 ? '' : 's'}.`,
|
|
@@ -438,7 +683,10 @@ export async function runCodemods(
|
|
|
438
683
|
totalFilesChanged,
|
|
439
684
|
totalTransformsApplied,
|
|
440
685
|
totalValidationBlocked,
|
|
686
|
+
changedFiles,
|
|
441
687
|
writtenFiles,
|
|
688
|
+
protectedFiles,
|
|
689
|
+
stagedContents: virtualContents,
|
|
442
690
|
errors,
|
|
443
691
|
skippedOptional,
|
|
444
692
|
};
|
package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, expect, it} from 'vitest';
|
|
4
|
+
import transform from '../migrate-native-picker-to-presentation.mjs';
|
|
5
|
+
|
|
6
|
+
async function applyTransform(source) {
|
|
7
|
+
const jscodeshift = (await import('jscodeshift')).default;
|
|
8
|
+
const j = jscodeshift.withParser('tsx');
|
|
9
|
+
const api = {jscodeshift: j, stats: () => {}, report: () => {}};
|
|
10
|
+
return transform({source, path: 'test.tsx'}, api) ?? source;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('migrate-native-picker-to-presentation', () => {
|
|
14
|
+
it('maps DateInput and DateTimeInput values (FR3)', async () => {
|
|
15
|
+
const output =
|
|
16
|
+
await applyTransform(`import {DateInput, DateTimeInput} from '@astryxdesign/core';
|
|
17
|
+
const a = <DateInput label="d" nativePicker="touch" />;
|
|
18
|
+
const b = <DateInput label="d" nativePicker="always" />;
|
|
19
|
+
const c = <DateTimeInput label="dt" nativePicker="never" />;`);
|
|
20
|
+
|
|
21
|
+
expect(output).toContain("presentation='adaptive-native'");
|
|
22
|
+
expect(output).toContain("presentation='native'");
|
|
23
|
+
expect(output).toContain("presentation='adaptive-bottom-sheet'");
|
|
24
|
+
expect(output).not.toContain('nativePicker');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('maps TimeInput never to text-input (FR3)', async () => {
|
|
28
|
+
const output =
|
|
29
|
+
await applyTransform(`import {TimeInput} from '@astryxdesign/core';
|
|
30
|
+
const a = <TimeInput label="t" nativePicker="never" />;
|
|
31
|
+
const b = <TimeInput label="t" nativePicker={'touch'} />;`);
|
|
32
|
+
|
|
33
|
+
expect(output).toContain("presentation='text-input'");
|
|
34
|
+
expect(output).toContain("presentation={'adaptive-native'}");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('drops nativePicker when presentation is already set (FR4)', async () => {
|
|
38
|
+
const output =
|
|
39
|
+
await applyTransform(`import {DateInput} from '@astryxdesign/core';
|
|
40
|
+
const a = <DateInput label="d" presentation="popover" nativePicker="always" />;`);
|
|
41
|
+
|
|
42
|
+
expect(output).toContain('presentation="popover"');
|
|
43
|
+
expect(output).not.toContain('nativePicker');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('leaves dynamic values and unrelated components unchanged', async () => {
|
|
47
|
+
const source = `import {DateInput, Selector} from '@astryxdesign/core';
|
|
48
|
+
const a = <DateInput label="d" nativePicker={mode} />;
|
|
49
|
+
const b = <Selector nativePicker="never" />;`;
|
|
50
|
+
expect(await applyTransform(source)).toBe(source);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('leaves a mixed static/dynamic conditional byte-identical when a sibling migrates', async () => {
|
|
54
|
+
const output =
|
|
55
|
+
await applyTransform(`import {TimeInput} from '@astryxdesign/core';
|
|
56
|
+
const a = <TimeInput label="t" nativePicker="touch" />;
|
|
57
|
+
const b = <TimeInput label="t" nativePicker={flag ? 'always' : mode} />;`);
|
|
58
|
+
|
|
59
|
+
expect(output).toContain("presentation='adaptive-native'");
|
|
60
|
+
expect(output).toContain("nativePicker={flag ? 'always' : mode}");
|
|
61
|
+
expect(output).not.toContain("'native'");
|
|
62
|
+
});
|
|
63
|
+
});
|