@decocms/blocks-cli 7.5.1 → 7.5.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.
Files changed (43) hide show
  1. package/package.json +2 -2
  2. package/scripts/generate-invoke.ts +10 -0
  3. package/scripts/generate-schema.ts +46 -15
  4. package/scripts/generate-sections.test.ts +67 -0
  5. package/scripts/generate-sections.ts +16 -2
  6. package/scripts/lib/codegenExclusions.test.ts +16 -6
  7. package/scripts/lib/codegenExclusions.ts +13 -1
  8. package/scripts/migrate/analyzers/loader-inventory.ts +1 -1
  9. package/scripts/migrate/phase-analyze.ts +3 -3
  10. package/scripts/migrate/phase-cleanup.test.ts +70 -1
  11. package/scripts/migrate/phase-cleanup.ts +47 -26
  12. package/scripts/migrate/phase-report.ts +7 -7
  13. package/scripts/migrate/phase-scaffold.ts +4 -4
  14. package/scripts/migrate/phase-transform.ts +1 -1
  15. package/scripts/migrate/phase-verify.ts +6 -5
  16. package/scripts/migrate/post-cleanup/rules.ts +55 -44
  17. package/scripts/migrate/post-cleanup/runner.test.ts +58 -34
  18. package/scripts/migrate/templates/cache-config.ts +4 -4
  19. package/scripts/migrate/templates/commerce-loaders.ts +10 -10
  20. package/scripts/migrate/templates/cursor-rules.test.ts +6 -0
  21. package/scripts/migrate/templates/cursor-rules.ts +12 -11
  22. package/scripts/migrate/templates/hooks.test.ts +7 -7
  23. package/scripts/migrate/templates/hooks.ts +10 -10
  24. package/scripts/migrate/templates/lib-utils.test.ts +2 -2
  25. package/scripts/migrate/templates/lib-utils.ts +4 -4
  26. package/scripts/migrate/templates/no-legacy-packages.test.ts +147 -0
  27. package/scripts/migrate/templates/package-json.ts +30 -10
  28. package/scripts/migrate/templates/routes.ts +8 -10
  29. package/scripts/migrate/templates/sdk-gen.ts +1 -1
  30. package/scripts/migrate/templates/section-loaders.ts +7 -7
  31. package/scripts/migrate/templates/server-entry.ts +37 -37
  32. package/scripts/migrate/templates/setup.ts +6 -6
  33. package/scripts/migrate/templates/types-gen.ts +2 -2
  34. package/scripts/migrate/templates/ui-components.ts +2 -2
  35. package/scripts/migrate/templates/vite-config.ts +12 -3
  36. package/scripts/migrate/transforms/fresh-apis.ts +5 -5
  37. package/scripts/migrate/transforms/imports.test.ts +138 -0
  38. package/scripts/migrate/transforms/imports.ts +65 -43
  39. package/scripts/migrate/transforms/jsx.ts +8 -0
  40. package/scripts/migrate/transforms/section-conventions.ts +1 -1
  41. package/scripts/migrate/types.ts +1 -1
  42. package/scripts/migrate-post-cleanup.ts +8 -8
  43. package/scripts/migrate.ts +11 -11
@@ -0,0 +1,138 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { transformImports } from "./imports";
3
+
4
+ describe("transformImports — shopify hooks", () => {
5
+ it("rewrites the three known Shopify hooks to the site-local scaffolded files, mirroring VTEX", () => {
6
+ const src =
7
+ `import { useUser } from "apps/shopify/hooks/useUser.ts";\n` +
8
+ `import { useCart } from "apps/shopify/hooks/useCart.ts";\n` +
9
+ `import { useWishlist } from "apps/shopify/hooks/useWishlist.ts";\n`;
10
+
11
+ const r = transformImports(src);
12
+
13
+ expect(r.changed).toBe(true);
14
+ expect(r.content).toContain(`import { useUser } from "~/hooks/useUser";`);
15
+ expect(r.content).toContain(`import { useCart } from "~/hooks/useCart";`);
16
+ expect(r.content).toContain(
17
+ `import { useWishlist } from "~/hooks/useWishlist";`,
18
+ );
19
+ // Must never point at the nonexistent @decocms/apps-shopify/hooks/* target.
20
+ expect(r.content).not.toContain("@decocms/apps-shopify/hooks");
21
+ });
22
+
23
+ it("does not rewrite an unknown shopify hook name to a nonexistent package export", () => {
24
+ // No shopify hook other than useCart/useUser/useWishlist has ever existed
25
+ // (verified against full git history), so there is no generic fallback
26
+ // rule anymore. Unhandled "apps/*" imports fall through to the general
27
+ // apps/ catch-all, which removes the import line entirely — surfacing as
28
+ // an obvious break at the usage site rather than an unresolvable module.
29
+ const src = `import { useSomethingElse } from "apps/shopify/hooks/useSomethingElse.ts";\n`;
30
+
31
+ const r = transformImports(src);
32
+
33
+ expect(r.content).not.toContain("@decocms/apps-shopify/hooks");
34
+ expect(r.content).not.toContain("apps/shopify/hooks");
35
+ });
36
+
37
+ it("still rewrites shopify utils/actions/loaders to the real @decocms/apps-shopify package", () => {
38
+ const src =
39
+ `import { formatMoney } from "apps/shopify/utils/formatMoney.ts";\n` +
40
+ `import { addItems } from "apps/shopify/actions/cart/addItems.ts";\n` +
41
+ `import ProductList from "apps/shopify/loaders/ProductList.ts";\n`;
42
+
43
+ const r = transformImports(src);
44
+
45
+ expect(r.content).toContain(
46
+ `import { formatMoney } from "@decocms/apps-shopify/utils/formatMoney";`,
47
+ );
48
+ expect(r.content).toContain(
49
+ `import { addItems } from "@decocms/apps-shopify/actions/cart/addItems";`,
50
+ );
51
+ expect(r.content).toContain(
52
+ `import ProductList from "@decocms/apps-shopify/loaders/ProductList";`,
53
+ );
54
+ });
55
+ });
56
+
57
+ describe("transformImports — splitDecoHooksImports", () => {
58
+ it("splits a mixed useDevice + non-useDevice named import from the legacy @deco/deco/hooks specifier", () => {
59
+ const src = `import { useDevice, useScript, useSection } from "@deco/deco/hooks";\n`;
60
+
61
+ const r = transformImports(src);
62
+
63
+ expect(r.changed).toBe(true);
64
+ expect(r.content).toContain(
65
+ `import { useDevice } from "@decocms/blocks/sdk/useDevice";`,
66
+ );
67
+ expect(r.content).toContain(
68
+ `import { useScript, useSection } from "@decocms/blocks/sdk/useScript";`,
69
+ );
70
+ expect(r.notes).toContain(
71
+ "Split useDevice into separate import from @decocms/blocks/sdk/useDevice",
72
+ );
73
+ });
74
+
75
+ it("emits only the useDevice import when useDevice is the sole named import", () => {
76
+ const src = `import { useDevice } from "@deco/deco/hooks";\n`;
77
+
78
+ const r = transformImports(src);
79
+
80
+ expect(r.content).toBe(
81
+ `import { useDevice } from "@decocms/blocks/sdk/useDevice";\n`,
82
+ );
83
+ expect(r.content).not.toContain("@decocms/blocks/sdk/useScript");
84
+ });
85
+
86
+ it("leaves a non-useDevice-only import pointed at useScript, unsplit", () => {
87
+ const src = `import { useScript, useSection } from "@deco/deco/hooks";\n`;
88
+
89
+ const r = transformImports(src);
90
+
91
+ expect(r.content).toBe(
92
+ `import { useScript, useSection } from "@decocms/blocks/sdk/useScript";\n`,
93
+ );
94
+ expect(r.content).not.toContain("@decocms/blocks/sdk/useDevice");
95
+ });
96
+
97
+ it("handles a `type` import with mixed useDevice/non-useDevice specifiers", () => {
98
+ const src = `import type { useDevice, useScript } from "@deco/deco/hooks";\n`;
99
+
100
+ const r = transformImports(src);
101
+
102
+ // The base IMPORT_RULES rewrite fires first (regardless of `type`), then
103
+ // splitDecoHooksImports re-splits the resulting line — confirming the
104
+ // regex-chain order (rewriteSpecifier → splitDecoHooksImports) still
105
+ // matches `import type { ... }` shapes, not just bare `import { ... }`.
106
+ expect(r.content).toContain(
107
+ `import { useDevice } from "@decocms/blocks/sdk/useDevice";`,
108
+ );
109
+ expect(r.content).toContain(
110
+ `import { useScript } from "@decocms/blocks/sdk/useScript";`,
111
+ );
112
+ });
113
+
114
+ it("preserves aliased specifiers (useDevice as useDeviceHook) on the split line", () => {
115
+ const src = `import { useDevice as useDeviceHook, useScript } from "@deco/deco/hooks";\n`;
116
+
117
+ const r = transformImports(src);
118
+
119
+ expect(r.content).toContain(
120
+ `import { useDevice as useDeviceHook } from "@decocms/blocks/sdk/useDevice";`,
121
+ );
122
+ expect(r.content).toContain(
123
+ `import { useScript } from "@decocms/blocks/sdk/useScript";`,
124
+ );
125
+ });
126
+
127
+ it("does not touch @decocms/blocks/sdk/useScript imports that were never routed through @deco/deco/hooks", () => {
128
+ // Guards the order-of-operations: splitDecoHooksImports must only ever
129
+ // fire as a post-process of the IMPORT_RULES rewrite, not independently
130
+ // match any pre-existing import already targeting useScript.
131
+ const src = `import { useScript } from "@decocms/blocks/sdk/useScript";\n`;
132
+
133
+ const r = transformImports(src);
134
+
135
+ expect(r.changed).toBe(false);
136
+ expect(r.content).toBe(src);
137
+ });
138
+ });
@@ -20,7 +20,7 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
20
20
  [/^"@preact\/signals"$/, `"~/sdk/signal"`],
21
21
 
22
22
  // Deco framework — hooks need splitting (useDevice, useScript, useSection)
23
- [/^"@deco\/deco\/hooks"$/, `"@decocms/start/sdk/useScript"`],
23
+ [/^"@deco\/deco\/hooks"$/, `"@decocms/blocks/sdk/useScript"`],
24
24
  [/^"@deco\/deco\/blocks"$/, `"~/types/deco"`],
25
25
  [/^"@deco\/deco\/o11y"$/, null], // logger — use console.log/warn/error instead
26
26
  [/^"@deco\/deco\/web"$/, null], // runtime.ts is rewritten
@@ -32,66 +32,82 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
32
32
  // Widget aliases (ImageWidget, HTMLWidget, ...) are framework-owned —
33
33
  // every site has the same type set, and the schema generator detects
34
34
  // them via type-text matching, not module identity. Re-export from
35
- // @decocms/start/types/widgets so we don't keep a duplicated 8-line
35
+ // @decocms/blocks/types/widgets so we don't keep a duplicated 8-line
36
36
  // file in every site.
37
- [/^"apps\/admin\/widgets\.ts"$/, `"@decocms/start/types/widgets"`],
37
+ [/^"apps\/admin\/widgets\.ts"$/, `"@decocms/blocks/types/widgets"`],
38
38
  [/^"apps\/website\/components\/Image\.tsx"$/, `"~/components/ui/Image"`],
39
39
  [/^"apps\/website\/components\/Picture\.tsx"$/, `"~/components/ui/Picture"`],
40
40
  [/^"apps\/website\/components\/Video\.tsx"$/, `"~/components/ui/Video"`],
41
41
  [/^"apps\/website\/components\/Theme\.tsx"$/, `"~/components/ui/Theme"`],
42
42
  [/^"apps\/website\/components\/_seo\/[^"]+?"$/, null], // SEO preview — framework-only, remove
43
43
  [/^"apps\/website\/components\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
44
- [/^"apps\/commerce\/types\.ts"$/, `"@decocms/apps/commerce/types"`],
44
+ [/^"apps\/commerce\/types\.ts"$/, `"@decocms/apps-commerce/types"`],
45
45
  [/^"apps\/commerce\/mod\.ts"$/, `"~/types/commerce-app"`],
46
- [/^"apps\/commerce\/types"$/, `"@decocms/apps/commerce/types"`],
46
+ [/^"apps\/commerce\/types"$/, `"@decocms/apps-commerce/types"`],
47
47
 
48
- // Apps — VTEX hooks: useUser/useCart/useWishlist → local hooks (react-query based @decocms/apps hooks crash Workers SSR)
48
+ // Apps — VTEX hooks: useUser/useCart/useWishlist → local hooks (react-query based @decocms/apps-vtex hooks crash Workers SSR)
49
49
  [/^"apps\/vtex\/hooks\/useUser(?:\.ts)?"$/, `"~/hooks/useUser"`],
50
50
  [/^"apps\/vtex\/hooks\/useCart(?:\.ts)?"$/, `"~/hooks/useCart"`],
51
51
  [/^"apps\/vtex\/hooks\/useWishlist(?:\.ts)?"$/, `"~/hooks/useWishlist"`],
52
- [/^"apps\/vtex\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/hooks/$1"`],
53
- // Specific VTEX utils that moved to different paths in @decocms/apps
52
+ [/^"apps\/vtex\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/hooks/$1"`],
53
+ // Specific VTEX utils that moved to different paths in @decocms/apps-vtex
54
54
  // fetchVTEX (generic fetchSafe + QS sanitization) lives at vtex/utils/fetch in apps-start.
55
- [/^"apps\/vtex\/utils\/fetchVTEX(?:\.ts)?"$/, `"@decocms/apps/vtex/utils/fetch"`],
56
- [/^"apps\/vtex\/utils\/client(?:\.ts)?"$/, `"@decocms/apps/vtex/client"`],
57
- [/^"apps\/vtex\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/utils/$1"`],
58
- [/^"apps\/vtex\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/actions/$1"`],
55
+ [/^"apps\/vtex\/utils\/fetchVTEX(?:\.ts)?"$/, `"@decocms/apps-vtex/utils/fetch"`],
56
+ [/^"apps\/vtex\/utils\/client(?:\.ts)?"$/, `"@decocms/apps-vtex/client"`],
57
+ [/^"apps\/vtex\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/utils/$1"`],
58
+ [/^"apps\/vtex\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/actions/$1"`],
59
59
  // Tier B loader path rewrites (apps-start has no `intelligentSearch/`, `legacy/<file>`, or `paths/` subdirs).
60
60
  // Intelligent Search loaders moved to inline-loaders/.
61
61
  [
62
62
  /^"apps\/vtex\/loaders\/intelligentSearch\/productList(?:\.ts)?"$/,
63
- `"@decocms/apps/vtex/inline-loaders/productList"`,
63
+ `"@decocms/apps-vtex/inline-loaders/productList"`,
64
64
  ],
65
65
  [
66
66
  /^"apps\/vtex\/loaders\/intelligentSearch\/productListingPage(?:\.ts)?"$/,
67
- `"@decocms/apps/vtex/inline-loaders/productListingPage"`,
67
+ `"@decocms/apps-vtex/inline-loaders/productListingPage"`,
68
68
  ],
69
69
  [
70
70
  /^"apps\/vtex\/loaders\/intelligentSearch\/productDetailsPage(?:\.ts)?"$/,
71
- `"@decocms/apps/vtex/inline-loaders/productDetailsPage"`,
71
+ `"@decocms/apps-vtex/inline-loaders/productDetailsPage"`,
72
72
  ],
73
73
  [
74
74
  /^"apps\/vtex\/loaders\/intelligentSearch\/suggestions(?:\.ts)?"$/,
75
- `"@decocms/apps/vtex/inline-loaders/suggestions"`,
75
+ `"@decocms/apps-vtex/inline-loaders/suggestions"`,
76
76
  ],
77
77
  // Legacy product loaders are consolidated into a single file (named exports).
78
78
  [
79
79
  /^"apps\/vtex\/loaders\/legacy\/(?:productList|productListingPage|productDetailsPage|search|category)(?:\.ts)?"$/,
80
- `"@decocms/apps/vtex/loaders/legacy"`,
80
+ `"@decocms/apps-vtex/loaders/legacy"`,
81
81
  ],
82
82
  // Path-default loaders (sitemap seeds) don't exist in TanStack Start — paths resolve at request time.
83
83
  [/^"apps\/vtex\/loaders\/paths\/(?:[^"]+)(?:\.ts)?"$/, null],
84
- [/^"apps\/vtex\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/vtex/loaders/$1"`],
85
- [/^"apps\/vtex\/types(?:\.ts)?"$/, `"@decocms/apps/vtex/types"`],
84
+ [/^"apps\/vtex\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/loaders/$1"`],
85
+ [/^"apps\/vtex\/types(?:\.ts)?"$/, `"@decocms/apps-vtex/types"`],
86
86
  [/^"apps\/vtex\/mod(?:\.ts)?"$/, `"~/types/vtex-app"`],
87
87
  // Apps — Shopify (hooks, utils, actions, loaders)
88
- [/^"apps\/shopify\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/shopify/hooks/$1"`],
89
- [/^"apps\/shopify\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/shopify/utils/$1"`],
90
- [/^"apps\/shopify\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/shopify/actions/$1"`],
91
- [/^"apps\/shopify\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/shopify/loaders/$1"`],
88
+ // Shopify hooks were never a real package export — not in the pre-split
89
+ // @decocms/apps monolith, not in @decocms/apps-shopify today (it ships
90
+ // no src/hooks/ dir and no ./hooks/* export; verified via `git log
91
+ // --diff-filter=A -- '**/shopify/hooks/**'` returning nothing across all
92
+ // history). The legacy migration reference
93
+ // (.agents/skills/deco-to-tanstack-migration/references/platform-hooks/README.md)
94
+ // confirms Shopify's useCart/useUser/useWishlist were always meant to be
95
+ // site-local no-op stubs, and templates/hooks.ts's generateHooks() still
96
+ // scaffolds them at src/hooks/use{Cart,User,Wishlist}.ts for every
97
+ // non-VTEX platform (shopify included). Mirror the VTEX rule shape below:
98
+ // route the three known hook names to the scaffolded local files, same as
99
+ // "apps/vtex/hooks/useUser" → "~/hooks/useUser" above. Do NOT reintroduce
100
+ // a generic "apps/shopify/hooks/$1" → "@decocms/apps-shopify/hooks/$1"
101
+ // fallback — that target has never existed.
102
+ [/^"apps\/shopify\/hooks\/useUser(?:\.ts)?"$/, `"~/hooks/useUser"`],
103
+ [/^"apps\/shopify\/hooks\/useCart(?:\.ts)?"$/, `"~/hooks/useCart"`],
104
+ [/^"apps\/shopify\/hooks\/useWishlist(?:\.ts)?"$/, `"~/hooks/useWishlist"`],
105
+ [/^"apps\/shopify\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-shopify/utils/$1"`],
106
+ [/^"apps\/shopify\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-shopify/actions/$1"`],
107
+ [/^"apps\/shopify\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-shopify/loaders/$1"`],
92
108
  // Apps — commerce (types, SDK, utils)
93
- [/^"apps\/commerce\/sdk\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/commerce/sdk/$1"`],
94
- [/^"apps\/commerce\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps/commerce/utils/$1"`],
109
+ [/^"apps\/commerce\/sdk\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-commerce/sdk/$1"`],
110
+ [/^"apps\/commerce\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-commerce/utils/$1"`],
95
111
 
96
112
  // Apps — shared utils (STALE, fetchSafe, createHttpClient, etc.)
97
113
  [/^"apps\/utils\/fetch(?:\.ts)?"$/, `"~/lib/fetch-utils"`],
@@ -116,7 +132,7 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
116
132
  [/^"@std\/crypto"$/, null], // Use globalThis.crypto instead
117
133
 
118
134
  // site/sdk/* → framework equivalents (before the catch-all site/ → ~/ rule)
119
- [/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/start/sdk/clx"`],
135
+ [/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/blocks/sdk/clx"`],
120
136
  [/^"site\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
121
137
  // useOffer and useVariantPossiblities kept as site files (~/sdk/)
122
138
  [/^"site\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
@@ -127,10 +143,10 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
127
143
  [/^"~\/account\.json"$/, `"~/constants/account"`],
128
144
 
129
145
  // $store/ → ~/ (common Deno import map alias for project root)
130
- [/^"\$store\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/start/sdk/clx"`],
146
+ [/^"\$store\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/blocks/sdk/clx"`],
131
147
  [/^"\$store\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
132
148
  // useOffer and useVariantPossiblities kept as site files (~/sdk/)
133
- [/^"\$store\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/formatPrice"`],
149
+ [/^"\$store\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps-commerce/sdk/formatPrice"`],
134
150
  [/^"\$store\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
135
151
  // islands → components (must be before $store catch-all)
136
152
  [/^"\$store\/islands\/ui\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
@@ -141,10 +157,10 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
141
157
  [/^"\$home\/(.+)"$/, `"~/$1"`],
142
158
 
143
159
  // site/ → ~/
144
- [/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/start/sdk/clx"`],
160
+ [/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/blocks/sdk/clx"`],
145
161
  [/^"site\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
146
162
  // useOffer and useVariantPossiblities kept as site files (~/sdk/)
147
- [/^"site\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps/commerce/sdk/formatPrice"`],
163
+ [/^"site\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps-commerce/sdk/formatPrice"`],
148
164
  [/^"site\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
149
165
  // islands → components (must be before site/ catch-all)
150
166
  [/^"site\/islands\/ui\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
@@ -155,10 +171,16 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
155
171
  [/^"~\/islands\/ui\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
156
172
  [/^"~\/islands\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/$1"`],
157
173
 
158
- // @decocms/apps hooks → local hooks (react-query hooks crash Workers SSR at module eval)
174
+ // @decocms/apps-vtex hooks → local hooks (react-query hooks crash Workers SSR at module eval)
175
+ // Pre-7.x monolith path — kept for sites whose Deno import map already
176
+ // aliased directly to the npm specifier instead of the "apps/vtex/..." form.
159
177
  [/^"@decocms\/apps\/vtex\/hooks\/useUser"$/, `"~/hooks/useUser"`],
160
178
  [/^"@decocms\/apps\/vtex\/hooks\/useCart"$/, `"~/hooks/useCart"`],
161
179
  [/^"@decocms\/apps\/vtex\/hooks\/useWishlist"$/, `"~/hooks/useWishlist"`],
180
+ // Post-7.x split package path — same rationale, current package name.
181
+ [/^"@decocms\/apps-vtex\/hooks\/useUser"$/, `"~/hooks/useUser"`],
182
+ [/^"@decocms\/apps-vtex\/hooks\/useCart"$/, `"~/hooks/useCart"`],
183
+ [/^"@decocms\/apps-vtex\/hooks\/useWishlist"$/, `"~/hooks/useWishlist"`],
162
184
  ];
163
185
 
164
186
  /**
@@ -167,14 +189,14 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
167
189
  * The key is the ending of the import path, the value is the replacement specifier.
168
190
  */
169
191
  const RELATIVE_SDK_REWRITES: Array<[RegExp, string]> = [
170
- // sdk/clx → @decocms/start/sdk/clx (framework utility)
171
- [/(?:\.\.\/)*sdk\/clx(?:\.tsx?)?$/, "@decocms/start/sdk/clx"],
192
+ // sdk/clx → @decocms/blocks/sdk/clx (framework utility)
193
+ [/(?:\.\.\/)*sdk\/clx(?:\.tsx?)?$/, "@decocms/blocks/sdk/clx"],
172
194
  // sdk/useId → react (useId is built-in in React 19)
173
195
  [/(?:\.\.\/)*sdk\/useId(?:\.tsx?)?$/, "react"],
174
196
  // sdk/useOffer — kept as-is (sites customize offer logic)
175
197
  // sdk/useVariantPossiblities — kept as-is (sites customize variant logic)
176
- // sdk/format → @decocms/apps/commerce/sdk/formatPrice
177
- [/(?:\.\.\/)*sdk\/format(?:\.tsx?)?$/, "@decocms/apps/commerce/sdk/formatPrice"],
198
+ // sdk/format → @decocms/apps-commerce/sdk/formatPrice
199
+ [/(?:\.\.\/)*sdk\/format(?:\.tsx?)?$/, "@decocms/apps-commerce/sdk/formatPrice"],
178
200
  // sdk/usePlatform → remove entirely
179
201
  [/(?:\.\.\/)*sdk\/usePlatform(?:\.tsx?)?$/, ""],
180
202
  // static/adminIcons → deleted (icon loaders need rewriting)
@@ -234,16 +256,16 @@ export function transformImports(
234
256
  /**
235
257
  * Post-process: split @deco/deco/hooks imports.
236
258
  * In the old stack, @deco/deco/hooks exported useDevice, useScript, useSection, etc.
237
- * In @decocms/start, useDevice is at @decocms/start/sdk/useDevice.
259
+ * In @decocms/blocks, useDevice is at @decocms/blocks/sdk/useDevice.
238
260
  * After import rewriting, we need to split lines like:
239
- * import { useDevice, useScript } from "@decocms/start/sdk/useScript"
261
+ * import { useDevice, useScript } from "@decocms/blocks/sdk/useScript"
240
262
  * into:
241
- * import { useDevice } from "@decocms/start/sdk/useDevice"
242
- * import { useScript } from "@decocms/start/sdk/useScript"
263
+ * import { useDevice } from "@decocms/blocks/sdk/useDevice"
264
+ * import { useScript } from "@decocms/blocks/sdk/useScript"
243
265
  */
244
266
  function splitDecoHooksImports(code: string): string {
245
267
  return code.replace(
246
- /^(import\s+(?:type\s+)?\{)([^}]*\buseDevice\b[^}]*)(\}\s+from\s+["']@decocms\/start\/sdk\/useScript["'];?)$/gm,
268
+ /^(import\s+(?:type\s+)?\{)([^}]*\buseDevice\b[^}]*)(\}\s+from\s+["']@decocms\/blocks\/sdk\/useScript["'];?)$/gm,
247
269
  (_match, _prefix, importList, _suffix) => {
248
270
  const items = importList.split(",").map((s: string) => s.trim()).filter(Boolean);
249
271
  const deviceItems = items.filter((s: string) => s.includes("useDevice"));
@@ -251,10 +273,10 @@ export function transformImports(
251
273
 
252
274
  const lines: string[] = [];
253
275
  if (deviceItems.length > 0) {
254
- lines.push(`import { ${deviceItems.join(", ")} } from "@decocms/start/sdk/useDevice";`);
276
+ lines.push(`import { ${deviceItems.join(", ")} } from "@decocms/blocks/sdk/useDevice";`);
255
277
  }
256
278
  if (otherItems.length > 0) {
257
- lines.push(`import { ${otherItems.join(", ")} } from "@decocms/start/sdk/useScript";`);
279
+ lines.push(`import { ${otherItems.join(", ")} } from "@decocms/blocks/sdk/useScript";`);
258
280
  }
259
281
  return lines.join("\n");
260
282
  },
@@ -356,7 +378,7 @@ export function transformImports(
356
378
  if (afterSplit !== result) {
357
379
  result = afterSplit;
358
380
  changed = true;
359
- notes.push("Split useDevice into separate import from @decocms/start/sdk/useDevice");
381
+ notes.push("Split useDevice into separate import from @decocms/blocks/sdk/useDevice");
360
382
  }
361
383
 
362
384
  // Rewrite dynamic imports: route through rewriteSpecifier so sdk-specific
@@ -257,6 +257,14 @@ export function transformJsx(content: string): TransformResult {
257
257
  // In TanStack Start, nested sections have Component as a string key, not a function.
258
258
  // SectionRenderer from @decocms/start/hooks handles the lazy registry lookup.
259
259
  //
260
+ // HANDOFF MARKER: "@decocms/start/hooks" below is intentionally the
261
+ // pre-split package path, not the real current one — it's a same-run
262
+ // handoff to phase-cleanup.ts's upgradeSectionRenderer(), which runs
263
+ // later in the same migration and rewrites this exact import specifier
264
+ // to `import { RenderSection } from "@decocms/blocks/hooks"`. If this
265
+ // transform's run order relative to the cleanup phase ever changes, that
266
+ // rewrite target must move with it.
267
+ //
260
268
  // Gate on ANY variant of the .Component/.props pattern (simple, extra props, or multi-line).
261
269
  const sectionPatternGate = /\.\s*Component[\s\n]+\{\.\.\.(\w+)\.props\}/;
262
270
  if (sectionPatternGate.test(result)) {
@@ -8,7 +8,7 @@ import type { SectionMeta, TransformResult } from "../types";
8
8
  * Adds section convention exports (sync, eager, layout, cache)
9
9
  * to section files based on metadata extracted during analysis.
10
10
  *
11
- * These exports are read by generate-sections.ts in @decocms/start
11
+ * These exports are read by generate-sections.ts in @decocms/blocks-cli
12
12
  * to build the sections.gen.ts registry.
13
13
  *
14
14
  * The set of section *names* that get hints applied is configurable
@@ -122,7 +122,7 @@ export interface LoaderInfo {
122
122
  hasCache: boolean;
123
123
  /** Has export const cacheKey */
124
124
  hasCacheKey: boolean;
125
- /** Maps to a known @decocms/apps equivalent */
125
+ /** Maps to a known @decocms/apps-* equivalent */
126
126
  appsEquivalent: string | null;
127
127
  /** Is a custom loader that needs dynamic import in commerce-loaders */
128
128
  isCustom: boolean;
@@ -8,8 +8,8 @@
8
8
  * but turns it into something CI can actually run.
9
9
  *
10
10
  * Usage (from a migrated site directory):
11
- * npx -p @decocms/start deco-post-cleanup
12
- * npx -p @decocms/start deco-post-cleanup --json
11
+ * npx -p @decocms/blocks-cli deco-post-cleanup
12
+ * npx -p @decocms/blocks-cli deco-post-cleanup --json
13
13
  *
14
14
  * Options:
15
15
  * --source <dir> Site directory to audit (default: current directory)
@@ -65,13 +65,13 @@ function parseArgs(args: string[]): CliOpts {
65
65
 
66
66
  function showHelp() {
67
67
  console.log(`
68
- @decocms/start — Post-Migration Cleanup Audit
68
+ @decocms/blocks-cli — Post-Migration Cleanup Audit
69
69
 
70
70
  Scans a migrated site for dead code and obsolete boilerplate that the
71
71
  framework now owns. Read-only — prints findings, does not modify files.
72
72
 
73
73
  Usage:
74
- npx -p @decocms/start deco-post-cleanup [options]
74
+ npx -p @decocms/blocks-cli deco-post-cleanup [options]
75
75
 
76
76
  Options:
77
77
  --source <dir> Site directory to audit (default: .)
@@ -88,10 +88,10 @@ function showHelp() {
88
88
  --help, -h Show this help
89
89
 
90
90
  Examples:
91
- npx -p @decocms/start deco-post-cleanup
92
- npx -p @decocms/start deco-post-cleanup --source ./my-site --json
93
- npx -p @decocms/start deco-post-cleanup --fix
94
- npx -p @decocms/start deco-post-cleanup --fix --strict # fail CI if anything left
91
+ npx -p @decocms/blocks-cli deco-post-cleanup
92
+ npx -p @decocms/blocks-cli deco-post-cleanup --source ./my-site --json
93
+ npx -p @decocms/blocks-cli deco-post-cleanup --fix
94
+ npx -p @decocms/blocks-cli deco-post-cleanup --fix --strict # fail CI if anything left
95
95
 
96
96
  See: .agents/skills/deco-to-tanstack-migration/references/post-migration-cleanup.md
97
97
  `);
@@ -4,10 +4,10 @@
4
4
  * Migration Script: Fresh/Deno/Preact → TanStack Start/React/Cloudflare Workers
5
5
  *
6
6
  * Converts a Deco storefront from the old Fresh/Deno stack to the new TanStack Start stack.
7
- * Part of the @decocms/start framework — run from a site's root directory.
7
+ * Ships as part of @decocms/blocks-cli — run from a site's root directory.
8
8
  *
9
9
  * Usage (from your Fresh site directory):
10
- * npx -p @decocms/start deco-migrate [options]
10
+ * npx -p @decocms/blocks-cli deco-migrate [options]
11
11
  *
12
12
  * Options:
13
13
  * --source <dir> Source directory (default: current directory)
@@ -102,10 +102,10 @@ function parseArgs(args: string[]): {
102
102
 
103
103
  function showHelp() {
104
104
  console.log(`
105
- @decocms/start — Migration Script: Fresh/Deno → TanStack Start
105
+ @decocms/blocks-cli — Migration Script: Fresh/Deno → TanStack Start
106
106
 
107
107
  Usage:
108
- npx -p @decocms/start deco-migrate [options]
108
+ npx -p @decocms/blocks-cli deco-migrate [options]
109
109
 
110
110
  Options:
111
111
  --source <dir> Source directory (default: .)
@@ -119,10 +119,10 @@ function showHelp() {
119
119
  --help, -h Show this help message
120
120
 
121
121
  Examples:
122
- npx -p @decocms/start deco-migrate --dry-run --verbose
123
- npx -p @decocms/start deco-migrate --source ./my-site
124
- npx -p @decocms/start deco-migrate --strict --with-build
125
- npx -p @decocms/start deco-migrate
122
+ npx -p @decocms/blocks-cli deco-migrate --dry-run --verbose
123
+ npx -p @decocms/blocks-cli deco-migrate --source ./my-site
124
+ npx -p @decocms/blocks-cli deco-migrate --strict --with-build
125
+ npx -p @decocms/blocks-cli deco-migrate
126
126
  `);
127
127
  }
128
128
 
@@ -136,7 +136,7 @@ async function main() {
136
136
 
137
137
  const sourceDir = path.resolve(opts.source);
138
138
 
139
- banner("@decocms/start — Migrate: Fresh/Deno → TanStack Start");
139
+ banner("@decocms/blocks-cli — Migrate: Fresh/Deno → TanStack Start");
140
140
  stat("Source", sourceDir);
141
141
  stat("Mode", opts.dryRun ? yellow("DRY RUN") : green("EXECUTE"));
142
142
  stat("Verbose", opts.verbose ? "yes" : "no");
@@ -254,7 +254,7 @@ function bootstrap(ctx: { sourceDir: string }) {
254
254
  // matching `packageManager` field that pins the version.
255
255
  const pm = "bun";
256
256
  if (!run(`${pm} install`, "Install dependencies", true)) return;
257
- run("bunx tsx node_modules/@decocms/start/scripts/generate-blocks.ts", "Generate CMS blocks");
257
+ run("bunx tsx node_modules/@decocms/blocks-cli/scripts/generate-blocks.ts", "Generate CMS blocks");
258
258
  // generate-invoke emits src/server/invoke.gen.ts with top-level
259
259
  // createServerFn declarations + the forwardResponseCookies bridge that
260
260
  // propagates VTEX Set-Cookie headers (orderFormId, segment, sc…) to the
@@ -265,7 +265,7 @@ function bootstrap(ctx: { sourceDir: string }) {
265
265
  // running the generator gives every freshly-migrated site the canonical
266
266
  // RPC path so VTEX hooks (useCart, useUser, useWishlist) work end-to-end.
267
267
  run(
268
- "bunx tsx node_modules/@decocms/start/scripts/generate-invoke.ts",
268
+ "bunx tsx node_modules/@decocms/blocks-cli/scripts/generate-invoke.ts",
269
269
  "Generate VTEX invoke server functions",
270
270
  );
271
271
  run("bunx tsr generate", "Generate TanStack routes");