@decocms/blocks-cli 7.0.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.
Files changed (93) hide show
  1. package/package.json +44 -0
  2. package/scripts/analyze-traces.mjs +1117 -0
  3. package/scripts/audit-observability-config.test.ts +446 -0
  4. package/scripts/audit-observability-config.ts +511 -0
  5. package/scripts/deco-migrate-cli.ts +444 -0
  6. package/scripts/fast-deploy-kv.test.ts +131 -0
  7. package/scripts/generate-blocks.test.ts +94 -0
  8. package/scripts/generate-blocks.ts +274 -0
  9. package/scripts/generate-invoke.test.ts +195 -0
  10. package/scripts/generate-invoke.ts +469 -0
  11. package/scripts/generate-loaders.ts +217 -0
  12. package/scripts/generate-schema.ts +1287 -0
  13. package/scripts/generate-sections.ts +237 -0
  14. package/scripts/htmx-analyze.ts +226 -0
  15. package/scripts/lib/blocks-dedupe.test.ts +179 -0
  16. package/scripts/lib/blocks-dedupe.ts +142 -0
  17. package/scripts/lib/cf-kv-rest.ts +78 -0
  18. package/scripts/lib/jsonc.ts +122 -0
  19. package/scripts/lib/kv-snapshot.ts +51 -0
  20. package/scripts/lib/read-decofile.ts +70 -0
  21. package/scripts/lib/sync-helpers.ts +44 -0
  22. package/scripts/migrate/analyzers/htmx-analyze.test.ts +372 -0
  23. package/scripts/migrate/analyzers/htmx-analyze.ts +425 -0
  24. package/scripts/migrate/analyzers/island-classifier.ts +96 -0
  25. package/scripts/migrate/analyzers/loader-inventory.ts +63 -0
  26. package/scripts/migrate/analyzers/section-metadata.ts +147 -0
  27. package/scripts/migrate/analyzers/theme-extractor.ts +122 -0
  28. package/scripts/migrate/colors.ts +46 -0
  29. package/scripts/migrate/config.test.ts +202 -0
  30. package/scripts/migrate/config.ts +186 -0
  31. package/scripts/migrate/phase-analyze.test.ts +63 -0
  32. package/scripts/migrate/phase-analyze.ts +782 -0
  33. package/scripts/migrate/phase-cleanup-audit.test.ts +137 -0
  34. package/scripts/migrate/phase-cleanup-audit.ts +105 -0
  35. package/scripts/migrate/phase-cleanup.test.ts +141 -0
  36. package/scripts/migrate/phase-cleanup.ts +1588 -0
  37. package/scripts/migrate/phase-compile.test.ts +193 -0
  38. package/scripts/migrate/phase-compile.ts +177 -0
  39. package/scripts/migrate/phase-report.ts +243 -0
  40. package/scripts/migrate/phase-scaffold.ts +593 -0
  41. package/scripts/migrate/phase-transform.ts +310 -0
  42. package/scripts/migrate/phase-verify.test.ts +127 -0
  43. package/scripts/migrate/phase-verify.ts +572 -0
  44. package/scripts/migrate/post-cleanup/rules.ts +1708 -0
  45. package/scripts/migrate/post-cleanup/runner.test.ts +1771 -0
  46. package/scripts/migrate/post-cleanup/runner.ts +137 -0
  47. package/scripts/migrate/post-cleanup/shim-classify.test.ts +352 -0
  48. package/scripts/migrate/post-cleanup/shim-classify.ts +246 -0
  49. package/scripts/migrate/post-cleanup/types.ts +106 -0
  50. package/scripts/migrate/source-layout.test.ts +111 -0
  51. package/scripts/migrate/source-layout.ts +103 -0
  52. package/scripts/migrate/templates/app-css.ts +366 -0
  53. package/scripts/migrate/templates/cache-config.ts +26 -0
  54. package/scripts/migrate/templates/commerce-loaders.ts +230 -0
  55. package/scripts/migrate/templates/cursor-rules.test.ts +59 -0
  56. package/scripts/migrate/templates/cursor-rules.ts +70 -0
  57. package/scripts/migrate/templates/hooks.test.ts +141 -0
  58. package/scripts/migrate/templates/hooks.ts +152 -0
  59. package/scripts/migrate/templates/knip-config.ts +27 -0
  60. package/scripts/migrate/templates/lib-utils.test.ts +139 -0
  61. package/scripts/migrate/templates/lib-utils.ts +326 -0
  62. package/scripts/migrate/templates/lockfile-check-yml.test.ts +26 -0
  63. package/scripts/migrate/templates/lockfile-check-yml.ts +66 -0
  64. package/scripts/migrate/templates/package-json.ts +177 -0
  65. package/scripts/migrate/templates/routes.ts +237 -0
  66. package/scripts/migrate/templates/sdk-gen.ts +59 -0
  67. package/scripts/migrate/templates/section-loaders.ts +456 -0
  68. package/scripts/migrate/templates/server-entry.ts +561 -0
  69. package/scripts/migrate/templates/setup.ts +148 -0
  70. package/scripts/migrate/templates/tsconfig.ts +21 -0
  71. package/scripts/migrate/templates/types-gen.ts +174 -0
  72. package/scripts/migrate/templates/ui-components.ts +144 -0
  73. package/scripts/migrate/templates/vite-config.ts +101 -0
  74. package/scripts/migrate/transforms/dead-code.ts +455 -0
  75. package/scripts/migrate/transforms/deno-isms.ts +85 -0
  76. package/scripts/migrate/transforms/fresh-apis.ts +223 -0
  77. package/scripts/migrate/transforms/htmx-on-events.test.ts +305 -0
  78. package/scripts/migrate/transforms/htmx-on-events.ts +193 -0
  79. package/scripts/migrate/transforms/imports.ts +385 -0
  80. package/scripts/migrate/transforms/jsx.ts +317 -0
  81. package/scripts/migrate/transforms/section-conventions.ts +210 -0
  82. package/scripts/migrate/transforms/tailwind.ts +739 -0
  83. package/scripts/migrate/types.ts +244 -0
  84. package/scripts/migrate-blocks-to-kv.ts +104 -0
  85. package/scripts/migrate-post-cleanup.ts +191 -0
  86. package/scripts/migrate-to-cf-observability.test.ts +215 -0
  87. package/scripts/migrate-to-cf-observability.ts +699 -0
  88. package/scripts/migrate.ts +282 -0
  89. package/scripts/smoke-otlp-errorlog.ts +46 -0
  90. package/scripts/smoke-otlp-meter.ts +48 -0
  91. package/scripts/sync-blocks-to-kv.ts +153 -0
  92. package/scripts/tailwind-lint.ts +518 -0
  93. package/tsconfig.json +7 -0
@@ -0,0 +1,148 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { MigrationContext } from "../types";
4
+
5
+ function discoverFonts(ctx: MigrationContext): string[] {
6
+ // Check public/fonts (post-move)
7
+ const fontsDir = path.join(ctx.sourceDir, "public", "fonts");
8
+ if (fs.existsSync(fontsDir)) return scanFontDir(fontsDir);
9
+
10
+ // Check static/fonts (pre-move)
11
+ const staticFonts = path.join(ctx.sourceDir, "static", "fonts");
12
+ if (fs.existsSync(staticFonts)) return scanFontDir(staticFonts);
13
+
14
+ // Check static-*/fonts/ (multi-brand sites like casaevideo)
15
+ try {
16
+ const entries = fs.readdirSync(ctx.sourceDir, { withFileTypes: true });
17
+ for (const e of entries) {
18
+ if (e.isDirectory() && e.name.startsWith("static-")) {
19
+ const brandFonts = path.join(ctx.sourceDir, e.name, "fonts");
20
+ if (fs.existsSync(brandFonts)) return scanFontDir(brandFonts);
21
+ }
22
+ }
23
+ } catch { /* ignore */ }
24
+
25
+ return [];
26
+ }
27
+
28
+ function scanFontDir(dir: string): string[] {
29
+ try {
30
+ const allFonts = fs.readdirSync(dir)
31
+ .filter((f) => /\.(woff2?|ttf|otf|eot)$/i.test(f));
32
+
33
+ // Only preload the most critical fonts (Regular + Bold of the primary family).
34
+ // All fonts are still available via @font-face in CSS — this just controls
35
+ // which ones get <link rel="preload"> for faster rendering.
36
+ const critical = allFonts.filter((f) =>
37
+ /[-_](Regular|Bold)\.(woff2?|ttf|otf)$/i.test(f) &&
38
+ !/Italic/i.test(f)
39
+ );
40
+
41
+ // If we found critical weights, use those; otherwise take first 2
42
+ const toPreload = critical.length > 0
43
+ ? critical.slice(0, 4) // max 4 preloads
44
+ : allFonts.slice(0, 2);
45
+
46
+ return toPreload.map((f) => `/fonts/${f}`);
47
+ } catch {
48
+ return [];
49
+ }
50
+ }
51
+
52
+ function hasMatchers(ctx: MigrationContext): boolean {
53
+ const matchersDir = path.join(ctx.sourceDir, "matchers");
54
+ if (fs.existsSync(matchersDir)) return true;
55
+ const srcMatchers = path.join(ctx.sourceDir, "src", "matchers");
56
+ return fs.existsSync(srcMatchers);
57
+ }
58
+
59
+ export function generateSetup(ctx: MigrationContext): string {
60
+ const isVtex = ctx.platform === "vtex";
61
+ const siteName = ctx.siteName;
62
+ const fonts = discoverFonts(ctx);
63
+ const hasLocationMatcher = hasMatchers(ctx);
64
+
65
+ // Build productionOrigins from known domain patterns
66
+ const origins: string[] = [];
67
+ // Check if source has productionOrigins in existing setup files
68
+ const possibleDomains = [
69
+ `www.${siteName}.com.br`,
70
+ `${siteName}.com.br`,
71
+ ];
72
+ for (const domain of possibleDomains) {
73
+ origins.push(`"https://${domain}"`);
74
+ }
75
+
76
+ const fontEntries = fonts.length > 0
77
+ ? fonts.map((f) => `"${f}"`).join(", ")
78
+ : "";
79
+
80
+ return `/**
81
+ * Site setup — orchestrator that wires framework, commerce, and sections.
82
+ *
83
+ * Actual logic lives in focused modules:
84
+ * setup/commerce-loaders.ts — COMMERCE_LOADERS map (VTEX + site data fetchers)
85
+ * setup/section-loaders.ts — registerSectionLoaders (per-section prop enrichment)
86
+ *
87
+ * Section metadata (eager, sync, layout, cache, LoadingFallback) is declared
88
+ * in each section file and auto-extracted by generate-sections.ts.
89
+ */
90
+
91
+ import "./cache-config";
92
+
93
+ import {
94
+ registerCommerceLoaders,
95
+ applySectionConventions,
96
+ } from "@decocms/start/cms";
97
+ import { createSiteSetup } from "@decocms/start/setup";
98
+ import { setInvokeLoaders } from "@decocms/start/admin";${isVtex ? `
99
+ import { createInstrumentedFetch } from "@decocms/start/sdk/instrumentedFetch";
100
+ import { initVtexFromBlocks, setVtexFetch } from "@decocms/apps/vtex";` : ""}${hasLocationMatcher ? `
101
+ import { registerLocationMatcher } from "./matchers/location";` : ""}
102
+ import { blocks as generatedBlocks } from "./server/cms/blocks.gen";
103
+ import { sectionMeta, syncComponents, loadingFallbacks } from "./server/cms/sections.gen";
104
+ import { PreviewProviders } from "@decocms/start/hooks";
105
+ // @ts-ignore Vite ?url import
106
+ import appCss from "./styles/app.css?url";
107
+
108
+ import { COMMERCE_LOADERS } from "./setup/commerce-loaders";
109
+ import "./setup/section-loaders";
110
+
111
+ // -- Framework setup --
112
+ createSiteSetup({
113
+ sections: import.meta.glob("./sections/**/*.tsx") as Record<string, () => Promise<any>>,
114
+ blocks: generatedBlocks,
115
+ meta: () => import("./server/admin/meta.gen.json").then((m) => m.default),
116
+ css: appCss,
117
+ fonts: [${fontEntries}],
118
+ productionOrigins: [
119
+ ${origins.join(",\n ")},
120
+ ],
121
+ previewWrapper: PreviewProviders,${hasLocationMatcher ? `
122
+ customMatchers: [registerLocationMatcher],` : ""}${isVtex ? `
123
+ initPlatform: (blocks) => initVtexFromBlocks(blocks),` : ""}
124
+ onResolveError: (error, resolveType, context) => {
125
+ console.error(\`[CMS-DEBUG] \${context} "\${resolveType}" failed:\`, error);
126
+ },
127
+ onDanglingReference: (resolveType) => {
128
+ console.warn(\`[CMS-DEBUG] Dangling reference: \${resolveType}\`);
129
+ return null;
130
+ },
131
+ });
132
+ ${isVtex ? `
133
+ // -- VTEX wiring --
134
+ setVtexFetch(createInstrumentedFetch("vtex"));
135
+ ` : ""}
136
+ // -- Convention-driven section registration --
137
+ applySectionConventions({
138
+ meta: sectionMeta,
139
+ syncComponents,
140
+ loadingFallbacks,
141
+ sectionGlob: import.meta.glob("./sections/**/*.tsx") as Record<string, () => Promise<any>>,
142
+ });
143
+
144
+ // -- Commerce + invoke --
145
+ registerCommerceLoaders(COMMERCE_LOADERS);
146
+ setInvokeLoaders(() => COMMERCE_LOADERS);
147
+ `;
148
+ }
@@ -0,0 +1,21 @@
1
+ export function generateTsconfig(): string {
2
+ const config = {
3
+ compilerOptions: {
4
+ jsx: "react-jsx",
5
+ moduleResolution: "bundler",
6
+ module: "ESNext",
7
+ target: "ES2022",
8
+ skipLibCheck: true,
9
+ strictNullChecks: true,
10
+ forceConsistentCasingInFileNames: true,
11
+ types: ["vite/client"],
12
+ baseUrl: ".",
13
+ paths: {
14
+ "~/*": ["./src/*"],
15
+ },
16
+ },
17
+ include: ["src/**/*", "vite.config.ts"],
18
+ };
19
+
20
+ return JSON.stringify(config, null, 2) + "\n";
21
+ }
@@ -0,0 +1,174 @@
1
+ import type { MigrationContext } from "../types";
2
+
3
+ export function generateTypeFiles(ctx: MigrationContext): Record<string, string> {
4
+ const files: Record<string, string> = {};
5
+
6
+ // src/types/widgets.ts is no longer generated — the framework owns these
7
+ // string aliases (`ImageWidget`, `HTMLWidget`, …) at
8
+ // `@decocms/start/types/widgets`, and `transforms/imports.ts` rewrites
9
+ // `apps/admin/widgets.ts` directly to that path. Schema generation
10
+ // works the same way: the generator matches by type *text*, not module
11
+ // identity (see scripts/generate-schema.ts:WIDGET_TYPE_FORMATS).
12
+
13
+ files["src/types/deco.ts"] = `export type SectionProps<T extends (...args: any[]) => any> = Awaited<
14
+ ReturnType<T>
15
+ >;
16
+
17
+ export type Resolved<T = any> = T;
18
+
19
+ export type Section = any;
20
+
21
+ export type Block = any;
22
+
23
+ export type LoadingFallbackProps = {
24
+ height?: number;
25
+ };
26
+
27
+ export function asResolved<T>(value: T): T {
28
+ return value;
29
+ }
30
+
31
+ export function isDeferred(value: unknown): boolean {
32
+ return false;
33
+ }
34
+
35
+ export const context = {
36
+ isDeploy: false,
37
+ platform: "tanstack-start" as const,
38
+ site: "${ctx.siteName}",
39
+ siteId: 0,
40
+ };
41
+
42
+ export function redirect(_url: string, _status?: number): never {
43
+ throw new Error("redirect is not supported in TanStack Start -- use router navigation instead");
44
+ }
45
+ `;
46
+
47
+ files["src/types/commerce-app.ts"] = `export type AppContext = {
48
+ device: "mobile" | "desktop" | "tablet";
49
+ };
50
+ `;
51
+
52
+ // Compat shim for apps/website/loaders/extension.ts
53
+ files["src/types/website.ts"] = `export type ExtensionOf<T = any> = T;
54
+ `;
55
+
56
+ if (ctx.platform === "vtex") {
57
+ const vtexAccount = ctx.vtexAccount || ctx.siteName;
58
+ files["src/types/vtex-app.ts"] = `export interface VtexConfig {
59
+ account: string;
60
+ publicUrl?: string;
61
+ }
62
+
63
+ export interface AppInvoke {
64
+ vtex: {
65
+ loaders: {
66
+ user: (props: Record<string, unknown>) => Promise<any>;
67
+ address: { list: (props: Record<string, unknown>) => Promise<any> };
68
+ payments: { userPayments: (props: Record<string, unknown>) => Promise<any> };
69
+ intelligentSearch: {
70
+ productList: (props: any) => Promise<any>;
71
+ productListingPage: (props: any) => Promise<any>;
72
+ };
73
+ [key: string]: any;
74
+ };
75
+ actions: {
76
+ payments: { delete: (props: { id: string }) => Promise<any> };
77
+ [key: string]: any;
78
+ };
79
+ [key: string]: any;
80
+ };
81
+ site: {
82
+ loaders: {
83
+ Wishlist: Record<string, (props: any) => Promise<any>>;
84
+ [key: string]: any;
85
+ };
86
+ [key: string]: any;
87
+ };
88
+ [key: string]: any;
89
+ }
90
+
91
+ export type AppContext = {
92
+ device: "mobile" | "desktop" | "tablet";
93
+ platform: "vtex";
94
+ account: string;
95
+ invoke: AppInvoke;
96
+ [key: string]: unknown;
97
+ };
98
+
99
+ export type LegacyAppContext = AppContext;
100
+ `;
101
+
102
+ files["src/types/vtex-loaders.ts"] = `import type { Product, ProductListingPage } from "@decocms/apps/commerce/types";
103
+
104
+ export interface ProductListProps {
105
+ page: ProductListingPage | null;
106
+ }
107
+
108
+ export interface ProductDetailsProps {
109
+ page: {
110
+ product: Product;
111
+ seo?: { title?: string; description?: string; canonical?: string };
112
+ } | null;
113
+ }
114
+
115
+ export interface SearchProps {
116
+ query?: string;
117
+ page?: number;
118
+ sort?: string;
119
+ filters?: Record<string, string>;
120
+ }
121
+
122
+ export type LabelledFuzzy = "disabled" | "automatic" | "always";
123
+
124
+ export function mapLabelledFuzzyToFuzzy(fuzzy: LabelledFuzzy): string {
125
+ const mapping: Record<LabelledFuzzy, string> = { disabled: "0", automatic: "auto", always: "1" };
126
+ return mapping[fuzzy] ?? "0";
127
+ }
128
+
129
+ /** Props type compatible with intelligentSearch/productListingPage loader */
130
+ export interface PLPProps {
131
+ query?: string;
132
+ page?: number;
133
+ count?: number;
134
+ sort?: string;
135
+ selectedFacets?: Array<{ key: string; value: string }>;
136
+ fuzzy?: LabelledFuzzy;
137
+ hideUnavailableItems?: boolean;
138
+ [key: string]: unknown;
139
+ }
140
+ `;
141
+
142
+ files["src/types/vtex-actions.ts"] = `export interface UserMutation {
143
+ firstName?: string;
144
+ lastName?: string;
145
+ email?: string;
146
+ document?: string;
147
+ phone?: string;
148
+ gender?: string;
149
+ birthDate?: string;
150
+ corporateName?: string;
151
+ corporateDocument?: string;
152
+ stateRegistration?: string;
153
+ isCorporate?: boolean;
154
+ }
155
+
156
+ export interface AddressMutation {
157
+ addressName?: string;
158
+ addressType?: string;
159
+ postalCode?: string;
160
+ street?: string;
161
+ number?: string;
162
+ complement?: string;
163
+ neighborhood?: string;
164
+ city?: string;
165
+ state?: string;
166
+ country?: string;
167
+ receiverName?: string;
168
+ reference?: string;
169
+ }
170
+ `;
171
+ }
172
+
173
+ return files;
174
+ }
@@ -0,0 +1,144 @@
1
+ import type { MigrationContext } from "../types";
2
+
3
+ export function generateUiComponents(_ctx: MigrationContext): Record<string, string> {
4
+ const files: Record<string, string> = {};
5
+
6
+ files["src/components/ui/Image.tsx"] = `export {
7
+ Image as default,
8
+ Image,
9
+ getOptimizedMediaUrl,
10
+ getSrcSet,
11
+ registerImageCdnDomain,
12
+ getImageCdnDomain,
13
+ FACTORS,
14
+ type ImageProps,
15
+ type FitOptions,
16
+ } from "@decocms/apps/commerce/components/Image";
17
+ `;
18
+
19
+ files["src/components/ui/Picture.tsx"] = `import type { ReactNode } from "react";
20
+ import {
21
+ Image,
22
+ getSrcSet,
23
+ type FitOptions,
24
+ type ImageProps,
25
+ } from "@decocms/apps/commerce/components/Image";
26
+
27
+ export interface PictureSourceProps {
28
+ src: string;
29
+ width: number;
30
+ height?: number;
31
+ media: string;
32
+ fit?: FitOptions;
33
+ sizes?: string;
34
+ }
35
+
36
+ export interface PictureProps extends Omit<ImageProps, "sizes"> {
37
+ sources: PictureSourceProps[];
38
+ }
39
+
40
+ export function Source(props: PictureSourceProps & { fit?: FitOptions }) {
41
+ const srcSet = getSrcSet(props.src, props.width, props.height, props.fit ?? "cover");
42
+ return (
43
+ <source
44
+ srcSet={srcSet}
45
+ media={props.media}
46
+ width={props.width}
47
+ height={props.height}
48
+ sizes={props.sizes ?? \`\${props.width}px\`}
49
+ />
50
+ );
51
+ }
52
+
53
+ export function Picture({
54
+ sources,
55
+ src,
56
+ width,
57
+ height,
58
+ fit = "cover",
59
+ preload,
60
+ children,
61
+ ...rest
62
+ }: PictureProps & { children?: ReactNode }) {
63
+ return (
64
+ <picture>
65
+ {children ?? sources?.map((source, i) => (
66
+ <Source key={i} {...source} fit={source.fit ?? fit} />
67
+ ))}
68
+ {src && <Image src={src} width={width} height={height} fit={fit} preload={preload} {...rest} />}
69
+ </picture>
70
+ );
71
+ }
72
+ `;
73
+
74
+ files["src/components/ui/Video.tsx"] = `interface Props {
75
+ src: string;
76
+ width?: number;
77
+ height?: number;
78
+ autoPlay?: boolean;
79
+ muted?: boolean;
80
+ loop?: boolean;
81
+ playsInline?: boolean;
82
+ controls?: boolean;
83
+ className?: string;
84
+ loading?: "lazy" | "eager";
85
+ poster?: string;
86
+ }
87
+
88
+ export default function Video({
89
+ src,
90
+ width,
91
+ height,
92
+ autoPlay = true,
93
+ muted = true,
94
+ loop = true,
95
+ playsInline = true,
96
+ controls = false,
97
+ className,
98
+ poster,
99
+ }: Props) {
100
+ return (
101
+ <video
102
+ src={src}
103
+ width={width}
104
+ height={height}
105
+ autoPlay={autoPlay}
106
+ muted={muted}
107
+ loop={loop}
108
+ playsInline={playsInline}
109
+ controls={controls}
110
+ className={className}
111
+ poster={poster}
112
+ />
113
+ );
114
+ }
115
+ `;
116
+
117
+ files["src/components/ui/Seo.tsx"] = `export interface Props {
118
+ title?: string;
119
+ description?: string;
120
+ canonical?: string;
121
+ image?: string;
122
+ noIndexing?: boolean;
123
+ jsonLDs?: unknown[];
124
+ }
125
+
126
+ export default function Seo({ jsonLDs }: Props) {
127
+ if (!jsonLDs?.length) return null;
128
+
129
+ return (
130
+ <>
131
+ {jsonLDs.map((jsonLD, i) => (
132
+ <script
133
+ key={i}
134
+ type="application/ld+json"
135
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLD) }}
136
+ />
137
+ ))}
138
+ </>
139
+ );
140
+ }
141
+ `;
142
+
143
+ return files;
144
+ }
@@ -0,0 +1,101 @@
1
+ import type { MigrationContext } from "../types";
2
+
3
+ export function generateViteConfig(ctx: MigrationContext): string {
4
+ const isVtex = ctx.platform === "vtex";
5
+
6
+ const vtexAccount = ctx.vtexAccount || ctx.siteName.replace(/-migrated$/, "").replace(/-storefront$/, "");
7
+
8
+ const vtexProxy = isVtex ? `
9
+ // VTEX API proxy for local development
10
+ proxy: {
11
+ "/api/": {
12
+ target: VTEX_ORIGIN,
13
+ changeOrigin: true,
14
+ cookieDomainRewrite: { "*": "" },
15
+ },
16
+ "/checkout": {
17
+ target: VTEX_ORIGIN,
18
+ changeOrigin: true,
19
+ cookieDomainRewrite: { "*": "" },
20
+ },
21
+ },` : "";
22
+
23
+ const vtexConstants = isVtex ? `
24
+ const VTEX_ACCOUNT = "${vtexAccount}";
25
+ const VTEX_ENVIRONMENT = "vtexcommercestable";
26
+ const VTEX_DOMAIN = "com.br";
27
+ const VTEX_ORIGIN = \`https://\${VTEX_ACCOUNT}.\${VTEX_ENVIRONMENT}.\${VTEX_DOMAIN}\`;
28
+ ` : "";
29
+
30
+ return `import { cloudflare } from "@cloudflare/vite-plugin";
31
+ import { tanstackStart } from "@tanstack/react-start/plugin/vite";
32
+ import { decoVitePlugin } from "@decocms/start/vite";
33
+ import react from "@vitejs/plugin-react";
34
+ import tailwindcss from "@tailwindcss/vite";
35
+ import { defineConfig } from "vite";
36
+ import path from "path";
37
+
38
+ const srcDir = path.resolve(__dirname, "src");
39
+ ${vtexConstants}
40
+ export default defineConfig({
41
+ server: {
42
+ allowedHosts: [".decocdn.com"],${vtexProxy}
43
+ },
44
+ plugins: [
45
+ cloudflare({ viteEnvironment: { name: "ssr" } }),
46
+ tanstackStart({ server: { entry: "server" } }),
47
+ react({
48
+ babel: {
49
+ plugins: [
50
+ ["babel-plugin-react-compiler", { target: "19" }],
51
+ ],
52
+ },
53
+ }),
54
+ tailwindcss(),
55
+ decoVitePlugin(),
56
+ ],
57
+ build: {
58
+ sourcemap: "hidden",
59
+ rollupOptions: {
60
+ onLog(level, log, handler) {
61
+ if (
62
+ log.code === "PLUGIN_WARNING" &&
63
+ log.plugin === "vite:reporter" &&
64
+ log.message?.includes("dynamic import will not move module")
65
+ ) {
66
+ return;
67
+ }
68
+ handler(level, log);
69
+ },
70
+ },
71
+ },
72
+ define: {
73
+ "process.env.DECO_SITE_NAME": JSON.stringify(
74
+ process.env.DECO_SITE_NAME || "${ctx.siteName}"
75
+ ),
76
+ },
77
+ esbuild: {
78
+ jsx: "automatic",
79
+ jsxImportSource: "react",
80
+ },
81
+ resolve: {
82
+ dedupe: [
83
+ "@decocms/start",
84
+ "@decocms/apps",
85
+ "@tanstack/react-start",
86
+ "@tanstack/react-router",
87
+ "@tanstack/react-start-server",
88
+ "@tanstack/start-server-core",
89
+ "@tanstack/start-client-core",
90
+ "@tanstack/start-plugin-core",
91
+ "@tanstack/start-storage-context",
92
+ "react",
93
+ "react-dom",
94
+ ],
95
+ alias: {
96
+ "~": srcDir,
97
+ },
98
+ },
99
+ });
100
+ `;
101
+ }