@decocms/blocks-cli 7.20.4 → 7.20.5
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/migrate/phase-scaffold.ts +5 -0
- package/scripts/migrate/phase-verify.ts +2 -0
- package/scripts/migrate/templates/commerce-init.test.ts +55 -0
- package/scripts/migrate/templates/commerce-init.ts +42 -0
- package/scripts/migrate/templates/no-legacy-packages.test.ts +5 -0
- package/scripts/migrate/templates/server-entry.ts +8 -0
- package/scripts/migrate/templates/setup.ts +12 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/blocks-cli",
|
|
3
|
-
"version": "7.20.
|
|
3
|
+
"version": "7.20.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Deco codegen (generate-blocks, generate-schema, generate-invoke) and Fresh-to-TanStack migration tooling",
|
|
6
6
|
"repository": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"lint:unused": "knip"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@decocms/blocks": "7.20.
|
|
33
|
+
"@decocms/blocks": "7.20.5",
|
|
34
34
|
"ts-morph": "^27.0.0",
|
|
35
35
|
"tsx": "^4.22.5"
|
|
36
36
|
},
|
|
@@ -14,6 +14,7 @@ import { generateAppCss } from "./templates/app-css";
|
|
|
14
14
|
import { generateTypeFiles } from "./templates/types-gen";
|
|
15
15
|
import { generateUiComponents } from "./templates/ui-components";
|
|
16
16
|
import { generateHooks } from "./templates/hooks";
|
|
17
|
+
import { generateCommerceInit } from "./templates/commerce-init";
|
|
17
18
|
import { generateCommerceLoaders } from "./templates/commerce-loaders";
|
|
18
19
|
import { generateSectionLoaders } from "./templates/section-loaders";
|
|
19
20
|
import { generateCacheConfig } from "./templates/cache-config";
|
|
@@ -110,6 +111,10 @@ export function scaffold(ctx: MigrationContext): void {
|
|
|
110
111
|
writeFile(ctx, "src/setup.ts", generateSetup(ctx));
|
|
111
112
|
writeFile(ctx, "src/cache-config.ts", generateCacheConfig(ctx));
|
|
112
113
|
writeFile(ctx, "src/setup/commerce-loaders.ts", generateCommerceLoaders(ctx));
|
|
114
|
+
// Server-only registration of COMMERCE_LOADERS + invoke — imported by
|
|
115
|
+
// worker-entry.ts, not setup.ts, so the loader/action graph stays out of the
|
|
116
|
+
// client bundle.
|
|
117
|
+
writeFile(ctx, "src/setup/commerce-init.ts", generateCommerceInit(ctx));
|
|
113
118
|
writeFile(ctx, "src/setup/section-loaders.ts", generateSectionLoaders(ctx));
|
|
114
119
|
|
|
115
120
|
// Theme extraction + Styles
|
|
@@ -32,6 +32,7 @@ const REQUIRED_FILES = [
|
|
|
32
32
|
"src/setup.ts",
|
|
33
33
|
"src/cache-config.ts",
|
|
34
34
|
"src/setup/commerce-loaders.ts",
|
|
35
|
+
"src/setup/commerce-init.ts",
|
|
35
36
|
"src/setup/section-loaders.ts",
|
|
36
37
|
"src/styles/app.css",
|
|
37
38
|
"src/apps/site.ts",
|
|
@@ -417,6 +418,7 @@ export const checks: Check[] = [
|
|
|
417
418
|
"src/setup.ts",
|
|
418
419
|
"src/cache-config.ts",
|
|
419
420
|
"src/setup/commerce-loaders.ts",
|
|
421
|
+
"src/setup/commerce-init.ts",
|
|
420
422
|
"src/setup/section-loaders.ts",
|
|
421
423
|
];
|
|
422
424
|
const missing = setupFiles.filter(
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { MigrationContext } from "../types";
|
|
3
|
+
import { createContext } from "../types";
|
|
4
|
+
import { generateCommerceInit } from "./commerce-init";
|
|
5
|
+
import { generateServerEntry } from "./server-entry";
|
|
6
|
+
import { generateSetup } from "./setup";
|
|
7
|
+
|
|
8
|
+
function makeCtx(platform: MigrationContext["platform"]): MigrationContext {
|
|
9
|
+
const ctx = createContext("/tmp/commerce-init-template-fixture-site");
|
|
10
|
+
ctx.siteName = "acme-storefront";
|
|
11
|
+
ctx.platform = platform;
|
|
12
|
+
ctx.vtexAccount = platform === "vtex" ? "acme" : null;
|
|
13
|
+
return ctx;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Regression guard for the site loader/action bundle-leak boundary.
|
|
18
|
+
*
|
|
19
|
+
* COMMERCE_LOADERS (and every site loader/action module it imports) must be
|
|
20
|
+
* registered in a server-only module imported by the worker entry, NEVER by
|
|
21
|
+
* setup.ts — which router.tsx imports into the client bundle. If the
|
|
22
|
+
* registration leaks back into setup.ts, the whole loader/action graph (and any
|
|
23
|
+
* credential hardcoded in it) ships to the browser assets again.
|
|
24
|
+
*/
|
|
25
|
+
describe("server-only commerce/invoke registration boundary", () => {
|
|
26
|
+
for (const platform of ["vtex", "custom"] as const) {
|
|
27
|
+
describe(`platform: ${platform}`, () => {
|
|
28
|
+
const ctx = makeCtx(platform);
|
|
29
|
+
const commerceInit = generateCommerceInit(ctx);
|
|
30
|
+
const setup = generateSetup(ctx);
|
|
31
|
+
const serverFiles = generateServerEntry(ctx);
|
|
32
|
+
|
|
33
|
+
it("commerce-init registers COMMERCE_LOADERS + invoke server-side", () => {
|
|
34
|
+
expect(commerceInit).toContain(`import { COMMERCE_LOADERS } from "./commerce-loaders"`);
|
|
35
|
+
expect(commerceInit).toContain("registerCommerceLoaders(COMMERCE_LOADERS)");
|
|
36
|
+
expect(commerceInit).toContain("setInvokeLoaders(() => COMMERCE_LOADERS)");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("setup.ts (client-imported) does NOT import or register COMMERCE_LOADERS", () => {
|
|
40
|
+
// The doc comment may reference COMMERCE_LOADERS; what must be absent is
|
|
41
|
+
// the actual import + the server-only registration calls.
|
|
42
|
+
expect(setup).not.toContain('from "./setup/commerce-loaders"');
|
|
43
|
+
expect(setup).not.toContain("registerCommerceLoaders(");
|
|
44
|
+
expect(setup).not.toContain("setInvokeLoaders(");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("worker-entry imports commerce-init, router.tsx does not", () => {
|
|
48
|
+
expect(serverFiles["src/worker-entry.ts"]).toContain('import "./setup/commerce-init"');
|
|
49
|
+
expect(serverFiles["src/router.tsx"]).not.toContain("commerce-init");
|
|
50
|
+
// router.tsx still imports the client-safe setup for section registration.
|
|
51
|
+
expect(serverFiles["src/router.tsx"]).toContain('import "./setup"');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { MigrationContext } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Server-only commerce + invoke registration.
|
|
5
|
+
*
|
|
6
|
+
* `commerce-loaders.ts` statically imports COMMERCE_LOADERS, which pulls in the
|
|
7
|
+
* site's loader/action modules and the platform commerce loaders. If that graph
|
|
8
|
+
* is reachable from the CLIENT entry (router.tsx -> setup.ts), Vite bundles all
|
|
9
|
+
* of it — and any credential hardcoded in a site loader/action — into the
|
|
10
|
+
* browser assets. So the registration lives here instead and is imported ONLY by
|
|
11
|
+
* the worker entry (server), never by router.tsx.
|
|
12
|
+
*
|
|
13
|
+
* This is safe because both consumers run server-side:
|
|
14
|
+
* - CMS commerce-loader resolution (loadCmsPage server fn), via
|
|
15
|
+
* registerCommerceLoaders.
|
|
16
|
+
* - the /deco/invoke handler, via setInvokeLoaders(() => COMMERCE_LOADERS)
|
|
17
|
+
* -> getRegisteredLoaders() inside handleInvoke.
|
|
18
|
+
*
|
|
19
|
+
* Client components reach loaders/actions exclusively through the HTTP `invoke`
|
|
20
|
+
* proxy (@decocms/blocks/sdk/invoke), which imports no modules.
|
|
21
|
+
*/
|
|
22
|
+
export function generateCommerceInit(_ctx: MigrationContext): string {
|
|
23
|
+
return `/**
|
|
24
|
+
* Server-only commerce + invoke registration.
|
|
25
|
+
*
|
|
26
|
+
* Imported ONLY by the worker entry (src/worker-entry.ts), never by
|
|
27
|
+
* src/router.tsx. This keeps COMMERCE_LOADERS — and every site loader/action
|
|
28
|
+
* module it imports — out of the client bundle, so no server-side credential
|
|
29
|
+
* can leak into the browser assets. Both consumers run server-side:
|
|
30
|
+
* - CMS commerce-loader resolution (registerCommerceLoaders)
|
|
31
|
+
* - the /deco/invoke handler (setInvokeLoaders)
|
|
32
|
+
* The client reaches loaders/actions only via the HTTP invoke proxy.
|
|
33
|
+
*/
|
|
34
|
+
import { registerCommerceLoaders } from "@decocms/blocks/cms";
|
|
35
|
+
import { setInvokeLoaders } from "@decocms/blocks-admin";
|
|
36
|
+
|
|
37
|
+
import { COMMERCE_LOADERS } from "./commerce-loaders";
|
|
38
|
+
|
|
39
|
+
registerCommerceLoaders(COMMERCE_LOADERS);
|
|
40
|
+
setInvokeLoaders(() => COMMERCE_LOADERS);
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
@@ -5,6 +5,7 @@ import { generateRoutes } from "./routes";
|
|
|
5
5
|
import { generateServerEntry } from "./server-entry";
|
|
6
6
|
import { generateSetup } from "./setup";
|
|
7
7
|
import { generateViteConfig } from "./vite-config";
|
|
8
|
+
import { generateCommerceInit } from "./commerce-init";
|
|
8
9
|
import { generateCommerceLoaders } from "./commerce-loaders";
|
|
9
10
|
import { generateSectionLoaders } from "./section-loaders";
|
|
10
11
|
import { generateHooks } from "./hooks";
|
|
@@ -76,6 +77,10 @@ describe("scaffolder templates never emit @decocms/start or @decocms/apps/*", ()
|
|
|
76
77
|
);
|
|
77
78
|
});
|
|
78
79
|
|
|
80
|
+
it("commerce-init.ts", () => {
|
|
81
|
+
assertNoLegacyPackageNames("commerce-init.ts", generateCommerceInit(ctx));
|
|
82
|
+
});
|
|
83
|
+
|
|
79
84
|
it("section-loaders.ts", () => {
|
|
80
85
|
assertNoLegacyPackageNames(
|
|
81
86
|
"section-loaders.ts",
|
|
@@ -65,6 +65,10 @@ function generateWorkerEntry(ctx: MigrationContext): string {
|
|
|
65
65
|
* npx -p @decocms/blocks-cli deco-cf-observability --write
|
|
66
66
|
*/
|
|
67
67
|
import "./setup";
|
|
68
|
+
// Server-only: registers COMMERCE_LOADERS + invoke. Kept out of ./setup (which
|
|
69
|
+
// router.tsx imports) so the loader/action module graph never enters the client
|
|
70
|
+
// bundle. See setup/commerce-init.ts.
|
|
71
|
+
import "./setup/commerce-init";
|
|
68
72
|
import handler, { createServerEntry } from "@tanstack/react-start/server-entry";
|
|
69
73
|
import { createDecoWorkerEntry } from "@decocms/tanstack";
|
|
70
74
|
import { instrumentWorker } from "@decocms/blocks/sdk/observability";
|
|
@@ -109,6 +113,10 @@ function generateVtexWorkerEntry(ctx: MigrationContext): string {
|
|
|
109
113
|
const vtexAccount = ctx.vtexAccount || ctx.siteName;
|
|
110
114
|
|
|
111
115
|
return `import "./setup";
|
|
116
|
+
// Server-only: registers COMMERCE_LOADERS + invoke. Kept out of ./setup (which
|
|
117
|
+
// router.tsx imports) so the loader/action module graph never enters the client
|
|
118
|
+
// bundle. See setup/commerce-init.ts.
|
|
119
|
+
import "./setup/commerce-init";
|
|
112
120
|
import handler, { createServerEntry } from "@tanstack/react-start/server-entry";
|
|
113
121
|
import { createDecoWorkerEntry } from "@decocms/tanstack";
|
|
114
122
|
import { instrumentWorker } from "@decocms/blocks/sdk/observability";
|
|
@@ -82,20 +82,23 @@ export function generateSetup(ctx: MigrationContext): string {
|
|
|
82
82
|
*
|
|
83
83
|
* Actual logic lives in focused modules:
|
|
84
84
|
* setup/commerce-loaders.ts — COMMERCE_LOADERS map (VTEX + site data fetchers)
|
|
85
|
+
* setup/commerce-init.ts — server-only registration of COMMERCE_LOADERS +
|
|
86
|
+
* invoke. Imported by worker-entry.ts, NOT here,
|
|
87
|
+
* so the loader/action module graph (and any
|
|
88
|
+
* credential in it) never enters the client bundle.
|
|
85
89
|
* setup/section-loaders.ts — registerSectionLoaders (per-section prop enrichment)
|
|
86
90
|
*
|
|
91
|
+
* This file IS imported by router.tsx (client) for section registration/hydration,
|
|
92
|
+
* so it must stay free of server-only imports (COMMERCE_LOADERS, invoke, secrets).
|
|
93
|
+
*
|
|
87
94
|
* Section metadata (eager, sync, layout, cache, LoadingFallback) is declared
|
|
88
95
|
* in each section file and auto-extracted by generate-sections.ts.
|
|
89
96
|
*/
|
|
90
97
|
|
|
91
98
|
import "./cache-config";
|
|
92
99
|
|
|
93
|
-
import {
|
|
94
|
-
|
|
95
|
-
applySectionConventions,
|
|
96
|
-
} from "@decocms/blocks/cms";
|
|
97
|
-
import { createSiteSetup } from "@decocms/blocks/setup";
|
|
98
|
-
import { setInvokeLoaders } from "@decocms/blocks-admin";${isVtex ? `
|
|
100
|
+
import { applySectionConventions } from "@decocms/blocks/cms";
|
|
101
|
+
import { createSiteSetup } from "@decocms/blocks/setup";${isVtex ? `
|
|
99
102
|
import { createInstrumentedFetch } from "@decocms/blocks/sdk/instrumentedFetch";
|
|
100
103
|
import { initVtexFromBlocks, setVtexFetch } from "@decocms/apps-vtex";` : ""}${hasLocationMatcher ? `
|
|
101
104
|
import { registerLocationMatcher } from "./matchers/location";` : ""}
|
|
@@ -105,7 +108,6 @@ import { PreviewProviders } from "@decocms/tanstack";
|
|
|
105
108
|
// @ts-ignore Vite ?url import
|
|
106
109
|
import appCss from "./styles/app.css?url";
|
|
107
110
|
|
|
108
|
-
import { COMMERCE_LOADERS } from "./setup/commerce-loaders";
|
|
109
111
|
import "./setup/section-loaders";
|
|
110
112
|
|
|
111
113
|
// -- Framework setup --
|
|
@@ -142,7 +144,8 @@ applySectionConventions({
|
|
|
142
144
|
});
|
|
143
145
|
|
|
144
146
|
// -- Commerce + invoke --
|
|
145
|
-
|
|
146
|
-
|
|
147
|
+
// Registration lives in setup/commerce-init.ts, imported ONLY by
|
|
148
|
+
// src/worker-entry.ts (server). Importing it here would drag COMMERCE_LOADERS —
|
|
149
|
+
// and every site loader/action module it references — into the client bundle.
|
|
147
150
|
`;
|
|
148
151
|
}
|