@astryxdesign/cli 0.1.2 → 0.1.3
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 +64 -1
- package/README.md +49 -55
- package/docs/layout.doc.dense.mjs +90 -0
- package/docs/layout.doc.mjs +160 -0
- package/docs/principles.doc.dense.mjs +2 -2
- package/docs/principles.doc.mjs +4 -0
- package/docs/principles.doc.zh.mjs +2 -2
- package/docs/theme.doc.mjs +2 -2
- package/package.json +13 -1
- package/src/api/blog.mjs +192 -0
- package/src/api/blog.test.mjs +144 -0
- package/src/api/component.mjs +253 -12
- package/src/api/discover.mjs +14 -7
- package/src/api/doctor.mjs +10 -25
- package/src/api/index.mjs +1 -0
- package/src/api/layout.mjs +11 -10
- package/src/api/layout.test.mjs +4 -1
- package/src/api/template-integration.test.mjs +225 -0
- package/src/api/template.mjs +242 -84
- package/src/api/validate-integration.mjs +370 -0
- package/src/api/validate-integration.test.mjs +222 -0
- package/src/codemod.mjs +93 -0
- package/src/codemod.test.mjs +134 -0
- package/src/codemods/__tests__/registry.test.mjs +1 -0
- package/src/codemods/__tests__/runner.test.mjs +103 -0
- package/src/codemods/integration-discovery.mjs +168 -0
- package/src/codemods/integration-discovery.test.mjs +234 -0
- package/src/codemods/integration-runner.mjs +109 -0
- package/src/codemods/registry.mjs +1 -0
- package/src/codemods/run-codemod.mjs +207 -0
- package/src/codemods/runner.mjs +73 -96
- package/src/codemods/transforms/v0.0.15/index.mjs +0 -13
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-css-surfaces.test.mjs +67 -0
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-declare-module.test.mjs +61 -0
- package/src/codemods/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +104 -0
- package/src/codemods/transforms/{v0.0.15 → v0.1.0}/drop-xds-prefix-imports.mjs +15 -4
- package/src/codemods/transforms/v0.1.0/index.mjs +43 -6
- package/src/codemods/transforms/v0.1.0/migrate-xds-css-surfaces.mjs +77 -0
- package/src/codemods/transforms/v0.1.0/migrate-xds-declare-module.mjs +78 -0
- package/src/codemods/transforms/v0.1.3/__tests__/migrate-layout-components-to-experimental.test.mjs +360 -0
- package/src/codemods/transforms/v0.1.3/index.mjs +19 -0
- package/src/codemods/transforms/v0.1.3/migrate-layout-components-to-experimental.mjs +260 -0
- package/src/commands/agent-docs.mjs +4 -1
- package/src/commands/blog.mjs +60 -0
- package/src/commands/build-theme.import-path.test.mjs +2 -12
- package/src/commands/build-theme.mjs +117 -0
- package/src/commands/build-theme.prose.test.mjs +5 -15
- package/src/commands/build-theme.watch.test.mjs +149 -0
- package/src/commands/component/index.mjs +44 -10
- package/src/commands/component-ownership.test.mjs +227 -0
- package/src/commands/discover.mjs +5 -16
- package/src/commands/doctor.test.mjs +3 -3
- package/src/commands/ensure-core-built.mjs +120 -0
- package/src/commands/json-contract.test.mjs +0 -32
- package/src/commands/swizzle.mjs +224 -235
- package/src/commands/swizzle.path-safety.test.mjs +3 -3
- package/src/commands/swizzle.routing.test.mjs +279 -0
- package/src/commands/template.mjs +33 -37
- package/src/commands/upgrade.config-ordering.test.mjs +273 -0
- package/src/commands/upgrade.integration-policy.test.mjs +204 -0
- package/src/commands/upgrade.mjs +349 -170
- package/src/commands/validate-integration.mjs +110 -0
- package/src/commands/validate-integration.test.mjs +124 -0
- package/src/config.mjs +7 -20
- package/src/config.test.mjs +89 -13
- package/src/index.mjs +8 -3
- package/src/integration.mjs +19 -0
- package/src/lib/component-discovery.mjs +177 -0
- package/src/lib/config-cache.mjs +91 -0
- package/src/lib/config-cache.test.mjs +83 -0
- package/src/lib/config-schema.mjs +41 -55
- package/src/lib/error-codes.mjs +15 -12
- package/src/lib/integration-warnings.mjs +62 -0
- package/src/lib/integration-warnings.test.mjs +102 -0
- package/src/lib/integrations.mjs +92 -115
- package/src/lib/integrations.test.mjs +88 -107
- package/src/lib/manifest.mjs +5 -2
- package/src/lib/module-loader.mjs +80 -0
- package/src/lib/module-loader.test.mjs +106 -0
- package/src/lib/project.mjs +502 -0
- package/src/lib/project.test.mjs +308 -0
- package/src/lib/site.mjs +20 -0
- package/src/template.mjs +73 -0
- package/src/template.test.mjs +127 -0
- package/src/types/api.d.ts +2 -6
- package/src/types/base.d.ts +3 -9
- package/src/types/codemod.d.ts +81 -0
- package/src/types/component.d.ts +27 -2
- package/src/types/config.d.ts +56 -85
- package/src/types/error-codes.d.ts +5 -3
- package/src/types/index.d.ts +0 -1
- package/src/types/integration.d.ts +29 -0
- package/src/types/swizzle.d.ts +9 -2
- package/src/types/template-api.d.ts +54 -0
- package/src/types/template.d.ts +10 -7
- package/src/types/upgrade.d.ts +29 -0
- package/src/types/validate-integration.d.ts +24 -0
- package/src/utils/github.mjs +0 -237
- package/src/utils/interactive.mjs +2 -2
- package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotVariants.doc.mjs +14 -0
- package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotVariants.tsx +28 -0
- package/templates/blocks/components/Blockquote/BlockquoteTestimonials.doc.mjs +14 -0
- package/templates/blocks/components/Blockquote/BlockquoteTestimonials.tsx +34 -0
- package/templates/blocks/components/Blockquote/BlockquoteWithCite.doc.mjs +14 -0
- package/templates/blocks/components/Blockquote/BlockquoteWithCite.tsx +21 -0
- package/templates/blocks/components/BreadcrumbItem/BreadcrumbItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/BreadcrumbItem/BreadcrumbItemBasic.tsx +15 -0
- package/templates/blocks/components/ButtonGroup/ButtonGroupBasic.doc.mjs +14 -0
- package/templates/blocks/components/ButtonGroup/ButtonGroupBasic.tsx +16 -0
- package/templates/blocks/components/ChatDictationButton/ChatDictationButtonBasic.doc.mjs +19 -0
- package/templates/blocks/components/ChatDictationButton/ChatDictationButtonBasic.tsx +34 -0
- package/templates/blocks/components/CheckboxListItem/CheckboxListItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/CheckboxListItem/CheckboxListItemBasic.tsx +30 -0
- package/templates/blocks/components/CollapsibleGroup/CollapsibleGroupAccordion.doc.mjs +14 -0
- package/templates/blocks/components/CollapsibleGroup/CollapsibleGroupAccordion.tsx +31 -0
- package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyBasic.doc.mjs +14 -0
- package/templates/blocks/components/CommandPaletteEmpty/CommandPaletteEmptyBasic.tsx +26 -0
- package/templates/blocks/components/CommandPaletteFooter/CommandPaletteFooterBasic.doc.mjs +14 -0
- package/templates/blocks/components/CommandPaletteFooter/CommandPaletteFooterBasic.tsx +32 -0
- package/templates/blocks/components/CommandPaletteGroup/CommandPaletteGroupBasic.doc.mjs +18 -0
- package/templates/blocks/components/CommandPaletteGroup/CommandPaletteGroupBasic.tsx +32 -0
- package/templates/blocks/components/CommandPaletteItem/CommandPaletteItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/CommandPaletteItem/CommandPaletteItemBasic.tsx +27 -0
- package/templates/blocks/components/ContextMenu/ContextMenuBasic.doc.mjs +14 -0
- package/templates/blocks/components/ContextMenu/ContextMenuBasic.tsx +32 -0
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.doc.mjs +14 -0
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithPresets.tsx +46 -0
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.doc.mjs +14 -0
- package/templates/blocks/components/DateRangeInput/DateRangeInputWithValidation.tsx +52 -0
- package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.doc.mjs +14 -0
- package/templates/blocks/components/DateTimeInput/DateTimeInputWithValidation.tsx +43 -0
- package/templates/blocks/components/DialogHeader/DialogHeaderBasic.doc.mjs +14 -0
- package/templates/blocks/components/DialogHeader/DialogHeaderBasic.tsx +30 -0
- package/templates/blocks/components/DropdownMenu/DropdownMenuShowcase.tsx +0 -6
- package/templates/blocks/components/DropdownMenuItem/DropdownMenuItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/DropdownMenuItem/DropdownMenuItemBasic.tsx +27 -0
- package/templates/blocks/components/DropdownMenuItem/DropdownMenuItemShowcase.tsx +1 -5
- package/templates/blocks/components/FieldLabel/FieldLabelBasic.doc.mjs +14 -0
- package/templates/blocks/components/FieldLabel/FieldLabelBasic.tsx +20 -0
- package/templates/blocks/components/FieldStatus/FieldStatusBasic.doc.mjs +14 -0
- package/templates/blocks/components/FieldStatus/FieldStatusBasic.tsx +23 -0
- package/templates/blocks/components/FileInput/FileInputBasic.doc.mjs +14 -0
- package/templates/blocks/components/FileInput/FileInputBasic.tsx +22 -0
- package/templates/blocks/components/GridSpan/GridSpanColumns.doc.mjs +14 -0
- package/templates/blocks/components/GridSpan/GridSpanColumns.tsx +38 -0
- package/templates/blocks/components/HStack/HStackBasic.doc.mjs +14 -0
- package/templates/blocks/components/HStack/HStackBasic.tsx +16 -0
- package/templates/blocks/components/Hooks/useKeyboardHintHookUsage.doc.mjs +14 -0
- package/templates/blocks/components/Hooks/useKeyboardHintHookUsage.tsx +57 -0
- package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.doc.mjs +1 -1
- package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.tsx +9 -4
- package/templates/blocks/components/InputGroup/InputGroupBasic.doc.mjs +14 -0
- package/templates/blocks/components/InputGroup/InputGroupBasic.tsx +27 -0
- package/templates/blocks/components/LayoutContent/LayoutContentBasic.doc.mjs +23 -0
- package/templates/blocks/components/LayoutContent/LayoutContentBasic.tsx +40 -0
- package/templates/blocks/components/LayoutFooter/LayoutFooterActions.doc.mjs +22 -0
- package/templates/blocks/components/LayoutFooter/LayoutFooterActions.tsx +41 -0
- package/templates/blocks/components/LayoutHeader/LayoutHeaderWithActions.doc.mjs +23 -0
- package/templates/blocks/components/LayoutHeader/LayoutHeaderWithActions.tsx +40 -0
- package/templates/blocks/components/LayoutPanel/LayoutPanelNavigation.doc.mjs +22 -0
- package/templates/blocks/components/LayoutPanel/LayoutPanelNavigation.tsx +37 -0
- package/templates/blocks/components/Lightbox/LightboxGallery.doc.mjs +14 -0
- package/templates/blocks/components/Lightbox/LightboxGallery.tsx +53 -0
- package/templates/blocks/components/Lightbox/LightboxShowcase.tsx +4 -3
- package/templates/blocks/components/Lightbox/LightboxVideo.doc.mjs +14 -0
- package/templates/blocks/components/Lightbox/LightboxVideo.tsx +27 -0
- package/templates/blocks/components/Lightbox/LightboxZoom.doc.mjs +14 -0
- package/templates/blocks/components/Lightbox/LightboxZoom.tsx +33 -0
- package/templates/blocks/components/LinkProvider/LinkProviderCustomLink.doc.mjs +1 -1
- package/templates/blocks/components/MetadataListItem/MetadataListItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/MetadataListItem/MetadataListItemBasic.tsx +19 -0
- package/templates/blocks/components/MobileNavToggle/MobileNavToggleBasic.doc.mjs +23 -0
- package/templates/blocks/components/MobileNavToggle/MobileNavToggleBasic.tsx +42 -0
- package/templates/blocks/components/MoreMenu/MoreMenuShowcase.tsx +0 -6
- package/templates/blocks/components/NavIcon/NavIconBasic.doc.mjs +14 -0
- package/templates/blocks/components/NavIcon/NavIconBasic.tsx +16 -0
- package/templates/blocks/components/RadioListItem/RadioListItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/RadioListItem/RadioListItemBasic.tsx +30 -0
- package/templates/blocks/components/Resizable/ResizableSidebar.doc.mjs +25 -0
- package/templates/blocks/components/Resizable/ResizableSidebar.tsx +67 -0
- package/templates/blocks/components/SegmentedControlItem/SegmentedControlItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/SegmentedControlItem/SegmentedControlItemBasic.tsx +21 -0
- package/templates/blocks/components/SelectorOption/SelectorOptionBasic.doc.mjs +14 -0
- package/templates/blocks/components/SelectorOption/SelectorOptionBasic.tsx +39 -0
- package/templates/blocks/components/SideNavCollapseButton/SideNavCollapseButtonBasic.doc.mjs +14 -0
- package/templates/blocks/components/SideNavCollapseButton/SideNavCollapseButtonBasic.tsx +55 -0
- package/templates/blocks/components/SideNavHeading/SideNavHeadingBasic.doc.mjs +14 -0
- package/templates/blocks/components/SideNavHeading/SideNavHeadingBasic.tsx +38 -0
- package/templates/blocks/components/SideNavItem/SideNavItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/SideNavItem/SideNavItemBasic.tsx +49 -0
- package/templates/blocks/components/SideNavSection/SideNavSectionBasic.doc.mjs +14 -0
- package/templates/blocks/components/SideNavSection/SideNavSectionBasic.tsx +71 -0
- package/templates/blocks/components/StackItem/StackItemFill.doc.mjs +14 -0
- package/templates/blocks/components/StackItem/StackItemFill.tsx +28 -0
- package/templates/blocks/components/Tab/TabWithSelectedIcon.doc.mjs +13 -0
- package/templates/blocks/components/Tab/TabWithSelectedIcon.tsx +39 -0
- package/templates/blocks/components/TabMenu/TabMenuBasic.doc.mjs +14 -0
- package/templates/blocks/components/TabMenu/TabMenuBasic.tsx +23 -0
- package/templates/blocks/components/Table/StickyColumnsHookUsage.doc.mjs +1 -1
- package/templates/blocks/components/ToggleButtonGroup/ToggleButtonGroupVertical.doc.mjs +14 -0
- package/templates/blocks/components/ToggleButtonGroup/ToggleButtonGroupVertical.tsx +47 -0
- package/templates/blocks/components/TopNavHeading/TopNavHeadingBasic.doc.mjs +14 -0
- package/templates/blocks/components/TopNavHeading/TopNavHeadingBasic.tsx +22 -0
- package/templates/blocks/components/TopNavItem/TopNavItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/TopNavItem/TopNavItemBasic.tsx +21 -0
- package/templates/blocks/components/TopNavMegaMenu/TopNavMegaMenuBasic.doc.mjs +20 -0
- package/templates/blocks/components/TopNavMegaMenu/TopNavMegaMenuBasic.tsx +46 -0
- package/templates/blocks/components/TopNavMegaMenuFeaturedCard/TopNavMegaMenuFeaturedCardBasic.doc.mjs +14 -0
- package/templates/blocks/components/TopNavMegaMenuFeaturedCard/TopNavMegaMenuFeaturedCardBasic.tsx +16 -0
- package/templates/blocks/components/TopNavMegaMenuItem/TopNavMegaMenuItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/TopNavMegaMenuItem/TopNavMegaMenuItemBasic.tsx +26 -0
- package/templates/blocks/components/TopNavMenu/TopNavMenuBasic.doc.mjs +14 -0
- package/templates/blocks/components/TopNavMenu/TopNavMenuBasic.tsx +43 -0
- package/templates/blocks/components/TypeaheadItem/TypeaheadItemBasic.doc.mjs +14 -0
- package/templates/blocks/components/TypeaheadItem/TypeaheadItemBasic.tsx +43 -0
- package/templates/blocks/components/VStack/VStackBasic.doc.mjs +14 -0
- package/templates/blocks/components/VStack/VStackBasic.tsx +20 -0
- package/templates/pages/kanban-board/page.tsx +729 -0
- package/templates/pages/kanban-board/template.doc.mjs +12 -0
- package/templates/pages/shell-nav/page.tsx +321 -0
- package/templates/pages/shell-nav/template.doc.mjs +12 -0
- package/templates/pages/shell-side-nav/page.tsx +241 -0
- package/templates/pages/shell-side-nav/template.doc.mjs +12 -0
- package/templates/pages/shell-top-nav/page.tsx +224 -0
- package/templates/pages/shell-top-nav/template.doc.mjs +12 -0
- package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-config-surfaces.test.mjs +0 -116
- package/src/codemods/transforms/v0.1.0/migrate-xds-config-surfaces.mjs +0 -230
- package/src/commands/gap-report.mjs +0 -464
- package/src/commands/gap-report.test.mjs +0 -168
- package/src/commands/swizzle-gap-safety.test.mjs +0 -273
- package/src/lib/config.mjs +0 -113
- package/src/lib/config.test.mjs +0 -91
- package/src/types/gap-report.d.ts +0 -29
- package/templates/blocks/components/TreeListBranches/TreeListBranchesShowcase.doc.mjs +0 -14
- package/templates/blocks/components/TreeListBranches/TreeListBranchesShowcase.tsx +0 -64
- package/templates/blocks/components/TreeListItem/TreeListItemShowcase.doc.mjs +0 -14
- package/templates/blocks/components/TreeListItem/TreeListItemShowcase.tsx +0 -60
- /package/src/codemods/transforms/{v0.0.15 → v0.1.0}/__tests__/drop-xds-prefix-imports.test.mjs +0 -0
package/src/commands/upgrade.mjs
CHANGED
|
@@ -15,13 +15,22 @@
|
|
|
15
15
|
* 3. Refresh agent docs (AGENTS.md / CLAUDE.md) if present
|
|
16
16
|
*
|
|
17
17
|
* Options:
|
|
18
|
-
* --from <version>
|
|
19
|
-
* --apply
|
|
20
|
-
* --force
|
|
21
|
-
* --codemod <name>
|
|
22
|
-
* --
|
|
23
|
-
*
|
|
24
|
-
* --
|
|
18
|
+
* --from <version> Previous version before the dependency upgrade
|
|
19
|
+
* --apply Write changes to disk (default: dry-run)
|
|
20
|
+
* --force Run codemods even when from >= installed version
|
|
21
|
+
* --codemod <name> Run a specific transform only
|
|
22
|
+
* --skip-codemod <name…> Exclude named codemods (variadic). Use this to
|
|
23
|
+
* re-run past a codemod that failed at execution time.
|
|
24
|
+
* --integration <spec> Load an explicit integration package or file
|
|
25
|
+
* --path <dir> Source directory (default: ./src)
|
|
26
|
+
* --install-deps Auto-install jscodeshift without prompting (for CI/LLM)
|
|
27
|
+
*
|
|
28
|
+
* Integration error policy:
|
|
29
|
+
* - A DISCOVERY/definition error for an integration (bad manifest/export,
|
|
30
|
+
* duplicate ids, missing root) SKIPS that integration's codemods and warns
|
|
31
|
+
* (via the integration-issue nudge) — it does NOT hard-fail the upgrade.
|
|
32
|
+
* - An EXECUTION-time failure (a transform THROWS while rewriting files)
|
|
33
|
+
* ABORTS the upgrade (nonzero exit) so a partial write never proceeds.
|
|
25
34
|
*/
|
|
26
35
|
|
|
27
36
|
import * as fs from 'node:fs';
|
|
@@ -32,12 +41,18 @@ import * as p from '@clack/prompts';
|
|
|
32
41
|
import {ensureJscodeshift} from '../codemods/ensure-jscodeshift.mjs';
|
|
33
42
|
import {getTransformsBetween, latestVersion} from '../codemods/registry.mjs';
|
|
34
43
|
import {runCodemods} from '../codemods/runner.mjs';
|
|
44
|
+
import {
|
|
45
|
+
discoverIntegrationCodemods,
|
|
46
|
+
selectIntegrationCodemods,
|
|
47
|
+
} from '../codemods/integration-discovery.mjs';
|
|
48
|
+
import {runIntegrationCodemods} from '../codemods/integration-runner.mjs';
|
|
35
49
|
import {installAgentDocs, discoverAgentDocs} from './agent-docs.mjs';
|
|
36
50
|
import {getRunPrefix} from '../utils/package-manager.mjs';
|
|
37
|
-
import {isValidSemver, semverGte
|
|
51
|
+
import {isValidSemver, semverGte} from '../utils/semver.mjs';
|
|
38
52
|
import {jsonOut, jsonError} from '../lib/json.mjs';
|
|
39
|
-
import {
|
|
53
|
+
import {Project} from '../lib/project.mjs';
|
|
40
54
|
import {loadIntegrations} from '../lib/integrations.mjs';
|
|
55
|
+
import {warnOnIntegrationIssues} from '../lib/integration-warnings.mjs';
|
|
41
56
|
import {ERROR_CODES} from '../lib/error-codes.mjs';
|
|
42
57
|
|
|
43
58
|
const execFileAsync = promisify(execFile);
|
|
@@ -64,90 +79,59 @@ function detectInstalledTargetVersion() {
|
|
|
64
79
|
return null;
|
|
65
80
|
}
|
|
66
81
|
|
|
67
|
-
function normalizeIntegrationTransforms(integration, from, to) {
|
|
68
|
-
const transforms = [];
|
|
69
|
-
for (const entry of integration.codemods ?? []) {
|
|
70
|
-
const entryFrom = entry.from ?? '0.0.0';
|
|
71
|
-
const entryTo = entry.to ?? to;
|
|
72
|
-
if (semverGte(from, entryTo) || semverGt(entryFrom, to)) continue;
|
|
73
|
-
if (!entry.name)
|
|
74
|
-
throw new Error(
|
|
75
|
-
`Integration ${integration.name ?? integration.__spec} has a codemod without a name.`,
|
|
76
|
-
);
|
|
77
|
-
if (!entry.transform)
|
|
78
|
-
throw new Error(
|
|
79
|
-
`Integration codemod ${entry.name} is missing transform.`,
|
|
80
|
-
);
|
|
81
|
-
const directTransform =
|
|
82
|
-
typeof entry.transform === 'function' ? entry.transform : null;
|
|
83
|
-
if (!directTransform)
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Integration codemod ${entry.name} did not resolve to a function.`,
|
|
86
|
-
);
|
|
87
|
-
transforms.push({
|
|
88
|
-
name: entry.name,
|
|
89
|
-
meta: {
|
|
90
|
-
title:
|
|
91
|
-
entry.title ??
|
|
92
|
-
`${integration.name ?? integration.__spec}: ${entry.name}`,
|
|
93
|
-
description: entry.description ?? '',
|
|
94
|
-
pr: entry.pr,
|
|
95
|
-
fileExtensions: entry.fileExtensions,
|
|
96
|
-
},
|
|
97
|
-
optional: !!entry.optional,
|
|
98
|
-
transform: directTransform,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
return transforms.length ? [{version: to, transforms}] : [];
|
|
102
|
-
}
|
|
103
|
-
|
|
104
82
|
function uniqueFiles(files) {
|
|
105
83
|
return [...new Set((files ?? []).filter(Boolean))];
|
|
106
84
|
}
|
|
107
85
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Run the app config's post-codemod hooks (config.hooks.postCodemod).
|
|
88
|
+
*
|
|
89
|
+
* Each hook's `buildCommand({packageDir, files})` returns a command to run
|
|
90
|
+
* (or a nullish value to skip). In dry-run mode we only PREVIEW — buildCommand
|
|
91
|
+
* is called (so a throw still fails the run) but the command is never executed.
|
|
92
|
+
* In apply mode the commands run in order via execFile; a nonzero exit (or a
|
|
93
|
+
* buildCommand throw) fails the upgrade.
|
|
94
|
+
*
|
|
95
|
+
* @param {import('../types/config').PostCodemodHook[]} hooks
|
|
96
|
+
* @param {{packageDir: string, files: string[], apply: boolean}} context
|
|
97
|
+
* @param {boolean} silent
|
|
98
|
+
*/
|
|
99
|
+
async function runPostCodemodHooks(hooks, context, silent) {
|
|
100
|
+
if (!hooks || hooks.length === 0) return;
|
|
113
101
|
|
|
114
102
|
const log = silent ? {info() {}, warn() {}, success() {}, error() {}} : p.log;
|
|
103
|
+
const {packageDir, files, apply} = context;
|
|
115
104
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
105
|
+
for (let i = 0; i < hooks.length; i++) {
|
|
106
|
+
const hook = hooks[i];
|
|
107
|
+
const label = hook.name ?? `postCodemod[${i}]`;
|
|
108
|
+
if (typeof hook.buildCommand !== 'function') {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`Post-codemod hook ${label} is missing a buildCommand function.`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const cmd = await hook.buildCommand({packageDir, files});
|
|
115
|
+
if (!cmd) {
|
|
116
|
+
log.info(`Post-codemod hook ${label} produced no command; skipping.`);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!apply) {
|
|
121
|
+
const preview = [cmd.command, ...(cmd.args ?? [])].join(' ');
|
|
122
|
+
log.info(`Post-codemod hook ${label} (dry run): ${preview}`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
await execFileAsync(cmd.command, cmd.args ?? [], {
|
|
127
|
+
cwd: cmd.options?.cwd ?? packageDir,
|
|
128
|
+
timeout: cmd.options?.timeout ?? 300_000,
|
|
120
129
|
stdio: 'pipe',
|
|
121
130
|
encoding: 'utf-8',
|
|
122
|
-
|
|
131
|
+
...cmd.options,
|
|
132
|
+
env: {...process.env, ...(cmd.options?.env ?? {})},
|
|
123
133
|
});
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const ctx = {...context, run};
|
|
127
|
-
for (const {integration, hook} of hooks) {
|
|
128
|
-
const label = `${integration.name ?? integration.__spec}:${hook.name ?? 'postCodemod'}`;
|
|
129
|
-
try {
|
|
130
|
-
if (typeof hook.run === 'function') {
|
|
131
|
-
await hook.run(ctx);
|
|
132
|
-
} else if (typeof hook.command === 'function') {
|
|
133
|
-
const cmd = await hook.command(ctx);
|
|
134
|
-
if (cmd) {
|
|
135
|
-
await run(cmd.command, cmd.args ?? [], {
|
|
136
|
-
cwd: cmd.cwd,
|
|
137
|
-
timeoutMs: cmd.timeoutMs,
|
|
138
|
-
env: cmd.env,
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
log.warn(
|
|
143
|
-
`Integration hook ${label} has no run() or command() function; skipping.`,
|
|
144
|
-
);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
log.success(`Post-codemod hook ${label} completed.`);
|
|
148
|
-
} catch (err) {
|
|
149
|
-
log.warn(`Post-codemod hook ${label} failed: ${err.message}`);
|
|
150
|
-
}
|
|
134
|
+
log.success(`Post-codemod hook ${label} completed.`);
|
|
151
135
|
}
|
|
152
136
|
}
|
|
153
137
|
|
|
@@ -169,6 +153,10 @@ export function registerUpgrade(program) {
|
|
|
169
153
|
false,
|
|
170
154
|
)
|
|
171
155
|
.option('--codemod <name>', 'Run a specific transform only')
|
|
156
|
+
.option(
|
|
157
|
+
'--skip-codemod <name...>',
|
|
158
|
+
'Exclude named codemods (repeatable). Re-run past a failed codemod by skipping it.',
|
|
159
|
+
)
|
|
172
160
|
.option(
|
|
173
161
|
'--integration <package-or-file>',
|
|
174
162
|
'Explicit integration package name or integration file path (repeatable)',
|
|
@@ -266,15 +254,170 @@ export function registerUpgrade(program) {
|
|
|
266
254
|
);
|
|
267
255
|
}
|
|
268
256
|
|
|
257
|
+
// ───────────────────────────────────────────────────────────────────
|
|
258
|
+
// PIPELINE ORDERING
|
|
259
|
+
//
|
|
260
|
+
// CORE codemods run BEFORE the consumer's config is loaded. `Project.load`
|
|
261
|
+
// STRICT-validates astryx.config.* and THROWS on unknown keys — but a core
|
|
262
|
+
// CONFIG codemod (e.g. v0.1.3 migrate-layout-components, signalled by
|
|
263
|
+
// `meta.codemodType === 'config'`) is precisely what repairs an otherwise
|
|
264
|
+
// -invalid config. Loading first created a chicken-and-egg: the config was
|
|
265
|
+
// rejected before the codemod that would fix it ever ran. Core codemods
|
|
266
|
+
// read files directly (config codemods via runConfigCodemod read
|
|
267
|
+
// astryx.config.*; code codemods scan --path), so they do NOT need the
|
|
268
|
+
// loaded config. We run them here, then load config, then sequence the
|
|
269
|
+
// integration codemods (which DO require a valid loaded config).
|
|
270
|
+
// ───────────────────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
if (!options.force && semverGte(currentVersion, targetVersion)) {
|
|
273
|
+
if (json) {
|
|
274
|
+
return jsonOut('upgrade.status', {
|
|
275
|
+
status: 'up_to_date',
|
|
276
|
+
from: currentVersion,
|
|
277
|
+
to: targetVersion,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
p.log.success('Already up to date — no codemods to run.');
|
|
281
|
+
p.log.info('Use --force to run codemods anyway.');
|
|
282
|
+
p.outro('Done');
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Resolve CORE transforms from the registry. These do not need the loaded
|
|
287
|
+
// config. Integration codemods are discovered later, AFTER the config
|
|
288
|
+
// loads successfully (they require a valid config to resolve).
|
|
289
|
+
const versionManifests = [
|
|
290
|
+
...(await getTransformsBetween(currentVersion, targetVersion)),
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
// Does the selected core set include >=1 CONFIG codemod? A config codemod
|
|
294
|
+
// is the established convention `meta.codemodType === 'config'` (see
|
|
295
|
+
// `toUnifiedEntry` in runner.mjs). This drives the graceful dry-run catch
|
|
296
|
+
// around `Project.load` below: a fixable config error is only "expected"
|
|
297
|
+
// when a pending core config codemod would repair it.
|
|
298
|
+
const coreConfigCodemodNames = [];
|
|
299
|
+
for (const {transforms} of versionManifests) {
|
|
300
|
+
for (const t of transforms) {
|
|
301
|
+
if (options.codemod && t.name !== options.codemod) continue;
|
|
302
|
+
if (t.meta?.codemodType === 'config') {
|
|
303
|
+
coreConfigCodemodNames.push(t.name);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const hasCoreConfigCodemod = coreConfigCodemodNames.length > 0;
|
|
308
|
+
|
|
309
|
+
// Codemods explicitly excluded via --skip-codemod, matched by the same
|
|
310
|
+
// identifier the run loop uses (core transform `t.name`, integration
|
|
311
|
+
// codemod `c.id`). Lets a user re-run past a codemod that failed at
|
|
312
|
+
// execution time.
|
|
313
|
+
const skipCodemods = new Set(options.skipCodemod ?? []);
|
|
314
|
+
|
|
315
|
+
// Count CORE transforms (optional codemods only count when explicitly
|
|
316
|
+
// requested). Integration counts are added after discovery below.
|
|
317
|
+
let totalTransforms = 0;
|
|
318
|
+
let totalOptional = 0;
|
|
319
|
+
for (const {transforms} of versionManifests) {
|
|
320
|
+
for (const t of transforms) {
|
|
321
|
+
if (options.codemod && t.name !== options.codemod) continue;
|
|
322
|
+
if (skipCodemods.has(t.name)) continue;
|
|
323
|
+
if (t.optional && !options.codemod) {
|
|
324
|
+
totalOptional++;
|
|
325
|
+
} else {
|
|
326
|
+
totalTransforms++;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Ensure jscodeshift is available before running any codemod.
|
|
332
|
+
const ready = await ensureJscodeshift({
|
|
333
|
+
installDeps: options.installDeps,
|
|
334
|
+
silent: json,
|
|
335
|
+
});
|
|
336
|
+
if (!ready) {
|
|
337
|
+
if (json)
|
|
338
|
+
return jsonError(
|
|
339
|
+
'jscodeshift is required but could not be installed.',
|
|
340
|
+
undefined,
|
|
341
|
+
ERROR_CODES.ERR_DEP_MISSING,
|
|
342
|
+
);
|
|
343
|
+
p.outro('Aborted');
|
|
344
|
+
process.exitCode = 1;
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// STEP 3 — Run CORE codemods FIRST (before loading config). In --apply,
|
|
349
|
+
// core config codemods WRITE the repaired config to disk; in dry-run they
|
|
350
|
+
// only PREVIEW. This is what makes the v0.1.3 config codemod reachable on
|
|
351
|
+
// a config that the strict loader would otherwise reject.
|
|
352
|
+
const codemodResult = await runCodemods(versionManifests, {
|
|
353
|
+
apply: options.apply,
|
|
354
|
+
path: options.path,
|
|
355
|
+
codemod: options.codemod,
|
|
356
|
+
skipCodemods,
|
|
357
|
+
silent: json,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// STEP 4 — Load the consumer's config (STRICT validation; unchanged). On
|
|
361
|
+
// --apply this now sees the repaired config the core codemod just wrote.
|
|
362
|
+
// We need `hooks.postCodemod` and the configured integration specs from
|
|
363
|
+
// here. Wrap in a graceful dry-run catch (see below).
|
|
364
|
+
// Assigned inside the try below; every catch branch returns, so these
|
|
365
|
+
// are always set before any later read.
|
|
269
366
|
let integrations;
|
|
367
|
+
let postCodemodHooks;
|
|
368
|
+
let integrationVersionGroups;
|
|
270
369
|
try {
|
|
271
|
-
const
|
|
370
|
+
const project = await Project.load(process.cwd());
|
|
371
|
+
postCodemodHooks = project.config.hooks?.postCodemod ?? [];
|
|
272
372
|
const integrationSpecs = uniqueFiles([
|
|
273
|
-
...(
|
|
373
|
+
...(project.integrations ?? []),
|
|
274
374
|
...(options.integration ?? []),
|
|
275
375
|
]);
|
|
276
376
|
integrations = await loadIntegrations(integrationSpecs);
|
|
277
377
|
} catch (err) {
|
|
378
|
+
// GRACEFUL DRY-RUN CATCH. A config that fails strict validation is the
|
|
379
|
+
// EXPECTED, fixable case ONLY when we are in dry-run AND a pending core
|
|
380
|
+
// config codemod just PREVIEWED a change to the config — i.e. the very
|
|
381
|
+
// codemod that would repair it. (Merely having a config codemod in the
|
|
382
|
+
// range is not enough: it may be a no-op on this config, in which case
|
|
383
|
+
// the validation error is genuine and we must abort. A config codemod
|
|
384
|
+
// that THREW reports zero would-change files, so it also fails this
|
|
385
|
+
// gate and aborts below — preserving the strictness contract.) This is
|
|
386
|
+
// the reason this PR reorders the pipeline.
|
|
387
|
+
const codemodWouldFixConfig =
|
|
388
|
+
hasCoreConfigCodemod && (codemodResult?.totalFilesChanged ?? 0) > 0;
|
|
389
|
+
if (!options.apply && codemodWouldFixConfig) {
|
|
390
|
+
const codemodFlags = coreConfigCodemodNames
|
|
391
|
+
.map(name => `--codemod ${name}`)
|
|
392
|
+
.join(' ');
|
|
393
|
+
const suggestedCommand = `astryx upgrade --from ${currentVersion} ${codemodFlags} --apply`;
|
|
394
|
+
const guidance =
|
|
395
|
+
'Your astryx.config currently fails strict validation, but a pending ' +
|
|
396
|
+
'config codemod would repair it. This dry run previewed the fix without ' +
|
|
397
|
+
'writing. Re-run with --apply to apply it, or run just the config codemod(s) ' +
|
|
398
|
+
'now:';
|
|
399
|
+
if (json) {
|
|
400
|
+
return jsonOut('upgrade.status', {
|
|
401
|
+
status: 'config_fixable',
|
|
402
|
+
from: currentVersion,
|
|
403
|
+
to: targetVersion,
|
|
404
|
+
configError: err.message,
|
|
405
|
+
configCodemods: coreConfigCodemodNames,
|
|
406
|
+
suggestedCommand,
|
|
407
|
+
message: guidance,
|
|
408
|
+
note: 'Integrations are skipped in this preview; they will be processed on the --apply run.',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
p.log.warn(guidance);
|
|
412
|
+
p.log.info(` ${suggestedCommand}`);
|
|
413
|
+
p.log.info(
|
|
414
|
+
'Integrations are skipped in this preview; they will be processed on the --apply run.',
|
|
415
|
+
);
|
|
416
|
+
p.outro('Dry run complete');
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
// Genuine config error (apply mode, OR dry-run with no pending core
|
|
420
|
+
// config codemod that would fix it): abort as before.
|
|
278
421
|
if (json)
|
|
279
422
|
return jsonError(
|
|
280
423
|
err.message,
|
|
@@ -286,39 +429,71 @@ export function registerUpgrade(program) {
|
|
|
286
429
|
process.exitCode = 1;
|
|
287
430
|
return;
|
|
288
431
|
}
|
|
432
|
+
|
|
289
433
|
if (!json && integrations.length > 0) {
|
|
290
434
|
p.log.info(
|
|
291
435
|
`Integrations: ${integrations.map(i => i.name ?? i.__spec).join(', ')}`,
|
|
292
436
|
);
|
|
293
437
|
}
|
|
294
438
|
|
|
295
|
-
if
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
439
|
+
// Non-blocking nudge: if any configured integration has validation
|
|
440
|
+
// issues, print one compact line to stderr pointing at
|
|
441
|
+
// validate-integration. Best-effort; suppressed in --json mode. This
|
|
442
|
+
// depends on integrations being loaded, so it lives after the successful
|
|
443
|
+
// config load (it is skipped on the graceful dry-run path above, where
|
|
444
|
+
// integrations were never loaded).
|
|
445
|
+
try {
|
|
446
|
+
await warnOnIntegrationIssues(integrations, {json});
|
|
447
|
+
} catch {
|
|
448
|
+
// Never let the nudge break the upgrade.
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// STEP 5 — Discover + run INTEGRATION codemods (only reached on a
|
|
452
|
+
// successful config load). A broken integration (bad export, invalid
|
|
453
|
+
// schema, duplicate id, missing root) is a DEFINITION error: skip that
|
|
454
|
+
// integration's codemods and warn (via the nudge above), rather than
|
|
455
|
+
// hard-failing the upgrade. An EXECUTION-time failure (a transform
|
|
456
|
+
// throwing) is handled later by the codemod-error gate, which still
|
|
457
|
+
// aborts the upgrade.
|
|
458
|
+
const integrationCodemodsByVersion = new Map();
|
|
459
|
+
for (const integration of integrations) {
|
|
460
|
+
if (!integration?.codemods) continue;
|
|
461
|
+
try {
|
|
462
|
+
const byVersion = await discoverIntegrationCodemods([integration]);
|
|
463
|
+
for (const [version, list] of byVersion) {
|
|
464
|
+
const existing = integrationCodemodsByVersion.get(version);
|
|
465
|
+
if (existing) existing.push(...list);
|
|
466
|
+
else integrationCodemodsByVersion.set(version, [...list]);
|
|
467
|
+
}
|
|
468
|
+
} catch {
|
|
469
|
+
// Skip this integration's codemods; the validate-integration nudge
|
|
470
|
+
// above surfaces the underlying issue. Best-effort, non-blocking.
|
|
302
471
|
}
|
|
303
|
-
p.log.success('Already up to date — no codemods to run.');
|
|
304
|
-
p.log.info('Use --force to run codemods anyway.');
|
|
305
|
-
p.outro('Done');
|
|
306
|
-
return;
|
|
307
472
|
}
|
|
473
|
+
integrationVersionGroups = selectIntegrationCodemods(
|
|
474
|
+
integrationCodemodsByVersion,
|
|
475
|
+
currentVersion,
|
|
476
|
+
targetVersion,
|
|
477
|
+
);
|
|
478
|
+
const hasIntegrationCodemods = integrationVersionGroups.some(
|
|
479
|
+
g => g.codemods.length > 0,
|
|
480
|
+
);
|
|
308
481
|
|
|
309
|
-
//
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
482
|
+
// Add integration transforms to the run counts.
|
|
483
|
+
for (const {codemods} of integrationVersionGroups) {
|
|
484
|
+
for (const c of codemods) {
|
|
485
|
+
if (options.codemod && c.id !== options.codemod) continue;
|
|
486
|
+
if (skipCodemods.has(c.id)) continue;
|
|
487
|
+
if (c.codemod.isOptional && !options.codemod) {
|
|
488
|
+
totalOptional++;
|
|
489
|
+
} else {
|
|
490
|
+
totalTransforms++;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
320
494
|
|
|
321
|
-
|
|
495
|
+
// No codemods at all for this range (neither core nor integration).
|
|
496
|
+
if (versionManifests.length === 0 && !hasIntegrationCodemods) {
|
|
322
497
|
if (json) {
|
|
323
498
|
return jsonOut('upgrade.status', {
|
|
324
499
|
status: 'no_codemods',
|
|
@@ -331,20 +506,7 @@ export function registerUpgrade(program) {
|
|
|
331
506
|
return;
|
|
332
507
|
}
|
|
333
508
|
|
|
334
|
-
//
|
|
335
|
-
let totalTransforms = 0;
|
|
336
|
-
let totalOptional = 0;
|
|
337
|
-
for (const {transforms} of versionManifests) {
|
|
338
|
-
for (const t of transforms) {
|
|
339
|
-
if (options.codemod && t.name !== options.codemod) continue;
|
|
340
|
-
if (t.optional && !options.codemod) {
|
|
341
|
-
totalOptional++;
|
|
342
|
-
} else {
|
|
343
|
-
totalTransforms++;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
509
|
+
// A named `--codemod` that matched nothing (across core + integration).
|
|
348
510
|
if (totalTransforms === 0 && totalOptional === 0) {
|
|
349
511
|
const msg = `Codemod "${options.codemod}" not found. Use --list to see available codemods.`;
|
|
350
512
|
if (json)
|
|
@@ -373,54 +535,73 @@ export function registerUpgrade(program) {
|
|
|
373
535
|
agentDocsRefreshed: false,
|
|
374
536
|
};
|
|
375
537
|
|
|
376
|
-
//
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
538
|
+
// Run file-based integration codemods alongside the core registry
|
|
539
|
+
// codemods (config codemods first, then code codemods), ordered by
|
|
540
|
+
// version. Their results are merged into the receipt below.
|
|
541
|
+
let integrationResult = null;
|
|
542
|
+
if (hasIntegrationCodemods) {
|
|
543
|
+
if (!json) p.log.step('Applying integration codemods...');
|
|
544
|
+
const jscodeshift = (await import('jscodeshift')).default;
|
|
545
|
+
integrationResult = runIntegrationCodemods(integrationVersionGroups, {
|
|
546
|
+
apply: options.apply,
|
|
547
|
+
path: options.path,
|
|
548
|
+
codemod: options.codemod,
|
|
549
|
+
skipCodemods,
|
|
550
|
+
jscodeshift,
|
|
551
|
+
silent: json,
|
|
552
|
+
});
|
|
391
553
|
}
|
|
392
554
|
|
|
393
|
-
// Run codemods
|
|
394
|
-
const codemodResult = await runCodemods(versionManifests, {
|
|
395
|
-
apply: options.apply,
|
|
396
|
-
path: options.path,
|
|
397
|
-
codemod: options.codemod,
|
|
398
|
-
silent: json,
|
|
399
|
-
});
|
|
400
555
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
);
|
|
406
|
-
|
|
556
|
+
// Merge core + integration codemod results into a single accounting so
|
|
557
|
+
// hooks, receipts, and error gating see both.
|
|
558
|
+
const mergedFilesChanged =
|
|
559
|
+
(codemodResult?.totalFilesChanged ?? 0) +
|
|
560
|
+
(integrationResult?.totalFilesChanged ?? 0);
|
|
561
|
+
const mergedTransformsApplied =
|
|
562
|
+
(codemodResult?.totalTransformsApplied ?? 0) +
|
|
563
|
+
(integrationResult?.totalTransformsApplied ?? 0);
|
|
564
|
+
const mergedWrittenFiles = [
|
|
565
|
+
...(codemodResult?.writtenFiles ?? []),
|
|
566
|
+
...(integrationResult?.writtenFiles ?? []),
|
|
567
|
+
];
|
|
568
|
+
const mergedErrors = [
|
|
569
|
+
...(codemodResult?.errors ?? []),
|
|
570
|
+
...(integrationResult?.errors ?? []),
|
|
571
|
+
];
|
|
572
|
+
|
|
573
|
+
// Post-codemod hooks come from the app config (config.hooks.postCodemod).
|
|
574
|
+
// They run only when codemods actually changed files. In apply mode the
|
|
575
|
+
// commands execute (nonzero exit fails the upgrade); in dry-run mode we
|
|
576
|
+
// only preview the resolved command (a buildCommand throw still fails).
|
|
577
|
+
const changedFileCount = mergedFilesChanged;
|
|
578
|
+
if (postCodemodHooks.length > 0 && changedFileCount > 0) {
|
|
579
|
+
const absoluteChangedFiles = uniqueFiles(mergedWrittenFiles);
|
|
580
|
+
const files = absoluteChangedFiles.map(file =>
|
|
407
581
|
path.relative(process.cwd(), file),
|
|
408
582
|
);
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
583
|
+
try {
|
|
584
|
+
await runPostCodemodHooks(
|
|
585
|
+
postCodemodHooks,
|
|
586
|
+
{
|
|
587
|
+
packageDir: process.cwd(),
|
|
588
|
+
files,
|
|
589
|
+
apply: options.apply,
|
|
590
|
+
},
|
|
591
|
+
json,
|
|
592
|
+
);
|
|
593
|
+
} catch (err) {
|
|
594
|
+
if (json)
|
|
595
|
+
return jsonError(
|
|
596
|
+
`Post-codemod hook failed: ${err.message}`,
|
|
597
|
+
{receipt},
|
|
598
|
+
ERROR_CODES.ERR_CODEMOD_FAILED,
|
|
599
|
+
);
|
|
600
|
+
p.log.error(`Post-codemod hook failed: ${err.message}`);
|
|
601
|
+
p.outro('Upgrade failed');
|
|
602
|
+
process.exitCode = 1;
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
424
605
|
}
|
|
425
606
|
|
|
426
607
|
// Refresh agent docs if any exist (AGENTS.md, CLAUDE.md, .claude/CLAUDE.md, etc.)
|
|
@@ -444,11 +625,9 @@ export function registerUpgrade(program) {
|
|
|
444
625
|
}
|
|
445
626
|
}
|
|
446
627
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
receipt.errors = codemodResult.errors ?? [];
|
|
451
|
-
}
|
|
628
|
+
receipt.filesChanged = mergedFilesChanged;
|
|
629
|
+
receipt.transformsApplied = mergedTransformsApplied;
|
|
630
|
+
receipt.errors = mergedErrors;
|
|
452
631
|
|
|
453
632
|
if (receipt.errors?.length > 0) {
|
|
454
633
|
const msg = `Upgrade completed with ${receipt.errors.length} codemod error${receipt.errors.length === 1 ? '' : 's'}.`;
|