@decocms/start 2.13.0 → 2.14.0
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/.agents/skills/deco-to-tanstack-migration/references/post-migration-cleanup.md +32 -6
- package/CLAUDE.md +1 -1
- package/package.json +1 -1
- package/scripts/migrate/post-cleanup/rules.ts +77 -6
- package/scripts/migrate/post-cleanup/runner.test.ts +123 -2
- package/scripts/migrate/post-cleanup/shim-classify.test.ts +352 -0
- package/scripts/migrate/post-cleanup/shim-classify.ts +246 -0
- package/.cursor/skills/deco-to-tanstack-migration/SKILL.md +0 -655
- package/.cursor/skills/deco-to-tanstack-migration/references/codemod-commands.md +0 -174
- package/.cursor/skills/deco-to-tanstack-migration/references/commerce/README.md +0 -78
- package/.cursor/skills/deco-to-tanstack-migration/references/deco-framework/README.md +0 -174
- package/.cursor/skills/deco-to-tanstack-migration/references/gotchas.md +0 -834
- package/.cursor/skills/deco-to-tanstack-migration/references/imports/README.md +0 -70
- package/.cursor/skills/deco-to-tanstack-migration/references/platform-hooks/README.md +0 -121
- package/.cursor/skills/deco-to-tanstack-migration/references/post-migration-cleanup.md +0 -231
- package/.cursor/skills/deco-to-tanstack-migration/references/signals/README.md +0 -220
- package/.cursor/skills/deco-to-tanstack-migration/references/vite-config/README.md +0 -103
- package/.cursor/skills/deco-to-tanstack-migration/templates/package-json.md +0 -75
- package/.cursor/skills/deco-to-tanstack-migration/templates/root-route.md +0 -127
- package/.cursor/skills/deco-to-tanstack-migration/templates/router.md +0 -96
- package/.cursor/skills/deco-to-tanstack-migration/templates/setup-ts.md +0 -148
- package/.cursor/skills/deco-to-tanstack-migration/templates/vite-config.md +0 -197
- package/.cursor/skills/deco-to-tanstack-migration/templates/worker-entry.md +0 -67
- /package/{.cursor → .agents}/skills/deco-to-tanstack-migration/references/server-functions/README.md +0 -0
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
# Vite Configuration
|
|
2
|
-
|
|
3
|
-
For the canonical battle-tested template (with VTEX dev proxy, CSP headers,
|
|
4
|
-
React Compiler, dedupe, framework plugin, and rollup chunk strategy) see
|
|
5
|
-
[`../../templates/vite-config.md`](../../templates/vite-config.md).
|
|
6
|
-
|
|
7
|
-
This page covers the post-migration **minimum viable config** if you've
|
|
8
|
-
stripped everything optional. Real sites should use the full template.
|
|
9
|
-
|
|
10
|
-
## Minimum Viable Config
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
import { cloudflare } from "@cloudflare/vite-plugin";
|
|
14
|
-
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
15
|
-
import { decoVitePlugin } from "@decocms/start/vite";
|
|
16
|
-
import react from "@vitejs/plugin-react";
|
|
17
|
-
import tailwindcss from "@tailwindcss/vite";
|
|
18
|
-
import { defineConfig } from "vite";
|
|
19
|
-
import path from "path";
|
|
20
|
-
|
|
21
|
-
const srcDir = path.resolve(__dirname, "src");
|
|
22
|
-
|
|
23
|
-
export default defineConfig({
|
|
24
|
-
plugins: [
|
|
25
|
-
cloudflare({ viteEnvironment: { name: "ssr" } }),
|
|
26
|
-
tanstackStart({ server: { entry: "server" } }),
|
|
27
|
-
react({
|
|
28
|
-
babel: {
|
|
29
|
-
plugins: [["babel-plugin-react-compiler", { target: "19" }]],
|
|
30
|
-
},
|
|
31
|
-
}),
|
|
32
|
-
tailwindcss(),
|
|
33
|
-
// Required — server-only stubs, blocks.gen.ts fast-path, meta.gen
|
|
34
|
-
// client stub, daemon/tunnel for dev mode. Without this, client
|
|
35
|
-
// bundle crashes on node:async_hooks / react-dom/server transitively
|
|
36
|
-
// imported by @decocms/start.
|
|
37
|
-
decoVitePlugin(),
|
|
38
|
-
],
|
|
39
|
-
resolve: {
|
|
40
|
-
// Required — dedupe React/TanStack/Deco packages so there's only one
|
|
41
|
-
// instance of each. Without this you get "Invalid hook call" errors.
|
|
42
|
-
dedupe: [
|
|
43
|
-
"@decocms/start",
|
|
44
|
-
"@decocms/apps",
|
|
45
|
-
"@tanstack/react-start",
|
|
46
|
-
"@tanstack/react-router",
|
|
47
|
-
"react",
|
|
48
|
-
"react-dom",
|
|
49
|
-
],
|
|
50
|
-
alias: {
|
|
51
|
-
"~": srcDir,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**One alias only**: `~` -> `src/`. Nothing else.
|
|
58
|
-
|
|
59
|
-
The `decoVitePlugin()` call is **mandatory** — the older skill examples
|
|
60
|
-
that omitted it (or inlined the stub logic) reflect the pre-2.x state of
|
|
61
|
-
`@decocms/start` and will produce build/runtime failures on current versions.
|
|
62
|
-
|
|
63
|
-
## tsconfig.json
|
|
64
|
-
|
|
65
|
-
Must mirror the Vite alias:
|
|
66
|
-
|
|
67
|
-
```json
|
|
68
|
-
{
|
|
69
|
-
"compilerOptions": {
|
|
70
|
-
"jsx": "react-jsx",
|
|
71
|
-
"moduleResolution": "bundler",
|
|
72
|
-
"module": "ESNext",
|
|
73
|
-
"target": "ES2022",
|
|
74
|
-
"skipLibCheck": true,
|
|
75
|
-
"strictNullChecks": true,
|
|
76
|
-
"baseUrl": ".",
|
|
77
|
-
"paths": {
|
|
78
|
-
"~/*": ["./src/*"]
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"include": ["src/**/*", "vite.config.ts"]
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
No `$store/*`, `site/*`, `apps/*`, `preact`, `@preact/signals`, `@deco/deco` paths. Those are all dead.
|
|
86
|
-
|
|
87
|
-
## React Compiler
|
|
88
|
-
|
|
89
|
-
The `babel-plugin-react-compiler` with `target: "19"` enables automatic memoization. Requires `@vitejs/plugin-react` instead of the default SWC plugin.
|
|
90
|
-
|
|
91
|
-
Install: `npm install -D @vitejs/plugin-react babel-plugin-react-compiler`
|
|
92
|
-
|
|
93
|
-
## Environment Variables
|
|
94
|
-
|
|
95
|
-
For VTEX API keys, use Cloudflare Workers secrets or `.dev.vars`:
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
VTEX_ACCOUNT=mystore
|
|
99
|
-
VTEX_APP_KEY=...
|
|
100
|
-
VTEX_APP_TOKEN=...
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Accessed via `process.env.*` in `createServerFn` handlers.
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
# package.json Template
|
|
2
|
-
|
|
3
|
-
Current as of `@decocms/start@1.6.2` and `@decocms/apps@1.4.1+`.
|
|
4
|
-
|
|
5
|
-
```json
|
|
6
|
-
{
|
|
7
|
-
"name": "my-tanstack-store",
|
|
8
|
-
"version": "0.1.0",
|
|
9
|
-
"type": "module",
|
|
10
|
-
"description": "storefront powered by TanStack Start",
|
|
11
|
-
"scripts": {
|
|
12
|
-
"dev": "vite dev",
|
|
13
|
-
"dev:clean": "rm -rf node_modules/.vite .wrangler/state .tanstack && vite dev",
|
|
14
|
-
"generate:blocks": "tsx node_modules/@decocms/start/scripts/generate-blocks.ts",
|
|
15
|
-
"generate:routes": "tsr generate",
|
|
16
|
-
"generate:schema": "tsx node_modules/@decocms/start/scripts/generate-schema.ts --site <SITE>",
|
|
17
|
-
"generate:invoke": "tsx node_modules/@decocms/start/scripts/generate-invoke.ts",
|
|
18
|
-
"generate:sections": "tsx node_modules/@decocms/start/scripts/generate-sections.ts",
|
|
19
|
-
"generate:loaders": "tsx node_modules/@decocms/start/scripts/generate-loaders.ts --exclude shopify/loaders,shopify/actions",
|
|
20
|
-
"build": "npm run generate:blocks && npm run generate:schema && npm run generate:sections && npm run generate:loaders && tsr generate && vite build",
|
|
21
|
-
"preview": "vite preview",
|
|
22
|
-
"deploy": "npm run build && wrangler deploy",
|
|
23
|
-
"types": "wrangler types",
|
|
24
|
-
"typecheck": "tsc --noEmit",
|
|
25
|
-
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
26
|
-
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
|
|
27
|
-
"knip": "knip",
|
|
28
|
-
"tailwind:lint": "tsx scripts/tailwind-lint.ts",
|
|
29
|
-
"tailwind:fix": "tsx scripts/tailwind-lint.ts --fix"
|
|
30
|
-
},
|
|
31
|
-
"dependencies": {
|
|
32
|
-
"@decocms/apps": "^1.4.1",
|
|
33
|
-
"@decocms/start": "^1.6.2",
|
|
34
|
-
"@tanstack/react-query": "5.90.21",
|
|
35
|
-
"@tanstack/react-router": "1.166.7",
|
|
36
|
-
"@tanstack/react-start": "1.166.8",
|
|
37
|
-
"@tanstack/react-store": "0.9.2",
|
|
38
|
-
"@tanstack/store": "0.9.2",
|
|
39
|
-
"react": "^19.2.4",
|
|
40
|
-
"react-dom": "^19.2.4"
|
|
41
|
-
},
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"@cloudflare/vite-plugin": "^1.27.0",
|
|
44
|
-
"@tailwindcss/vite": "^4.2.1",
|
|
45
|
-
"@tanstack/router-cli": "1.166.7",
|
|
46
|
-
"@types/react": "^19.2.14",
|
|
47
|
-
"@types/react-dom": "^19.2.3",
|
|
48
|
-
"@vitejs/plugin-react": "^5.1.4",
|
|
49
|
-
"babel-plugin-react-compiler": "^1.0.0",
|
|
50
|
-
"daisyui": "^5.5.19",
|
|
51
|
-
"knip": "^5.61.2",
|
|
52
|
-
"prettier": "^3.5.3",
|
|
53
|
-
"tailwindcss": "^4.2.1",
|
|
54
|
-
"ts-morph": "^27.0.2",
|
|
55
|
-
"tsx": "^4.19.4",
|
|
56
|
-
"typescript": "^5.9.3",
|
|
57
|
-
"vite": "^7.3.1",
|
|
58
|
-
"wrangler": "^4.72.0"
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Notes
|
|
64
|
-
|
|
65
|
-
- **Minimum `@decocms/start` version is `1.6.2`** — earlier versions have a bug where deferred sections (`Lazy`-wrapped) lose `routeParams`, causing PDP loaders with `:slug` to return null. See gotcha #47.
|
|
66
|
-
- **`generate` scripts** run as part of `build`. In dev, Vite HMR picks up changes without re-running them — you only need to re-run `generate:blocks` / `generate:sections` after editing `.deco/blocks/` or a section's metadata exports.
|
|
67
|
-
- **`generate:loaders --exclude shopify/loaders,shopify/actions`** — the Shopify app ships its own loaders/actions, registered by `autoconfigApps`. Exclude to avoid double-registering site-local invoke entries for them.
|
|
68
|
-
- **`tsr generate`** produces `src/routeTree.gen.ts` from file-based routes.
|
|
69
|
-
- **GitHub Packages**: add an `.npmrc` in the repo root (git-ignore is optional):
|
|
70
|
-
```
|
|
71
|
-
@decocms:registry=https://npm.pkg.github.com
|
|
72
|
-
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
|
|
73
|
-
```
|
|
74
|
-
Then set `NODE_AUTH_TOKEN` in `.env` for local installs and as a CI secret. Alternatively, pin by Git tag via `github:` URL syntax — see gotcha #45.
|
|
75
|
-
- **React Compiler** is enabled via `babel-plugin-react-compiler` in `vite.config.ts`. Most sections benefit without annotation.
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
# __root.tsx Template
|
|
2
|
-
|
|
3
|
-
Two options — pick the one that matches the site's needs.
|
|
4
|
-
|
|
5
|
-
## Option A — Minimal (recommended default)
|
|
6
|
-
|
|
7
|
-
`DecoRootLayout` from `@decocms/start/hooks` wraps the `<html>` shell with all the pieces the framework expects (DaisyUI theme, LiveControls, HeadContent, Scripts, analytics bootstrap). Use this for every new site unless you need custom providers at the root.
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
import { createRootRoute } from "@tanstack/react-router";
|
|
11
|
-
import { DecoRootLayout } from "@decocms/start/hooks";
|
|
12
|
-
// @ts-ignore Vite ?url import
|
|
13
|
-
import appCss from "../styles/app.css?url";
|
|
14
|
-
|
|
15
|
-
export const Route = createRootRoute({
|
|
16
|
-
head: () => ({
|
|
17
|
-
meta: [
|
|
18
|
-
{ charSet: "utf-8" },
|
|
19
|
-
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
|
20
|
-
{ title: "My Store" },
|
|
21
|
-
],
|
|
22
|
-
links: [
|
|
23
|
-
{ rel: "stylesheet", href: appCss },
|
|
24
|
-
{ rel: "icon", href: "/favicon.ico" },
|
|
25
|
-
],
|
|
26
|
-
}),
|
|
27
|
-
component: RootLayout,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
function RootLayout() {
|
|
31
|
-
return <DecoRootLayout lang="pt-BR" siteName="my-store" />;
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Option B — Custom providers (cart/React Query at root)
|
|
36
|
-
|
|
37
|
-
Only reach for this when you need providers wrapping the CMS outlet — a root-mounted cart store that must hydrate before any section, global React Query client, etc. Everything else stays in sections.
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { useState } from "react";
|
|
41
|
-
import {
|
|
42
|
-
createRootRoute,
|
|
43
|
-
HeadContent,
|
|
44
|
-
Outlet,
|
|
45
|
-
Scripts,
|
|
46
|
-
useRouterState,
|
|
47
|
-
} from "@tanstack/react-router";
|
|
48
|
-
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
49
|
-
import { LiveControls } from "@decocms/start/hooks";
|
|
50
|
-
import { ANALYTICS_SCRIPT } from "@decocms/start/sdk/analytics";
|
|
51
|
-
// @ts-ignore Vite ?url import
|
|
52
|
-
import appCss from "../styles/app.css?url";
|
|
53
|
-
|
|
54
|
-
const PROGRESS_CSS = `
|
|
55
|
-
@keyframes decoProgress{0%{width:0}30%{width:50%}60%{width:80%}100%{width:98%}}
|
|
56
|
-
.deco-nav-progress{position:fixed;top:0;left:0;height:3px;background:var(--color-primary,#e53e3e);z-index:9999;animation:decoProgress 4s cubic-bezier(.4,0,.2,1) forwards;pointer-events:none;box-shadow:0 0 8px var(--color-primary,#e53e3e)}
|
|
57
|
-
`;
|
|
58
|
-
|
|
59
|
-
function NavigationProgress() {
|
|
60
|
-
const isLoading = useRouterState({ select: (s) => s.isLoading });
|
|
61
|
-
if (!isLoading) return null;
|
|
62
|
-
return (
|
|
63
|
-
<>
|
|
64
|
-
<style dangerouslySetInnerHTML={{ __html: PROGRESS_CSS }} />
|
|
65
|
-
<div className="deco-nav-progress" />
|
|
66
|
-
</>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export const Route = createRootRoute({
|
|
71
|
-
head: () => ({
|
|
72
|
-
meta: [
|
|
73
|
-
{ charSet: "utf-8" },
|
|
74
|
-
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
|
75
|
-
{ title: "My Store" },
|
|
76
|
-
],
|
|
77
|
-
links: [
|
|
78
|
-
{ rel: "stylesheet", href: appCss },
|
|
79
|
-
{ rel: "icon", href: "/favicon.ico" },
|
|
80
|
-
],
|
|
81
|
-
}),
|
|
82
|
-
component: RootLayout,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
function RootLayout() {
|
|
86
|
-
const [queryClient] = useState(
|
|
87
|
-
() => new QueryClient({
|
|
88
|
-
defaultOptions: {
|
|
89
|
-
queries: {
|
|
90
|
-
staleTime: import.meta.env.DEV ? 0 : 30_000,
|
|
91
|
-
gcTime: import.meta.env.DEV ? 0 : 5 * 60_000,
|
|
92
|
-
refetchOnWindowFocus: import.meta.env.DEV,
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
}),
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
return (
|
|
99
|
-
<html lang="pt-BR" data-theme="light" suppressHydrationWarning>
|
|
100
|
-
<head>
|
|
101
|
-
<HeadContent />
|
|
102
|
-
</head>
|
|
103
|
-
<body className="bg-base-100 text-base-content" suppressHydrationWarning>
|
|
104
|
-
<NavigationProgress />
|
|
105
|
-
<QueryClientProvider client={queryClient}>
|
|
106
|
-
<Outlet />
|
|
107
|
-
</QueryClientProvider>
|
|
108
|
-
<LiveControls site="my-store" />
|
|
109
|
-
<script type="module" dangerouslySetInnerHTML={{ __html: ANALYTICS_SCRIPT }} />
|
|
110
|
-
<Scripts />
|
|
111
|
-
</body>
|
|
112
|
-
</html>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Key points (applies to both)
|
|
118
|
-
|
|
119
|
-
1. **`data-theme="light"`** on `<html>` — required for DaisyUI v4/v5 CSS variables to activate in production AND in the admin preview shell. Option A sets this for you.
|
|
120
|
-
2. **`suppressHydrationWarning`** on `<html>` and `<body>` — browser extensions mutate these elements; React would warn on mismatch.
|
|
121
|
-
3. **`LiveControls site={...}`** — admin iframe bridge. `site` MUST match the CMS site name used in `@decocms/apps/registry` entries and `.deco/blocks/`.
|
|
122
|
-
4. **No `Device.Provider`** — do NOT hardcode `<Device.Provider value={{ isMobile: true }}>` in the root. Device detection belongs in each page route's `createServerFn` loader (see gotcha #29).
|
|
123
|
-
5. **Keep the root thin** — sections own their own data. Avoid cross-section state at the root unless it's truly global (cart, theme). Every provider at the root ships to every page, even ones that don't need it.
|
|
124
|
-
|
|
125
|
-
## Cart/platform state
|
|
126
|
-
|
|
127
|
-
If you add a cart store at the root (Option B), wire it AFTER `QueryClientProvider` so section-local hooks can use both. The cart store itself should be a module-level `@tanstack/store` `Store`, and a `useCart()` hook reads via `useStore(cartStore)`. SSR hydration: the page loader fetches the initial cart (via the minicart loader or Shopify `getCart`) and the root passes the initial state to `cartStore.setState()` before hydration.
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
# router.tsx Template
|
|
2
|
-
|
|
3
|
-
```typescript
|
|
4
|
-
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
|
|
5
|
-
import { routeTree } from "./routeTree.gen";
|
|
6
|
-
|
|
7
|
-
export function createRouter() {
|
|
8
|
-
const router = createTanStackRouter({
|
|
9
|
-
routeTree,
|
|
10
|
-
scrollRestoration: true,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// Scroll to top on forward navigation (PUSH/REPLACE), skip on back/forward (GO)
|
|
14
|
-
router.subscribe("onResolved", (evt) => {
|
|
15
|
-
if (evt.type === "GO") return;
|
|
16
|
-
window.scrollTo({ top: 0 });
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
return router;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
declare module "@tanstack/react-router" {
|
|
23
|
-
interface Register {
|
|
24
|
-
router: ReturnType<typeof createRouter>;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
## Route Files
|
|
30
|
-
|
|
31
|
-
### src/routes/index.tsx (Homepage)
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
import { createFileRoute } from "@tanstack/react-router";
|
|
35
|
-
import { cmsHomeRouteConfig } from "@decocms/start/routes";
|
|
36
|
-
import { DecoPageRenderer } from "@decocms/start/hooks";
|
|
37
|
-
import { loadDeferredSection } from "@decocms/start/routes/cmsRoute";
|
|
38
|
-
|
|
39
|
-
const { loader, head } = cmsHomeRouteConfig({
|
|
40
|
-
defaultTitle: "My Store",
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
export const Route = createFileRoute("/")({
|
|
44
|
-
loader,
|
|
45
|
-
head,
|
|
46
|
-
component: HomePage,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
function HomePage() {
|
|
50
|
-
const { resolvedSections, deferredSections, pagePath } = Route.useLoaderData();
|
|
51
|
-
return (
|
|
52
|
-
<DecoPageRenderer
|
|
53
|
-
sections={resolvedSections}
|
|
54
|
-
deferredSections={deferredSections}
|
|
55
|
-
pagePath={pagePath}
|
|
56
|
-
loadDeferredSectionFn={loadDeferredSection}
|
|
57
|
-
/>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### src/routes/$.tsx (Catch-All CMS Route)
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import { createFileRoute, notFound } from "@tanstack/react-router";
|
|
66
|
-
import { cmsRouteConfig } from "@decocms/start/routes";
|
|
67
|
-
import { DecoPageRenderer } from "@decocms/start/hooks";
|
|
68
|
-
import { loadDeferredSection } from "@decocms/start/routes/cmsRoute";
|
|
69
|
-
|
|
70
|
-
const { loader, head } = cmsRouteConfig({
|
|
71
|
-
siteName: "My Store",
|
|
72
|
-
ignoreSearchParams: ["skuId"],
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
export const Route = createFileRoute("/$")({
|
|
76
|
-
loader: async (ctx) => {
|
|
77
|
-
const data = await loader(ctx);
|
|
78
|
-
if (!data) throw notFound();
|
|
79
|
-
return data;
|
|
80
|
-
},
|
|
81
|
-
head,
|
|
82
|
-
component: CmsPage,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
function CmsPage() {
|
|
86
|
-
const { resolvedSections, deferredSections, pagePath } = Route.useLoaderData();
|
|
87
|
-
return (
|
|
88
|
-
<DecoPageRenderer
|
|
89
|
-
sections={resolvedSections}
|
|
90
|
-
deferredSections={deferredSections}
|
|
91
|
-
pagePath={pagePath}
|
|
92
|
-
loadDeferredSectionFn={loadDeferredSection}
|
|
93
|
-
/>
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
```
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
# setup.ts Template
|
|
2
|
-
|
|
3
|
-
Minimal, convention-driven setup. Matches `@decocms/start >= 1.6.2`. Based on the storefront-tanstack Shopify port.
|
|
4
|
-
|
|
5
|
-
Three framework composers do the work — the site file is short by design:
|
|
6
|
-
|
|
7
|
-
- `createSiteSetup(options)` — CMS engine + admin protocol + matcher registration
|
|
8
|
-
- `applySectionConventions(gen)` — reads `sections.gen.ts` and wires eager/layout/seo/cache/sync sections
|
|
9
|
-
- `autoconfigApps(blocks, APP_REGISTRY)` — dual-registers every app's loaders + actions from `@decocms/apps/registry`
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
/**
|
|
13
|
-
* Site setup — orchestrator that wires framework, apps, and sections.
|
|
14
|
-
*
|
|
15
|
-
* App-installed loaders + actions (Shopify, VTEX, Resend, …) are wired via
|
|
16
|
-
* `autoconfigApps(blocks, APP_REGISTRY)` — adding a new app is a one-line
|
|
17
|
-
* entry in `@decocms/apps/registry.ts`, no change needed here.
|
|
18
|
-
*
|
|
19
|
-
* Section-specific prop enrichment lives in `setup/section-loaders.ts`.
|
|
20
|
-
* Section metadata (eager, sync, layout, cache, LoadingFallback) is declared
|
|
21
|
-
* in each section file and auto-extracted by generate-sections.ts.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
import "./cache-config";
|
|
25
|
-
|
|
26
|
-
import {
|
|
27
|
-
registerCommerceLoaders,
|
|
28
|
-
applySectionConventions,
|
|
29
|
-
} from "@decocms/start/cms";
|
|
30
|
-
import { createSiteSetup } from "@decocms/start/setup";
|
|
31
|
-
import { autoconfigApps } from "@decocms/start/apps";
|
|
32
|
-
import { createInstrumentedFetch } from "@decocms/start/sdk/instrumentedFetch";
|
|
33
|
-
import { initShopifyFromBlocks, setShopifyFetch } from "@decocms/apps/shopify";
|
|
34
|
-
import { APP_REGISTRY } from "@decocms/apps/registry";
|
|
35
|
-
import { blocks as generatedBlocks } from "./server/cms/blocks.gen";
|
|
36
|
-
import {
|
|
37
|
-
sectionMeta,
|
|
38
|
-
syncComponents,
|
|
39
|
-
loadingFallbacks,
|
|
40
|
-
} from "./server/cms/sections.gen";
|
|
41
|
-
import { PreviewProviders } from "@decocms/start/hooks";
|
|
42
|
-
// @ts-ignore Vite ?url import
|
|
43
|
-
import appCss from "./styles/app.css?url";
|
|
44
|
-
|
|
45
|
-
import "./setup/section-loaders";
|
|
46
|
-
|
|
47
|
-
// -- Framework setup --
|
|
48
|
-
createSiteSetup({
|
|
49
|
-
sections: import.meta.glob("./sections/**/*.tsx") as Record<string, () => Promise<any>>,
|
|
50
|
-
blocks: generatedBlocks,
|
|
51
|
-
meta: () => import("./server/admin/meta.gen.json").then((m) => m.default),
|
|
52
|
-
css: appCss,
|
|
53
|
-
fonts: [],
|
|
54
|
-
productionOrigins: [
|
|
55
|
-
"https://www.<SITE>.com.br",
|
|
56
|
-
"https://<SITE>.com.br",
|
|
57
|
-
],
|
|
58
|
-
previewWrapper: PreviewProviders,
|
|
59
|
-
initPlatform: (blocks) => initShopifyFromBlocks(blocks), // or initVtexFromBlocks
|
|
60
|
-
onResolveError: (error, resolveType, context) => {
|
|
61
|
-
console.error(`[CMS-DEBUG] ${context} "${resolveType}" failed:`, error);
|
|
62
|
-
},
|
|
63
|
-
onDanglingReference: (resolveType) => {
|
|
64
|
-
console.warn(`[CMS-DEBUG] Dangling reference: ${resolveType}`);
|
|
65
|
-
return null;
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
// -- Platform fetch instrumentation (optional, for observability) --
|
|
70
|
-
setShopifyFetch(createInstrumentedFetch("shopify"));
|
|
71
|
-
|
|
72
|
-
// -- Convention-driven section registration --
|
|
73
|
-
applySectionConventions({
|
|
74
|
-
meta: sectionMeta,
|
|
75
|
-
syncComponents,
|
|
76
|
-
loadingFallbacks,
|
|
77
|
-
sectionGlob: import.meta.glob("./sections/**/*.tsx") as Record<string, () => Promise<any>>,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// -- Apps: auto-configure from decofile against the @decocms/apps registry --
|
|
81
|
-
// Dual-registers into commerce loaders (CMS resolve) + invoke handlers (admin)
|
|
82
|
-
// for every configured app. Adding a new app = add an entry in
|
|
83
|
-
// @decocms/apps/registry.ts. No change here per app.
|
|
84
|
-
await autoconfigApps(generatedBlocks, APP_REGISTRY);
|
|
85
|
-
|
|
86
|
-
// -- Site-local loaders (not shipped by an app) --
|
|
87
|
-
// Register .ts and bare variants — CMS resolver may query either.
|
|
88
|
-
registerCommerceLoaders({
|
|
89
|
-
"site/loaders/minicart.ts": async () => (await import("./loaders/minicart")).default(),
|
|
90
|
-
"site/loaders/minicart": async () => (await import("./loaders/minicart")).default(),
|
|
91
|
-
"site/loaders/user.ts": async () => (await import("./loaders/user")).default(),
|
|
92
|
-
"site/loaders/user": async () => (await import("./loaders/user")).default(),
|
|
93
|
-
"site/loaders/wishlist.ts": async () => (await import("./loaders/wishlist")).default(),
|
|
94
|
-
"site/loaders/wishlist": async () => (await import("./loaders/wishlist")).default(),
|
|
95
|
-
});
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Section metadata convention
|
|
99
|
-
|
|
100
|
-
Declare section behavior in the section file, not in setup.ts. `generate-sections.ts` scans `src/sections/**` and emits `src/server/cms/sections.gen.ts`.
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
// src/sections/Header/Header.tsx
|
|
104
|
-
export default function Header(props) { /* ... */ }
|
|
105
|
-
|
|
106
|
-
export const eager = true; // always render eagerly (bypass Lazy wrappers)
|
|
107
|
-
export const sync = true; // import bundled, not code-split (for first paint)
|
|
108
|
-
export const layout = true; // render as a layout section (header/footer/theme)
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
112
|
-
// src/sections/Product/SearchResult.tsx
|
|
113
|
-
export const cache = "listing"; // SWR cache profile
|
|
114
|
-
|
|
115
|
-
export function LoadingFallback() {
|
|
116
|
-
return <div className="animate-pulse h-96 bg-base-200" />;
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
// src/sections/SEO/SeoPDP.tsx
|
|
122
|
-
export const seo = true; // extract page SEO from this section's output
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
After changes to any `export const <flag>` or `LoadingFallback`, re-run `npm run generate:sections` (or `npm run build`).
|
|
126
|
-
|
|
127
|
-
## What NOT to put here
|
|
128
|
-
|
|
129
|
-
- **Section imports** — lazy via Vite glob, sync via `sectionMeta.sync` + `syncComponents`. Zero manual imports needed.
|
|
130
|
-
- **App loaders/actions** — `autoconfigApps` registers every entry in `APP_REGISTRY`. Adding Shopify/VTEX/Resend loaders by hand is a bug.
|
|
131
|
-
- **alwaysEager arrays** — driven by `export const eager = true` in section files.
|
|
132
|
-
- **SEO registration** — driven by `export const seo = true`.
|
|
133
|
-
- **Cache profile arrays** — driven by `export const cache = "..."`.
|
|
134
|
-
- **`setMetaData` / `setRenderShell` / `setInvokeLoaders` calls** — `createSiteSetup` handles them. Only reach for the low-level APIs if composing a non-standard pipeline.
|
|
135
|
-
|
|
136
|
-
## Adding a new app (e.g., 4th commerce integration)
|
|
137
|
-
|
|
138
|
-
1. `@decocms/apps/registry.ts` gets one new entry: `{ blockKey, module, displayName, category, description }`
|
|
139
|
-
2. Bump `@decocms/apps` minor version; site installs the bump
|
|
140
|
-
3. Add the block in `.deco/blocks/<blockKey>.json` with platform config
|
|
141
|
-
4. Nothing else changes — `autoconfigApps` picks it up on next boot
|
|
142
|
-
|
|
143
|
-
## See also
|
|
144
|
-
|
|
145
|
-
- `applySectionConventions` source: `@decocms/start/src/cms/applySectionConventions.ts`
|
|
146
|
-
- `autoconfigApps` source: `@decocms/start/src/apps/autoconfig.ts`
|
|
147
|
-
- Registry source: `@decocms/apps/registry.ts`
|
|
148
|
-
- Generate scripts: `@decocms/start/scripts/generate-{blocks,schema,sections,loaders,invoke}.ts`
|