@decocms/blocks-cli 7.5.1 → 7.5.2
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/package.json +2 -2
- package/scripts/generate-invoke.ts +10 -0
- package/scripts/migrate/analyzers/loader-inventory.ts +1 -1
- package/scripts/migrate/phase-analyze.ts +3 -3
- package/scripts/migrate/phase-cleanup.ts +29 -23
- package/scripts/migrate/phase-report.ts +7 -7
- package/scripts/migrate/phase-scaffold.ts +4 -4
- package/scripts/migrate/phase-transform.ts +1 -1
- package/scripts/migrate/phase-verify.ts +6 -5
- package/scripts/migrate/post-cleanup/rules.ts +55 -44
- package/scripts/migrate/post-cleanup/runner.test.ts +58 -34
- package/scripts/migrate/templates/cache-config.ts +4 -4
- package/scripts/migrate/templates/commerce-loaders.ts +10 -10
- package/scripts/migrate/templates/cursor-rules.test.ts +6 -0
- package/scripts/migrate/templates/cursor-rules.ts +12 -11
- package/scripts/migrate/templates/hooks.test.ts +7 -7
- package/scripts/migrate/templates/hooks.ts +10 -10
- package/scripts/migrate/templates/lib-utils.test.ts +2 -2
- package/scripts/migrate/templates/lib-utils.ts +4 -4
- package/scripts/migrate/templates/no-legacy-packages.test.ts +147 -0
- package/scripts/migrate/templates/package-json.ts +24 -10
- package/scripts/migrate/templates/routes.ts +8 -10
- package/scripts/migrate/templates/sdk-gen.ts +1 -1
- package/scripts/migrate/templates/section-loaders.ts +7 -7
- package/scripts/migrate/templates/server-entry.ts +37 -37
- package/scripts/migrate/templates/setup.ts +6 -6
- package/scripts/migrate/templates/types-gen.ts +2 -2
- package/scripts/migrate/templates/ui-components.ts +2 -2
- package/scripts/migrate/templates/vite-config.ts +12 -3
- package/scripts/migrate/transforms/fresh-apis.ts +5 -5
- package/scripts/migrate/transforms/imports.ts +49 -43
- package/scripts/migrate/transforms/section-conventions.ts +1 -1
- package/scripts/migrate/types.ts +1 -1
- package/scripts/migrate-post-cleanup.ts +8 -8
- package/scripts/migrate.ts +11 -11
|
@@ -37,15 +37,15 @@ export function generateCommerceLoaders(ctx: MigrationContext): string {
|
|
|
37
37
|
lines.push(` */`);
|
|
38
38
|
|
|
39
39
|
if (ctx.platform === "vtex") {
|
|
40
|
-
lines.push(`import { getVtexConfig } from "@decocms/apps
|
|
41
|
-
lines.push(`import { createVtexCommerceLoaders, createCachedPDPLoader } from "@decocms/apps
|
|
40
|
+
lines.push(`import { getVtexConfig } from "@decocms/apps-vtex";`);
|
|
41
|
+
lines.push(`import { createVtexCommerceLoaders, createCachedPDPLoader } from "@decocms/apps-vtex/commerceLoaders";`);
|
|
42
42
|
if (hasAutocomplete) {
|
|
43
|
-
lines.push(`import { autocompleteSearch } from "@decocms/apps
|
|
43
|
+
lines.push(`import { autocompleteSearch } from "@decocms/apps-vtex/loaders/autocomplete";`);
|
|
44
44
|
}
|
|
45
|
-
lines.push(`import { getAddressByPostalCode } from "@decocms/apps
|
|
46
|
-
lines.push(`import { createAddressFromRequest, updateAddressFromRequest, deleteAddressFromRequest } from "@decocms/apps
|
|
47
|
-
lines.push(`import { updateProfileFromRequest, newsletterProfileFromRequest, deletePaymentFromRequest, getPasswordLastUpdate } from "@decocms/apps
|
|
48
|
-
lines.push(`import { createCachedLoader } from "@decocms/
|
|
45
|
+
lines.push(`import { getAddressByPostalCode } from "@decocms/apps-vtex/loaders/address";`);
|
|
46
|
+
lines.push(`import { createAddressFromRequest, updateAddressFromRequest, deleteAddressFromRequest } from "@decocms/apps-vtex/actions/address";`);
|
|
47
|
+
lines.push(`import { updateProfileFromRequest, newsletterProfileFromRequest, deletePaymentFromRequest, getPasswordLastUpdate } from "@decocms/apps-vtex/actions/profile";`);
|
|
48
|
+
lines.push(`import { createCachedLoader } from "@decocms/blocks/sdk/cachedLoader";`);
|
|
49
49
|
|
|
50
50
|
if (hasVtexAuth) {
|
|
51
51
|
lines.push(`import vtexAuthLoader from "../loaders/vtex-auth-loader";`);
|
|
@@ -92,7 +92,7 @@ export function generateCommerceLoaders(ctx: MigrationContext): string {
|
|
|
92
92
|
|
|
93
93
|
// Autocomplete aliases
|
|
94
94
|
if (hasAutocomplete) {
|
|
95
|
-
lines.push(` // Autocomplete search — from @decocms/apps`);
|
|
95
|
+
lines.push(` // Autocomplete search — from @decocms/apps-vtex`);
|
|
96
96
|
lines.push(` "site/loaders/search/intelligenseSearch.ts": cachedAutocomplete,`);
|
|
97
97
|
lines.push(` "site/loaders/search/intelligenseSearch": cachedAutocomplete,`);
|
|
98
98
|
lines.push(``);
|
|
@@ -109,7 +109,7 @@ export function generateCommerceLoaders(ctx: MigrationContext): string {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// VTEX address CRUD
|
|
112
|
-
lines.push(` // VTEX address CRUD — request-aware wrappers from @decocms/apps`);
|
|
112
|
+
lines.push(` // VTEX address CRUD — request-aware wrappers from @decocms/apps-vtex`);
|
|
113
113
|
lines.push(` "vtex/actions/address/createAddress": createAddressFromRequest as any,`);
|
|
114
114
|
lines.push(` "vtex/actions/address/updateAddress": updateAddressFromRequest as any,`);
|
|
115
115
|
lines.push(` "vtex/actions/address/deleteAddress": deleteAddressFromRequest as any,`);
|
|
@@ -215,7 +215,7 @@ export function generateCommerceLoaders(ctx: MigrationContext): string {
|
|
|
215
215
|
|
|
216
216
|
lines.push(``);
|
|
217
217
|
// Profile actions
|
|
218
|
-
lines.push(` // Profile actions — request-aware wrappers from @decocms/apps`);
|
|
218
|
+
lines.push(` // Profile actions — request-aware wrappers from @decocms/apps-vtex`);
|
|
219
219
|
lines.push(` "vtex/actions/profile/updateProfile": updateProfileFromRequest as any,`);
|
|
220
220
|
lines.push(` "vtex/actions/profile/updateProfile.ts": updateProfileFromRequest as any,`);
|
|
221
221
|
lines.push(` "vtex/actions/profile/newsletterProfile": newsletterProfileFromRequest as any,`);
|
|
@@ -43,6 +43,12 @@ describe("generateMigrationPolicyPointerRule", () => {
|
|
|
43
43
|
expect(body.length).toBeLessThan(3000);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
it("does not emit the pre-split @decocms/start or @decocms/apps package names", () => {
|
|
47
|
+
expect(body).not.toContain("@decocms/start");
|
|
48
|
+
expect(body).not.toContain("@decocms/apps\`");
|
|
49
|
+
expect(body).toContain("@decocms/blocks-cli");
|
|
50
|
+
});
|
|
51
|
+
|
|
46
52
|
it("is deterministic — same site name, same output", () => {
|
|
47
53
|
const a = generateMigrationPolicyPointerRule("foo");
|
|
48
54
|
const b = generateMigrationPolicyPointerRule("foo");
|
|
@@ -26,10 +26,11 @@ alwaysApply: true
|
|
|
26
26
|
|
|
27
27
|
# Migration Tooling Policy — Pointer
|
|
28
28
|
|
|
29
|
-
> This site (\`${siteName}\`) was generated by the \`@decocms/
|
|
30
|
-
> migration script. The canonical policy that governs how
|
|
31
|
-
> tooling, framework (\`@decocms/
|
|
32
|
-
> (\`@decocms/apps
|
|
29
|
+
> This site (\`${siteName}\`) was generated by the \`@decocms/blocks-cli\`
|
|
30
|
+
> migration script (\`deco-migrate\`). The canonical policy that governs how
|
|
31
|
+
> the migration tooling, framework (\`@decocms/blocks\` + \`@decocms/tanstack\`),
|
|
32
|
+
> and commerce layer (\`@decocms/apps-*\`) evolve lives **upstream**, not in
|
|
33
|
+
> this repo.
|
|
33
34
|
|
|
34
35
|
## Where to read
|
|
35
36
|
|
|
@@ -38,24 +39,24 @@ alwaysApply: true
|
|
|
38
39
|
- **Plan (living tracker, decisions + waves):**
|
|
39
40
|
https://github.com/decocms/deco-start/blob/main/MIGRATION_TOOLING_PLAN.md
|
|
40
41
|
- **Migration skill (phase playbook):**
|
|
41
|
-
|
|
42
|
+
https://github.com/decocms/deco-start/blob/main/.agents/skills/deco-to-tanstack-migration/SKILL.md
|
|
42
43
|
|
|
43
44
|
## What you need to know in this site
|
|
44
45
|
|
|
45
46
|
| ID | Decision | What it means here |
|
|
46
47
|
|----|----------|--------------------|
|
|
47
|
-
| **D1** | Force convergence — no fork runtime support | Site customisations live in \`src/apps/local/\` or open a PR to \`@decocms/apps
|
|
48
|
+
| **D1** | Force convergence — no fork runtime support | Site customisations live in \`src/apps/local/\` or open a PR to \`@decocms/apps-*\`. Don't wrap framework/commerce code in soft adapters. |
|
|
48
49
|
| **D2** | Rewrite HTMX on migration | If you find HTMX residue, rewrite to React. Don't bring back \`hx-*\` runtime. |
|
|
49
|
-
| **D3** | Generated stubs throw at runtime | If a \`~/lib/vtex-*\` import comes from a stub that returns \`null\` / \`{}\` / identity-cast, replace it. \`npx -p @decocms/
|
|
50
|
-
| **D4** | Site-local apps by default, promote at 3+ sites | Don't try to upstream a pattern that has only shipped here. Build it twice in different sites first, then PR it to \`@decocms/apps
|
|
50
|
+
| **D3** | Generated stubs throw at runtime | If a \`~/lib/vtex-*\` import comes from a stub that returns \`null\` / \`{}\` / identity-cast, replace it. \`npx -p @decocms/blocks-cli deco-post-cleanup --fix\` does the safe swaps automatically. |
|
|
51
|
+
| **D4** | Site-local apps by default, promote at 3+ sites | Don't try to upstream a pattern that has only shipped here. Build it twice in different sites first, then PR it to \`@decocms/apps-*\`. |
|
|
51
52
|
| **D5** | Failed migrations: \`rm -rf\` and re-run | No restart-mode magic. If the migration goes sideways, blow away the working tree and run again. |
|
|
52
53
|
|
|
53
54
|
## How to find issues this rule wants you to fix
|
|
54
55
|
|
|
55
56
|
\`\`\`bash
|
|
56
|
-
npx -p @decocms/
|
|
57
|
-
npx -p @decocms/
|
|
58
|
-
npx -p @decocms/
|
|
57
|
+
npx -p @decocms/blocks-cli deco-post-cleanup # audit only
|
|
58
|
+
npx -p @decocms/blocks-cli deco-post-cleanup --fix # auto-fix the safe rules
|
|
59
|
+
npx -p @decocms/blocks-cli deco-post-cleanup --strict # exit 1 on any finding (CI)
|
|
59
60
|
\`\`\`
|
|
60
61
|
|
|
61
62
|
## Process
|
|
@@ -44,12 +44,12 @@ describe("generateHooks (vtex)", () => {
|
|
|
44
44
|
const code = files["src/hooks/useCart.ts"];
|
|
45
45
|
// Imports from the framework factory.
|
|
46
46
|
expect(code).toContain(
|
|
47
|
-
'import { createUseCart } from "@decocms/apps
|
|
47
|
+
'import { createUseCart } from "@decocms/apps-vtex/hooks/createUseCart"',
|
|
48
48
|
);
|
|
49
49
|
expect(code).toContain('import { invoke } from "~/server/invoke"');
|
|
50
|
-
// Re-exports types from @decocms/apps directly.
|
|
50
|
+
// Re-exports types from @decocms/apps-vtex directly.
|
|
51
51
|
expect(code).toContain(
|
|
52
|
-
'export type { OrderForm, OrderFormItem } from "@decocms/apps
|
|
52
|
+
'export type { OrderForm, OrderFormItem } from "@decocms/apps-vtex/types"',
|
|
53
53
|
);
|
|
54
54
|
// Calls the factory with invoke and destructures the public API.
|
|
55
55
|
expect(code).toContain(
|
|
@@ -70,11 +70,11 @@ describe("generateHooks (vtex)", () => {
|
|
|
70
70
|
it("useUser is the createUseUser factory shim (no signal-stub boilerplate)", () => {
|
|
71
71
|
const code = files["src/hooks/useUser.ts"];
|
|
72
72
|
expect(code).toContain(
|
|
73
|
-
'import { createUseUser } from "@decocms/apps
|
|
73
|
+
'import { createUseUser } from "@decocms/apps-vtex/hooks/createUseUser"',
|
|
74
74
|
);
|
|
75
75
|
expect(code).toContain('import { invoke } from "~/server/invoke"');
|
|
76
76
|
expect(code).toContain(
|
|
77
|
-
'export type { Person } from "@decocms/apps
|
|
77
|
+
'export type { Person } from "@decocms/apps-vtex/loaders/user"',
|
|
78
78
|
);
|
|
79
79
|
expect(code).toContain("export const { useUser, resetUser } = createUseUser");
|
|
80
80
|
// Must NOT scaffold the legacy signal stub.
|
|
@@ -85,11 +85,11 @@ describe("generateHooks (vtex)", () => {
|
|
|
85
85
|
it("useWishlist is the createUseWishlist factory shim", () => {
|
|
86
86
|
const code = files["src/hooks/useWishlist.ts"];
|
|
87
87
|
expect(code).toContain(
|
|
88
|
-
'import { createUseWishlist } from "@decocms/apps
|
|
88
|
+
'import { createUseWishlist } from "@decocms/apps-vtex/hooks/createUseWishlist"',
|
|
89
89
|
);
|
|
90
90
|
expect(code).toContain('import { invoke } from "~/server/invoke"');
|
|
91
91
|
expect(code).toContain(
|
|
92
|
-
'export type { WishlistItem } from "@decocms/apps
|
|
92
|
+
'export type { WishlistItem } from "@decocms/apps-vtex/loaders/wishlist"',
|
|
93
93
|
);
|
|
94
94
|
expect(code).toContain(
|
|
95
95
|
"export const { useWishlist, resetWishlist } = createUseWishlist",
|
|
@@ -19,11 +19,11 @@ export function generateHooks(ctx: MigrationContext): Record<string, string> {
|
|
|
19
19
|
function generateVtexUseCart(): string {
|
|
20
20
|
// The legacy invoke-based useCart hook is now a 5-line factory call —
|
|
21
21
|
// the heavy lifting (singleton state, listener pattern, async actions,
|
|
22
|
-
// analytics helpers) lives in @decocms/apps
|
|
23
|
-
return `import { createUseCart } from "@decocms/apps
|
|
22
|
+
// analytics helpers) lives in @decocms/apps-vtex/hooks/createUseCart.
|
|
23
|
+
return `import { createUseCart } from "@decocms/apps-vtex/hooks/createUseCart";
|
|
24
24
|
import { invoke } from "~/server/invoke";
|
|
25
25
|
|
|
26
|
-
export type { OrderForm, OrderFormItem } from "@decocms/apps
|
|
26
|
+
export type { OrderForm, OrderFormItem } from "@decocms/apps-vtex/types";
|
|
27
27
|
|
|
28
28
|
export const { useCart, resetCart, itemToAnalyticsItem } = createUseCart({
|
|
29
29
|
invoke,
|
|
@@ -70,22 +70,22 @@ export default useCart;
|
|
|
70
70
|
// VTEX path — these are five-line factory shims. The heavy lifting
|
|
71
71
|
// (singleton state, listener pattern, async actions, signal-shaped
|
|
72
72
|
// accessors, legacy arg-swap conventions) lives in
|
|
73
|
-
// @decocms/apps
|
|
73
|
+
// @decocms/apps-vtex/hooks/createUseUser and createUseWishlist.
|
|
74
74
|
function generateVtexUseUser(): string {
|
|
75
|
-
return `import { createUseUser } from "@decocms/apps
|
|
75
|
+
return `import { createUseUser } from "@decocms/apps-vtex/hooks/createUseUser";
|
|
76
76
|
import { invoke } from "~/server/invoke";
|
|
77
77
|
|
|
78
|
-
export type { Person } from "@decocms/apps
|
|
78
|
+
export type { Person } from "@decocms/apps-vtex/loaders/user";
|
|
79
79
|
|
|
80
80
|
export const { useUser, resetUser } = createUseUser({ invoke });
|
|
81
81
|
`;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function generateVtexUseWishlist(): string {
|
|
85
|
-
return `import { createUseWishlist } from "@decocms/apps
|
|
85
|
+
return `import { createUseWishlist } from "@decocms/apps-vtex/hooks/createUseWishlist";
|
|
86
86
|
import { invoke } from "~/server/invoke";
|
|
87
87
|
|
|
88
|
-
export type { WishlistItem } from "@decocms/apps
|
|
88
|
+
export type { WishlistItem } from "@decocms/apps-vtex/loaders/wishlist";
|
|
89
89
|
|
|
90
90
|
export const { useWishlist, resetWishlist } = createUseWishlist({ invoke });
|
|
91
91
|
`;
|
|
@@ -98,7 +98,7 @@ function generateGenericUseUser(): string {
|
|
|
98
98
|
return `/**
|
|
99
99
|
* User Hook — wire to invoke.site.loaders for your platform's user API.
|
|
100
100
|
*
|
|
101
|
-
* VTEX sites get a real factory from @decocms/apps
|
|
101
|
+
* VTEX sites get a real factory from @decocms/apps-vtex/hooks/createUseUser;
|
|
102
102
|
* see migration template hooks.ts for the canonical five-line shim.
|
|
103
103
|
*/
|
|
104
104
|
import { signal } from "~/sdk/signal";
|
|
@@ -125,7 +125,7 @@ function generateGenericUseWishlist(): string {
|
|
|
125
125
|
return `/**
|
|
126
126
|
* Wishlist Hook — wire to invoke.site.loaders/actions for your platform.
|
|
127
127
|
*
|
|
128
|
-
* VTEX sites get a real factory from @decocms/apps
|
|
128
|
+
* VTEX sites get a real factory from @decocms/apps-vtex/hooks/createUseWishlist;
|
|
129
129
|
* see migration template hooks.ts for the canonical five-line shim.
|
|
130
130
|
*/
|
|
131
131
|
import { signal } from "~/sdk/signal";
|
|
@@ -99,7 +99,7 @@ describe("D3 — generated stubs throw at runtime", () => {
|
|
|
99
99
|
it("vtex-transform.toProduct throws and points at the canonical path", () => {
|
|
100
100
|
const src = LIB_TEMPLATES["src/lib/vtex-transform.ts"];
|
|
101
101
|
expect(src).toMatch(/throw new Error/);
|
|
102
|
-
expect(src).toMatch(/@decocms\/apps
|
|
102
|
+
expect(src).toMatch(/@decocms\/apps-vtex\/utils\/transform/);
|
|
103
103
|
expect(src).toMatch(/\[deco-migrate\]/);
|
|
104
104
|
});
|
|
105
105
|
|
|
@@ -117,7 +117,7 @@ describe("D3 — generated stubs throw at runtime", () => {
|
|
|
117
117
|
const src = LIB_TEMPLATES["src/lib/vtex-segment.ts"];
|
|
118
118
|
expect(src).toMatch(/getSegmentFromBag[\s\S]*?throw new Error/);
|
|
119
119
|
expect(src).toMatch(/withSegmentCookie[\s\S]*?throw new Error/);
|
|
120
|
-
expect(src).toMatch(/@decocms\/apps
|
|
120
|
+
expect(src).toMatch(/@decocms\/apps-vtex\/utils\/segment/);
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
it("non-stub helpers stay implemented (negative check — no throw)", () => {
|
|
@@ -46,11 +46,11 @@ export function selectImportedLibTemplates(
|
|
|
46
46
|
//
|
|
47
47
|
// Each thrown message points at the canonical replacement so the fix
|
|
48
48
|
// is mechanical. `deco-post-cleanup --fix` automates the swap.
|
|
49
|
-
const LIB_VTEX_TRANSFORM = `import type { Product } from "@decocms/apps
|
|
49
|
+
const LIB_VTEX_TRANSFORM = `import type { Product } from "@decocms/apps-commerce/types";
|
|
50
50
|
|
|
51
51
|
const STUB =
|
|
52
52
|
"[deco-migrate] \`~/lib/vtex-transform.toProduct\` is a generated stub. " +
|
|
53
|
-
"Replace with: import { toProduct } from '@decocms/apps
|
|
53
|
+
"Replace with: import { toProduct } from '@decocms/apps-vtex/utils/transform' " +
|
|
54
54
|
"(canonical signature: \`toProduct(product, sku, level, options)\`). " +
|
|
55
55
|
"Run \`deco-post-cleanup --fix\` or see the deco-to-tanstack-migration skill " +
|
|
56
56
|
"(post-migration-cleanup § 5).";
|
|
@@ -119,13 +119,13 @@ const LIB_VTEX_SEGMENT = `// Per the migration tooling policy (D3): both these s
|
|
|
119
119
|
const STUB_GET_SEGMENT_FROM_BAG =
|
|
120
120
|
"[deco-migrate] \`~/lib/vtex-segment.getSegmentFromBag\` is a generated " +
|
|
121
121
|
"stub. Refactor: read cookies via \`request.headers.get('cookie')\` then " +
|
|
122
|
-
"call \`buildSegmentFromCookies()\` from '@decocms/apps
|
|
122
|
+
"call \`buildSegmentFromCookies()\` from '@decocms/apps-vtex/utils/segment'. " +
|
|
123
123
|
"The bag-based lookup mechanism does not exist on TanStack Start.";
|
|
124
124
|
|
|
125
125
|
const STUB_WITH_SEGMENT_COOKIE =
|
|
126
126
|
"[deco-migrate] \`~/lib/vtex-segment.withSegmentCookie\` is a generated " +
|
|
127
127
|
"stub. Replace with: import { withSegmentCookie } from " +
|
|
128
|
-
"'@decocms/apps
|
|
128
|
+
"'@decocms/apps-vtex/utils/segment' (canonical signature: " +
|
|
129
129
|
"\`withSegmentCookie(segment, headers?)\`). Run \`deco-post-cleanup --fix\` " +
|
|
130
130
|
"or see the deco-to-tanstack-migration skill.";
|
|
131
131
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createContext } from "../types";
|
|
3
|
+
import type { MigrationContext } from "../types";
|
|
4
|
+
import { generateRoutes } from "./routes";
|
|
5
|
+
import { generateServerEntry } from "./server-entry";
|
|
6
|
+
import { generateSetup } from "./setup";
|
|
7
|
+
import { generateViteConfig } from "./vite-config";
|
|
8
|
+
import { generateCommerceLoaders } from "./commerce-loaders";
|
|
9
|
+
import { generateSectionLoaders } from "./section-loaders";
|
|
10
|
+
import { generateHooks } from "./hooks";
|
|
11
|
+
import { generateUiComponents } from "./ui-components";
|
|
12
|
+
import { generateTypeFiles } from "./types-gen";
|
|
13
|
+
import { generateCacheConfig } from "./cache-config";
|
|
14
|
+
import { generateSdkFiles } from "./sdk-gen";
|
|
15
|
+
import { generatePackageJson } from "./package-json";
|
|
16
|
+
import { generateMigrationPolicyPointerRule } from "./cursor-rules";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Fleet-wide regression guard: none of the scaffolder's templates may emit
|
|
20
|
+
* the pre-7.x-split `@decocms/start` package, or a bare `@decocms/apps/`
|
|
21
|
+
* monolith subpath import — neither exists for 7.x consumers, so any
|
|
22
|
+
* occurrence here means a freshly scaffolded site ships broken code.
|
|
23
|
+
*
|
|
24
|
+
* `@decocms/apps-<platform>` (the current split packages) are fine and
|
|
25
|
+
* intentionally NOT flagged by this check.
|
|
26
|
+
*/
|
|
27
|
+
function assertNoLegacyPackageNames(label: string, output: string) {
|
|
28
|
+
expect(output, `${label} must not emit "@decocms/start"`).not.toContain(
|
|
29
|
+
"@decocms/start",
|
|
30
|
+
);
|
|
31
|
+
expect(
|
|
32
|
+
output,
|
|
33
|
+
`${label} must not emit the "@decocms/apps/" monolith subpath`,
|
|
34
|
+
).not.toMatch(/@decocms\/apps\//);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeCtx(platform: MigrationContext["platform"]): MigrationContext {
|
|
38
|
+
const ctx = createContext("/tmp/no-legacy-packages-fixture-site");
|
|
39
|
+
ctx.siteName = "acme-storefront";
|
|
40
|
+
ctx.platform = platform;
|
|
41
|
+
ctx.vtexAccount = platform === "vtex" ? "acme" : null;
|
|
42
|
+
return ctx;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("scaffolder templates never emit @decocms/start or @decocms/apps/*", () => {
|
|
46
|
+
for (const platform of ["vtex", "custom"] as const) {
|
|
47
|
+
describe(`platform: ${platform}`, () => {
|
|
48
|
+
const ctx = makeCtx(platform);
|
|
49
|
+
|
|
50
|
+
it("routes.ts", () => {
|
|
51
|
+
const files = generateRoutes(ctx);
|
|
52
|
+
for (const [path, content] of Object.entries(files)) {
|
|
53
|
+
assertNoLegacyPackageNames(`routes.ts (${path})`, content);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("server-entry.ts", () => {
|
|
58
|
+
const files = generateServerEntry(ctx);
|
|
59
|
+
for (const [path, content] of Object.entries(files)) {
|
|
60
|
+
assertNoLegacyPackageNames(`server-entry.ts (${path})`, content);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("setup.ts", () => {
|
|
65
|
+
assertNoLegacyPackageNames("setup.ts", generateSetup(ctx));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("vite-config.ts", () => {
|
|
69
|
+
assertNoLegacyPackageNames("vite-config.ts", generateViteConfig(ctx));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("commerce-loaders.ts", () => {
|
|
73
|
+
assertNoLegacyPackageNames(
|
|
74
|
+
"commerce-loaders.ts",
|
|
75
|
+
generateCommerceLoaders(ctx),
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("section-loaders.ts", () => {
|
|
80
|
+
assertNoLegacyPackageNames(
|
|
81
|
+
"section-loaders.ts",
|
|
82
|
+
generateSectionLoaders(ctx),
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("hooks.ts", () => {
|
|
87
|
+
const files = generateHooks(ctx);
|
|
88
|
+
for (const [path, content] of Object.entries(files)) {
|
|
89
|
+
assertNoLegacyPackageNames(`hooks.ts (${path})`, content);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("types-gen.ts", () => {
|
|
94
|
+
const files = generateTypeFiles(ctx);
|
|
95
|
+
for (const [path, content] of Object.entries(files)) {
|
|
96
|
+
assertNoLegacyPackageNames(`types-gen.ts (${path})`, content);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("cache-config.ts", () => {
|
|
101
|
+
assertNoLegacyPackageNames(
|
|
102
|
+
"cache-config.ts",
|
|
103
|
+
generateCacheConfig(ctx),
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("sdk-gen.ts", () => {
|
|
108
|
+
const files = generateSdkFiles(ctx);
|
|
109
|
+
for (const [path, content] of Object.entries(files)) {
|
|
110
|
+
assertNoLegacyPackageNames(`sdk-gen.ts (${path})`, content);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Platform-agnostic templates
|
|
117
|
+
it("ui-components.ts", () => {
|
|
118
|
+
const ctx = makeCtx("custom");
|
|
119
|
+
const files = generateUiComponents(ctx);
|
|
120
|
+
for (const [path, content] of Object.entries(files)) {
|
|
121
|
+
assertNoLegacyPackageNames(`ui-components.ts (${path})`, content);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("cursor-rules.ts", () => {
|
|
126
|
+
assertNoLegacyPackageNames(
|
|
127
|
+
"cursor-rules.ts",
|
|
128
|
+
generateMigrationPolicyPointerRule("acme-storefront"),
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// package-json.ts shells out to `npm view` to resolve the latest published
|
|
133
|
+
// version, with a hardcoded fallback if that fails (offline/CI-sandboxed).
|
|
134
|
+
// Either way the emitted dependency *names* must never be the retired
|
|
135
|
+
// @decocms/start / @decocms/apps monolith.
|
|
136
|
+
it("package-json.ts", () => {
|
|
137
|
+
const ctx = makeCtx("vtex");
|
|
138
|
+
const pkg = generatePackageJson(ctx);
|
|
139
|
+
assertNoLegacyPackageNames("package-json.ts", pkg);
|
|
140
|
+
const parsed = JSON.parse(pkg);
|
|
141
|
+
expect(parsed.dependencies).not.toHaveProperty("@decocms/start");
|
|
142
|
+
expect(parsed.dependencies).not.toHaveProperty("@decocms/apps");
|
|
143
|
+
expect(parsed.dependencies).toHaveProperty("@decocms/blocks");
|
|
144
|
+
expect(parsed.dependencies).toHaveProperty("@decocms/tanstack");
|
|
145
|
+
expect(parsed.dependencies).toHaveProperty("@decocms/apps-vtex");
|
|
146
|
+
}, 20000);
|
|
147
|
+
});
|
|
@@ -95,9 +95,19 @@ export function generatePackageJson(ctx: MigrationContext): string {
|
|
|
95
95
|
|
|
96
96
|
const siteDeps = extractedDeps;
|
|
97
97
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
// @decocms/blocks, @decocms/blocks-admin, @decocms/blocks-cli,
|
|
99
|
+
// @decocms/tanstack, and the @decocms/apps-* platform packages are
|
|
100
|
+
// published in lockstep from the same monorepo release, so a single
|
|
101
|
+
// version lookup covers all of them (mirrors how consumer sites bump
|
|
102
|
+
// "@decocms/blocks/blocks-admin/nextjs ranges" together).
|
|
103
|
+
const frameworkVersion = getLatestVersion("@decocms/blocks", "7.5.1");
|
|
104
|
+
|
|
105
|
+
const platformPackage: Partial<Record<typeof ctx.platform, string>> = {
|
|
106
|
+
vtex: "@decocms/apps-vtex",
|
|
107
|
+
shopify: "@decocms/apps-shopify",
|
|
108
|
+
magento: "@decocms/apps-magento",
|
|
109
|
+
};
|
|
110
|
+
const platformDep = platformPackage[ctx.platform];
|
|
101
111
|
|
|
102
112
|
const pkg = {
|
|
103
113
|
name: ctx.siteName,
|
|
@@ -109,14 +119,14 @@ export function generatePackageJson(ctx: MigrationContext): string {
|
|
|
109
119
|
"dev:clean":
|
|
110
120
|
"rm -rf node_modules/.vite .wrangler/state .tanstack && vite dev",
|
|
111
121
|
"generate:blocks":
|
|
112
|
-
"tsx node_modules/@decocms/
|
|
122
|
+
"tsx node_modules/@decocms/blocks-cli/scripts/generate-blocks.ts",
|
|
113
123
|
"generate:routes": "tsr generate",
|
|
114
|
-
"generate:schema": `tsx node_modules/@decocms/
|
|
124
|
+
"generate:schema": `tsx node_modules/@decocms/blocks-cli/scripts/generate-schema.ts --site ${ctx.siteName}`,
|
|
115
125
|
"generate:invoke":
|
|
116
|
-
"tsx node_modules/@decocms/
|
|
126
|
+
"tsx node_modules/@decocms/blocks-cli/scripts/generate-invoke.ts",
|
|
117
127
|
"generate:sections":
|
|
118
|
-
"tsx node_modules/@decocms/
|
|
119
|
-
"generate:loaders": `tsx node_modules/@decocms/
|
|
128
|
+
"tsx node_modules/@decocms/blocks-cli/scripts/generate-sections.ts",
|
|
129
|
+
"generate:loaders": `tsx node_modules/@decocms/blocks-cli/scripts/generate-loaders.ts --exclude vtex/loaders,vtex/actions,loaders/vtex-auth-loader,loaders/reviews/productReviews,loaders/product/buyTogether,loaders/search/productListPageCollection,loaders/search/intelligenseSearch,loaders/Layouts/ProductCard`,
|
|
120
130
|
build:
|
|
121
131
|
"npm run generate:blocks && npm run generate:sections && npm run generate:loaders && npm run generate:schema && npm run generate:invoke && tsr generate && vite build",
|
|
122
132
|
preview: "vite preview",
|
|
@@ -141,8 +151,11 @@ export function generatePackageJson(ctx: MigrationContext): string {
|
|
|
141
151
|
license: "MIT",
|
|
142
152
|
packageManager: `bun@${CANONICAL_BUN_VERSION}`,
|
|
143
153
|
dependencies: {
|
|
144
|
-
"@decocms/
|
|
145
|
-
"@decocms/
|
|
154
|
+
"@decocms/blocks": `^${frameworkVersion}`,
|
|
155
|
+
"@decocms/blocks-admin": `^${frameworkVersion}`,
|
|
156
|
+
"@decocms/tanstack": `^${frameworkVersion}`,
|
|
157
|
+
"@decocms/apps-commerce": `^${frameworkVersion}`,
|
|
158
|
+
...(platformDep ? { [platformDep]: `^${frameworkVersion}` } : {}),
|
|
146
159
|
"@tanstack/react-query": "5.90.21",
|
|
147
160
|
"@tanstack/react-router": "1.166.7",
|
|
148
161
|
"@tanstack/react-start": "1.166.8",
|
|
@@ -155,6 +168,7 @@ export function generatePackageJson(ctx: MigrationContext): string {
|
|
|
155
168
|
},
|
|
156
169
|
devDependencies: {
|
|
157
170
|
"@cloudflare/vite-plugin": "^1.27.0",
|
|
171
|
+
"@decocms/blocks-cli": `^${frameworkVersion}`,
|
|
158
172
|
"@tailwindcss/vite": "^4.2.1",
|
|
159
173
|
"@tanstack/router-cli": "1.166.7",
|
|
160
174
|
"@types/react": "^19.2.14",
|
|
@@ -63,7 +63,7 @@ function generateRoot(ctx: MigrationContext, siteTitle: string, vtexAccount: str
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
return `import { createRootRoute } from "@tanstack/react-router";
|
|
66
|
-
import { DecoRootLayout } from "@decocms/
|
|
66
|
+
import { DecoRootLayout } from "@decocms/tanstack";
|
|
67
67
|
// @ts-ignore Vite ?url import
|
|
68
68
|
import appCss from "../styles/app.css?url";
|
|
69
69
|
|
|
@@ -107,8 +107,7 @@ function RootLayout() {
|
|
|
107
107
|
|
|
108
108
|
function generateIndex(ctx: MigrationContext, siteTitle: string): string {
|
|
109
109
|
return `import { createFileRoute } from "@tanstack/react-router";
|
|
110
|
-
import { cmsHomeRouteConfig,
|
|
111
|
-
import { DecoPageRenderer } from "@decocms/start/hooks";
|
|
110
|
+
import { cmsHomeRouteConfig, DecoPageRenderer, loadDeferredSection } from "@decocms/tanstack";
|
|
112
111
|
|
|
113
112
|
// MIGRATION TODO: customize defaultTitle / defaultDescription / fallback
|
|
114
113
|
// copy below for ${siteTitle}. CMS \`Site.seo\` overrides these once block
|
|
@@ -144,7 +143,7 @@ function HomePage() {
|
|
|
144
143
|
deferredPromises={data.deferredPromises}
|
|
145
144
|
pagePath={data.pagePath}
|
|
146
145
|
pageUrl={data.pageUrl}
|
|
147
|
-
loadDeferredSectionFn={
|
|
146
|
+
loadDeferredSectionFn={loadDeferredSection}
|
|
148
147
|
/>
|
|
149
148
|
);
|
|
150
149
|
}
|
|
@@ -153,8 +152,7 @@ function HomePage() {
|
|
|
153
152
|
|
|
154
153
|
function generateCatchAll(ctx: MigrationContext, siteTitle: string): string {
|
|
155
154
|
return `import { createFileRoute } from "@tanstack/react-router";
|
|
156
|
-
import { cmsRouteConfig,
|
|
157
|
-
import { DecoPageRenderer } from "@decocms/start/hooks";
|
|
155
|
+
import { cmsRouteConfig, DecoPageRenderer, loadDeferredSection } from "@decocms/tanstack";
|
|
158
156
|
|
|
159
157
|
// MIGRATION TODO: customize defaultTitle / defaultDescription for ${siteTitle}
|
|
160
158
|
// (CMS \`Site.seo\` overrides these per-page once block resolution kicks in).
|
|
@@ -182,7 +180,7 @@ function CmsPage() {
|
|
|
182
180
|
deferredPromises={data.deferredPromises}
|
|
183
181
|
pagePath={data.pagePath}
|
|
184
182
|
pageUrl={data.pageUrl}
|
|
185
|
-
loadDeferredSectionFn={
|
|
183
|
+
loadDeferredSectionFn={loadDeferredSection}
|
|
186
184
|
/>
|
|
187
185
|
);
|
|
188
186
|
}
|
|
@@ -214,7 +212,7 @@ function NotFoundPage() {
|
|
|
214
212
|
|
|
215
213
|
function generateDecoMeta(): string {
|
|
216
214
|
return `import { createFileRoute } from "@tanstack/react-router";
|
|
217
|
-
import { decoMetaRoute } from "@decocms/
|
|
215
|
+
import { decoMetaRoute } from "@decocms/tanstack";
|
|
218
216
|
|
|
219
217
|
export const Route = createFileRoute("/deco/meta")(decoMetaRoute);
|
|
220
218
|
`;
|
|
@@ -222,7 +220,7 @@ export const Route = createFileRoute("/deco/meta")(decoMetaRoute);
|
|
|
222
220
|
|
|
223
221
|
function generateDecoInvoke(): string {
|
|
224
222
|
return `import { createFileRoute } from "@tanstack/react-router";
|
|
225
|
-
import { decoInvokeRoute } from "@decocms/
|
|
223
|
+
import { decoInvokeRoute } from "@decocms/tanstack";
|
|
226
224
|
|
|
227
225
|
export const Route = createFileRoute("/deco/invoke/$")(decoInvokeRoute);
|
|
228
226
|
`;
|
|
@@ -230,7 +228,7 @@ export const Route = createFileRoute("/deco/invoke/$")(decoInvokeRoute);
|
|
|
230
228
|
|
|
231
229
|
function generateDecoRender(): string {
|
|
232
230
|
return `import { createFileRoute } from "@tanstack/react-router";
|
|
233
|
-
import { decoRenderRoute } from "@decocms/
|
|
231
|
+
import { decoRenderRoute } from "@decocms/tanstack";
|
|
234
232
|
|
|
235
233
|
export const Route = createFileRoute("/deco/render")(decoRenderRoute);
|
|
236
234
|
`;
|
|
@@ -12,7 +12,7 @@ export function generateSdkFiles(ctx: MigrationContext): Record<string, string>
|
|
|
12
12
|
function generateDeviceServer(): string {
|
|
13
13
|
return `import { createServerFn } from "@tanstack/react-start";
|
|
14
14
|
import { getRequestHeader } from "@tanstack/react-start/server";
|
|
15
|
-
import { detectDevice } from "@decocms/
|
|
15
|
+
import { detectDevice } from "@decocms/blocks/sdk/useDevice";
|
|
16
16
|
|
|
17
17
|
export const getDeviceFromServer = createServerFn({ method: "GET" }).handler(
|
|
18
18
|
async () => {
|
|
@@ -71,22 +71,22 @@ export function generateSectionLoaders(ctx: MigrationContext): string {
|
|
|
71
71
|
lines.push(` withSearchParam,`);
|
|
72
72
|
lines.push(` withSectionLoader,`);
|
|
73
73
|
lines.push(` compose,`);
|
|
74
|
-
lines.push(`} from "@decocms/
|
|
74
|
+
lines.push(`} from "@decocms/blocks/cms";`);
|
|
75
75
|
|
|
76
76
|
if (hasSearchResult) {
|
|
77
|
-
lines.push(`import { detectDevice } from "@decocms/
|
|
77
|
+
lines.push(`import { detectDevice } from "@decocms/blocks/sdk/useDevice";`);
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
if (isVtex) {
|
|
81
|
-
lines.push(`import { getVtexConfig } from "@decocms/apps
|
|
81
|
+
lines.push(`import { getVtexConfig } from "@decocms/apps-vtex";`);
|
|
82
82
|
if (hasWishlistSection && hasWishlistLoaders) {
|
|
83
|
-
lines.push(`import { getUser } from "@decocms/apps
|
|
84
|
-
lines.push(`import { getVtexCookies } from "@decocms/apps
|
|
83
|
+
lines.push(`import { getUser } from "@decocms/apps-vtex/loaders/user";`);
|
|
84
|
+
lines.push(`import { getVtexCookies } from "@decocms/apps-vtex/utils/cookies";`);
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
if (hasAccountSections) {
|
|
89
|
-
lines.push(`import { vtexAccountLoaders } from "@decocms/apps
|
|
89
|
+
lines.push(`import { vtexAccountLoaders } from "@decocms/apps-vtex/utils/accountLoaders";`);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
if (hasProductReviewsLoader && (hasProductReviews || hasSearchResult)) {
|
|
@@ -338,7 +338,7 @@ export function generateSectionLoaders(ctx: MigrationContext): string {
|
|
|
338
338
|
// ---------- Account sections ----------
|
|
339
339
|
if (isVtex && hasAccountSections) {
|
|
340
340
|
entries.push(``);
|
|
341
|
-
entries.push(` // Account sections — via @decocms/apps factory`);
|
|
341
|
+
entries.push(` // Account sections — via @decocms/apps-vtex factory`);
|
|
342
342
|
|
|
343
343
|
for (const meta of ctx.sectionMetas) {
|
|
344
344
|
if (!meta.isAccountSection) continue;
|