@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
|
@@ -43,7 +43,7 @@ function generateWorkerEntry(ctx: MigrationContext): string {
|
|
|
43
43
|
* Cloudflare Worker entry point.
|
|
44
44
|
*
|
|
45
45
|
* Wraps TanStack Start with admin protocol handlers, edge caching, and
|
|
46
|
-
* the @decocms/
|
|
46
|
+
* the @decocms/blocks observability stack (5.0+, Cloudflare-native):
|
|
47
47
|
* - logs: console.* -> CF Workers Logs (captured by the platform
|
|
48
48
|
* when wrangler.jsonc has \`observability.enabled: true\`).
|
|
49
49
|
* View in the CF dashboard -> Workers & Pages -> <site> ->
|
|
@@ -57,27 +57,27 @@ function generateWorkerEntry(ctx: MigrationContext): string {
|
|
|
57
57
|
*
|
|
58
58
|
* No in-Worker OTLP exporter ships with 5.x — the CF dashboard is the
|
|
59
59
|
* destination. A ClickHouse-collector adapter is scaffolded at
|
|
60
|
-
* @decocms/
|
|
60
|
+
* @decocms/blocks/sdk/otelAdapters/clickhouseCollector but throws if
|
|
61
61
|
* called; it'll get a real implementation once the OTel collector
|
|
62
62
|
* gateway lands.
|
|
63
63
|
*
|
|
64
64
|
* To wire wrangler.jsonc with the canonical observability block, run:
|
|
65
|
-
* npx -p @decocms/
|
|
65
|
+
* npx -p @decocms/blocks-cli deco-cf-observability --write
|
|
66
66
|
*/
|
|
67
67
|
import "./setup";
|
|
68
68
|
import handler, { createServerEntry } from "@tanstack/react-start/server-entry";
|
|
69
|
-
import { createDecoWorkerEntry } from "@decocms/
|
|
70
|
-
import { instrumentWorker } from "@decocms/
|
|
69
|
+
import { createDecoWorkerEntry } from "@decocms/tanstack";
|
|
70
|
+
import { instrumentWorker } from "@decocms/blocks/sdk/observability";
|
|
71
71
|
import {
|
|
72
72
|
handleMeta,
|
|
73
73
|
handleDecofileRead,
|
|
74
74
|
handleDecofileReload,
|
|
75
75
|
handleRender,
|
|
76
76
|
corsHeaders,
|
|
77
|
-
} from "@decocms/
|
|
77
|
+
} from "@decocms/blocks-admin";
|
|
78
78
|
${isCommerce ? `
|
|
79
79
|
// TODO: Uncomment and wire proxy for ${platformLabel}
|
|
80
|
-
// import { shouldProxyTo${capitalize(platformLabel!)}, proxyTo${capitalize(platformLabel!)} } from "@decocms/apps
|
|
80
|
+
// import { shouldProxyTo${capitalize(platformLabel!)}, proxyTo${capitalize(platformLabel!)} } from "@decocms/apps-${platformLabel}/utils/proxy";
|
|
81
81
|
` : ""}
|
|
82
82
|
const serverEntry = createServerEntry({ fetch: handler.fetch });
|
|
83
83
|
|
|
@@ -110,23 +110,23 @@ function generateVtexWorkerEntry(ctx: MigrationContext): string {
|
|
|
110
110
|
|
|
111
111
|
return `import "./setup";
|
|
112
112
|
import handler, { createServerEntry } from "@tanstack/react-start/server-entry";
|
|
113
|
-
import { createDecoWorkerEntry } from "@decocms/
|
|
114
|
-
import { instrumentWorker } from "@decocms/
|
|
113
|
+
import { createDecoWorkerEntry } from "@decocms/tanstack";
|
|
114
|
+
import { instrumentWorker } from "@decocms/blocks/sdk/observability";
|
|
115
115
|
import {
|
|
116
116
|
handleMeta,
|
|
117
117
|
handleDecofileRead,
|
|
118
118
|
handleDecofileReload,
|
|
119
119
|
handleRender,
|
|
120
120
|
corsHeaders,
|
|
121
|
-
} from "@decocms/
|
|
122
|
-
import { shouldProxyToVtex, createVtexCheckoutProxy } from "@decocms/apps
|
|
123
|
-
import { extractVtexContext } from "@decocms/apps
|
|
124
|
-
import { loadRedirects, matchRedirect } from "@decocms/
|
|
125
|
-
import { withABTesting } from "@decocms/
|
|
126
|
-
import { loadBlocks } from "@decocms/
|
|
121
|
+
} from "@decocms/blocks-admin";
|
|
122
|
+
import { shouldProxyToVtex, createVtexCheckoutProxy } from "@decocms/apps-vtex/utils/proxy";
|
|
123
|
+
import { extractVtexContext } from "@decocms/apps-vtex/middleware";
|
|
124
|
+
import { loadRedirects, matchRedirect } from "@decocms/blocks/sdk/redirects";
|
|
125
|
+
import { withABTesting } from "@decocms/blocks/sdk/abTesting";
|
|
126
|
+
import { loadBlocks } from "@decocms/blocks/cms";
|
|
127
127
|
|
|
128
128
|
// ---------------------------------------------------------------------------
|
|
129
|
-
// VTEX checkout proxy — configured via @decocms/apps factory
|
|
129
|
+
// VTEX checkout proxy — configured via @decocms/apps-vtex factory
|
|
130
130
|
// ---------------------------------------------------------------------------
|
|
131
131
|
|
|
132
132
|
const proxyCheckout = createVtexCheckoutProxy({
|
|
@@ -226,7 +226,7 @@ const abTestedWorker = withABTesting(decoWorker, {
|
|
|
226
226
|
// ---------------------------------------------------------------------------
|
|
227
227
|
// Observability wrap (outermost layer)
|
|
228
228
|
//
|
|
229
|
-
// @decocms/
|
|
229
|
+
// @decocms/blocks 5.0+ converged on Cloudflare-native observability:
|
|
230
230
|
// - logs: console.* -> CF Workers Logs (dashboard captures, no
|
|
231
231
|
// app-side OTLP exporter)
|
|
232
232
|
// - traces: @opentelemetry/api global tracer (bridged from
|
|
@@ -236,7 +236,7 @@ const abTestedWorker = withABTesting(decoWorker, {
|
|
|
236
236
|
// src/sdk/otelAdapters/clickhouseCollector.ts)
|
|
237
237
|
//
|
|
238
238
|
// Wire wrangler.jsonc with the canonical observability block via:
|
|
239
|
-
// npx -p @decocms/
|
|
239
|
+
// npx -p @decocms/blocks-cli deco-cf-observability --write
|
|
240
240
|
// ---------------------------------------------------------------------------
|
|
241
241
|
export default instrumentWorker(abTestedWorker, {
|
|
242
242
|
serviceName: "${ctx.siteName}",
|
|
@@ -246,7 +246,7 @@ export default instrumentWorker(abTestedWorker, {
|
|
|
246
246
|
|
|
247
247
|
function generateRouter(): string {
|
|
248
248
|
return `import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
249
|
-
import { createDecoRouter } from "@decocms/
|
|
249
|
+
import { createDecoRouter } from "@decocms/tanstack";
|
|
250
250
|
import { routeTree } from "./routeTree.gen";
|
|
251
251
|
import "./setup";
|
|
252
252
|
|
|
@@ -274,16 +274,16 @@ declare module "@tanstack/react-router" {
|
|
|
274
274
|
|
|
275
275
|
function generateRuntime(): string {
|
|
276
276
|
return `/**
|
|
277
|
-
* Runtime invoke proxy — re-exports the framework canonical from @decocms/
|
|
277
|
+
* Runtime invoke proxy — re-exports the framework canonical from @decocms/blocks/sdk/invoke.
|
|
278
278
|
*
|
|
279
279
|
* The implementation (typed RPC over /deco/invoke, dotted-path proxy, .ts
|
|
280
|
-
* suffix fallback) lives in @decocms/
|
|
280
|
+
* suffix fallback) lives in @decocms/blocks/sdk/invoke. This file exists so
|
|
281
281
|
* existing site code can keep \`import { invoke } from "~/runtime"\` and
|
|
282
282
|
* \`Runtime.invoke\` shapes without churn.
|
|
283
283
|
*
|
|
284
|
-
* Don't reimplement here — extend @decocms/
|
|
284
|
+
* Don't reimplement here — extend @decocms/blocks/sdk/invoke instead.
|
|
285
285
|
*/
|
|
286
|
-
import { invoke } from "@decocms/
|
|
286
|
+
import { invoke } from "@decocms/blocks/sdk/invoke";
|
|
287
287
|
|
|
288
288
|
export { invoke };
|
|
289
289
|
|
|
@@ -328,14 +328,14 @@ export const invoke = {} as const;
|
|
|
328
328
|
*/
|
|
329
329
|
import { createServerFn } from "@tanstack/react-start";
|
|
330
330
|
import { getRequestHeader } from "@tanstack/react-start/server";
|
|
331
|
-
import { forwardResponseCookies } from "@decocms/
|
|
331
|
+
import { forwardResponseCookies } from "@decocms/tanstack/sdk/cookiePassthrough";
|
|
332
332
|
import { vtexActions } from "./invoke.gen";
|
|
333
333
|
${hasVtexAuthLoader ? `import vtexAuthLoader from "../loaders/vtex-auth-loader";\n` : ""}import {
|
|
334
334
|
extractVtexCookiesFromHeader,
|
|
335
335
|
stripCookieDomain,
|
|
336
336
|
performVtexLogout,
|
|
337
337
|
parseVtexAuthJwt,
|
|
338
|
-
} from "@decocms/apps
|
|
338
|
+
} from "@decocms/apps-vtex/utils/authHelpers";
|
|
339
339
|
|
|
340
340
|
export type { OrderForm } from "./invoke.gen";
|
|
341
341
|
|
|
@@ -398,17 +398,17 @@ export const vtexActions = {} as const;
|
|
|
398
398
|
// Each server function is a top-level const so TanStack Start's compiler
|
|
399
399
|
// can transform createServerFn().handler() into RPC stubs on the client.
|
|
400
400
|
import { createServerFn } from "@tanstack/react-start";
|
|
401
|
-
import { getOrCreateCart, addItemsToCart, updateCartItems, addCouponToCart, simulateCart, getSellersByRegion, setShippingPostalCode, updateOrderFormAttachment } from "@decocms/apps
|
|
402
|
-
import { createSession, editSession } from "@decocms/apps
|
|
403
|
-
import { createDocument, getDocument, patchDocument, searchDocuments, uploadAttachment } from "@decocms/apps
|
|
404
|
-
import { subscribe } from "@decocms/apps
|
|
405
|
-
import { notifyMe } from "@decocms/apps
|
|
406
|
-
import type { OrderForm } from "@decocms/apps
|
|
407
|
-
import type { SimulationItem, RegionResult } from "@decocms/apps
|
|
408
|
-
import type { SessionData } from "@decocms/apps
|
|
409
|
-
import type { CreateDocumentResult, UploadAttachmentOpts } from "@decocms/apps
|
|
410
|
-
import type { SubscribeProps } from "@decocms/apps
|
|
411
|
-
import type { NotifyMeProps } from "@decocms/apps
|
|
401
|
+
import { getOrCreateCart, addItemsToCart, updateCartItems, addCouponToCart, simulateCart, getSellersByRegion, setShippingPostalCode, updateOrderFormAttachment } from "@decocms/apps-vtex/actions/checkout";
|
|
402
|
+
import { createSession, editSession } from "@decocms/apps-vtex/actions/session";
|
|
403
|
+
import { createDocument, getDocument, patchDocument, searchDocuments, uploadAttachment } from "@decocms/apps-vtex/actions/masterData";
|
|
404
|
+
import { subscribe } from "@decocms/apps-vtex/actions/newsletter";
|
|
405
|
+
import { notifyMe } from "@decocms/apps-vtex/actions/misc";
|
|
406
|
+
import type { OrderForm } from "@decocms/apps-vtex/types";
|
|
407
|
+
import type { SimulationItem, RegionResult } from "@decocms/apps-vtex/actions/checkout";
|
|
408
|
+
import type { SessionData } from "@decocms/apps-vtex/actions/session";
|
|
409
|
+
import type { CreateDocumentResult, UploadAttachmentOpts } from "@decocms/apps-vtex/actions/masterData";
|
|
410
|
+
import type { SubscribeProps } from "@decocms/apps-vtex/actions/newsletter";
|
|
411
|
+
import type { NotifyMeProps } from "@decocms/apps-vtex/actions/misc";
|
|
412
412
|
|
|
413
413
|
function unwrapResult<T>(result: unknown): T {
|
|
414
414
|
if (result && typeof result === "object" && "data" in result) {
|
|
@@ -549,7 +549,7 @@ export const vtexActions = {
|
|
|
549
549
|
notifyMe: $notifyMe as unknown as (ctx: { data: NotifyMeProps }) => Promise<void>,
|
|
550
550
|
} as const;
|
|
551
551
|
|
|
552
|
-
export type { OrderForm } from "@decocms/apps
|
|
552
|
+
export type { OrderForm } from "@decocms/apps-vtex/types";
|
|
553
553
|
|
|
554
554
|
export const invoke = {
|
|
555
555
|
vtex: {
|
|
@@ -93,15 +93,15 @@ import "./cache-config";
|
|
|
93
93
|
import {
|
|
94
94
|
registerCommerceLoaders,
|
|
95
95
|
applySectionConventions,
|
|
96
|
-
} from "@decocms/
|
|
97
|
-
import { createSiteSetup } from "@decocms/
|
|
98
|
-
import { setInvokeLoaders } from "@decocms/
|
|
99
|
-
import { createInstrumentedFetch } from "@decocms/
|
|
100
|
-
import { initVtexFromBlocks, setVtexFetch } from "@decocms/apps
|
|
96
|
+
} from "@decocms/blocks/cms";
|
|
97
|
+
import { createSiteSetup } from "@decocms/blocks/setup";
|
|
98
|
+
import { setInvokeLoaders } from "@decocms/blocks-admin";${isVtex ? `
|
|
99
|
+
import { createInstrumentedFetch } from "@decocms/blocks/sdk/instrumentedFetch";
|
|
100
|
+
import { initVtexFromBlocks, setVtexFetch } from "@decocms/apps-vtex";` : ""}${hasLocationMatcher ? `
|
|
101
101
|
import { registerLocationMatcher } from "./matchers/location";` : ""}
|
|
102
102
|
import { blocks as generatedBlocks } from "../.deco/blocks.gen";
|
|
103
103
|
import { sectionMeta, syncComponents, loadingFallbacks } from "../.deco/sections.gen";
|
|
104
|
-
import { PreviewProviders } from "@decocms/
|
|
104
|
+
import { PreviewProviders } from "@decocms/tanstack";
|
|
105
105
|
// @ts-ignore Vite ?url import
|
|
106
106
|
import appCss from "./styles/app.css?url";
|
|
107
107
|
|
|
@@ -5,7 +5,7 @@ export function generateTypeFiles(ctx: MigrationContext): Record<string, string>
|
|
|
5
5
|
|
|
6
6
|
// src/types/widgets.ts is no longer generated — the framework owns these
|
|
7
7
|
// string aliases (`ImageWidget`, `HTMLWidget`, …) at
|
|
8
|
-
// `@decocms/
|
|
8
|
+
// `@decocms/blocks/types/widgets`, and `transforms/imports.ts` rewrites
|
|
9
9
|
// `apps/admin/widgets.ts` directly to that path. Schema generation
|
|
10
10
|
// works the same way: the generator matches by type *text*, not module
|
|
11
11
|
// identity (see scripts/generate-schema.ts:WIDGET_TYPE_FORMATS).
|
|
@@ -99,7 +99,7 @@ export type AppContext = {
|
|
|
99
99
|
export type LegacyAppContext = AppContext;
|
|
100
100
|
`;
|
|
101
101
|
|
|
102
|
-
files["src/types/vtex-loaders.ts"] = `import type { Product, ProductListingPage } from "@decocms/apps
|
|
102
|
+
files["src/types/vtex-loaders.ts"] = `import type { Product, ProductListingPage } from "@decocms/apps-commerce/types";
|
|
103
103
|
|
|
104
104
|
export interface ProductListProps {
|
|
105
105
|
page: ProductListingPage | null;
|
|
@@ -13,7 +13,7 @@ export function generateUiComponents(_ctx: MigrationContext): Record<string, str
|
|
|
13
13
|
FACTORS,
|
|
14
14
|
type ImageProps,
|
|
15
15
|
type FitOptions,
|
|
16
|
-
} from "@decocms/
|
|
16
|
+
} from "@decocms/blocks/hooks";
|
|
17
17
|
`;
|
|
18
18
|
|
|
19
19
|
files["src/components/ui/Picture.tsx"] = `import type { ReactNode } from "react";
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
getSrcSet,
|
|
23
23
|
type FitOptions,
|
|
24
24
|
type ImageProps,
|
|
25
|
-
} from "@decocms/
|
|
25
|
+
} from "@decocms/blocks/hooks";
|
|
26
26
|
|
|
27
27
|
export interface PictureSourceProps {
|
|
28
28
|
src: string;
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type { MigrationContext } from "../types";
|
|
2
2
|
|
|
3
|
+
const PLATFORM_PACKAGE: Partial<Record<MigrationContext["platform"], string>> = {
|
|
4
|
+
vtex: "@decocms/apps-vtex",
|
|
5
|
+
shopify: "@decocms/apps-shopify",
|
|
6
|
+
magento: "@decocms/apps-magento",
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
export function generateViteConfig(ctx: MigrationContext): string {
|
|
4
10
|
const isVtex = ctx.platform === "vtex";
|
|
11
|
+
const platformDep = PLATFORM_PACKAGE[ctx.platform];
|
|
5
12
|
|
|
6
13
|
const vtexAccount = ctx.vtexAccount || ctx.siteName.replace(/-migrated$/, "").replace(/-storefront$/, "");
|
|
7
14
|
|
|
@@ -29,7 +36,7 @@ const VTEX_ORIGIN = \`https://\${VTEX_ACCOUNT}.\${VTEX_ENVIRONMENT}.\${VTEX_DOMA
|
|
|
29
36
|
|
|
30
37
|
return `import { cloudflare } from "@cloudflare/vite-plugin";
|
|
31
38
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
32
|
-
import { decoVitePlugin } from "@decocms/
|
|
39
|
+
import { decoVitePlugin } from "@decocms/tanstack/vite";
|
|
33
40
|
import react from "@vitejs/plugin-react";
|
|
34
41
|
import tailwindcss from "@tailwindcss/vite";
|
|
35
42
|
import { defineConfig } from "vite";
|
|
@@ -80,8 +87,10 @@ export default defineConfig({
|
|
|
80
87
|
},
|
|
81
88
|
resolve: {
|
|
82
89
|
dedupe: [
|
|
83
|
-
"@decocms/
|
|
84
|
-
"@decocms/
|
|
90
|
+
"@decocms/blocks",
|
|
91
|
+
"@decocms/blocks-admin",
|
|
92
|
+
"@decocms/tanstack",
|
|
93
|
+
"@decocms/apps-commerce",${platformDep ? `\n "${platformDep}",` : ""}
|
|
85
94
|
"@tanstack/react-start",
|
|
86
95
|
"@tanstack/react-router",
|
|
87
96
|
"@tanstack/react-start-server",
|
|
@@ -165,10 +165,10 @@ export function transformFreshApis(content: string): TransformResult {
|
|
|
165
165
|
if (result.includes("scriptAsDataURI")) {
|
|
166
166
|
// Ensure useScript is imported
|
|
167
167
|
if (
|
|
168
|
-
!result.includes('"@decocms/
|
|
169
|
-
!result.includes("'@decocms/
|
|
168
|
+
!result.includes('"@decocms/blocks/sdk/useScript"') &&
|
|
169
|
+
!result.includes("'@decocms/blocks/sdk/useScript'")
|
|
170
170
|
) {
|
|
171
|
-
result = `import { useScript } from "@decocms/
|
|
171
|
+
result = `import { useScript } from "@decocms/blocks/sdk/useScript";\n${result}`;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
// Transform src={scriptAsDataURI(...)} into dangerouslySetInnerHTML={{ __html: useScript(...) }}
|
|
@@ -190,7 +190,7 @@ export function transformFreshApis(content: string): TransformResult {
|
|
|
190
190
|
notes.push("Replaced scriptAsDataURI with useScript + dangerouslySetInnerHTML");
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
// allowCorsFor — not available in @decocms/
|
|
193
|
+
// allowCorsFor — not available in @decocms/blocks, remove usage
|
|
194
194
|
if (result.includes("allowCorsFor")) {
|
|
195
195
|
result = result.replace(
|
|
196
196
|
/^import\s+\{[^}]*\ballowCorsFor\b[^}]*\}\s+from\s+["'][^"']+["'];?\s*\n?/gm,
|
|
@@ -204,7 +204,7 @@ export function transformFreshApis(content: string): TransformResult {
|
|
|
204
204
|
|
|
205
205
|
// ctx.response.headers → not available, flag
|
|
206
206
|
if (result.includes("ctx.response")) {
|
|
207
|
-
notes.push("MANUAL: ctx.response usage found — FnContext in @decocms/
|
|
207
|
+
notes.push("MANUAL: ctx.response usage found — FnContext in @decocms/blocks does not have response object");
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// { crypto } from "@std/crypto" → use globalThis.crypto (Web Crypto API)
|
|
@@ -20,7 +20,7 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
20
20
|
[/^"@preact\/signals"$/, `"~/sdk/signal"`],
|
|
21
21
|
|
|
22
22
|
// Deco framework — hooks need splitting (useDevice, useScript, useSection)
|
|
23
|
-
[/^"@deco\/deco\/hooks"$/, `"@decocms/
|
|
23
|
+
[/^"@deco\/deco\/hooks"$/, `"@decocms/blocks/sdk/useScript"`],
|
|
24
24
|
[/^"@deco\/deco\/blocks"$/, `"~/types/deco"`],
|
|
25
25
|
[/^"@deco\/deco\/o11y"$/, null], // logger — use console.log/warn/error instead
|
|
26
26
|
[/^"@deco\/deco\/web"$/, null], // runtime.ts is rewritten
|
|
@@ -32,66 +32,66 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
32
32
|
// Widget aliases (ImageWidget, HTMLWidget, ...) are framework-owned —
|
|
33
33
|
// every site has the same type set, and the schema generator detects
|
|
34
34
|
// them via type-text matching, not module identity. Re-export from
|
|
35
|
-
// @decocms/
|
|
35
|
+
// @decocms/blocks/types/widgets so we don't keep a duplicated 8-line
|
|
36
36
|
// file in every site.
|
|
37
|
-
[/^"apps\/admin\/widgets\.ts"$/, `"@decocms/
|
|
37
|
+
[/^"apps\/admin\/widgets\.ts"$/, `"@decocms/blocks/types/widgets"`],
|
|
38
38
|
[/^"apps\/website\/components\/Image\.tsx"$/, `"~/components/ui/Image"`],
|
|
39
39
|
[/^"apps\/website\/components\/Picture\.tsx"$/, `"~/components/ui/Picture"`],
|
|
40
40
|
[/^"apps\/website\/components\/Video\.tsx"$/, `"~/components/ui/Video"`],
|
|
41
41
|
[/^"apps\/website\/components\/Theme\.tsx"$/, `"~/components/ui/Theme"`],
|
|
42
42
|
[/^"apps\/website\/components\/_seo\/[^"]+?"$/, null], // SEO preview — framework-only, remove
|
|
43
43
|
[/^"apps\/website\/components\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
|
|
44
|
-
[/^"apps\/commerce\/types\.ts"$/, `"@decocms/apps
|
|
44
|
+
[/^"apps\/commerce\/types\.ts"$/, `"@decocms/apps-commerce/types"`],
|
|
45
45
|
[/^"apps\/commerce\/mod\.ts"$/, `"~/types/commerce-app"`],
|
|
46
|
-
[/^"apps\/commerce\/types"$/, `"@decocms/apps
|
|
46
|
+
[/^"apps\/commerce\/types"$/, `"@decocms/apps-commerce/types"`],
|
|
47
47
|
|
|
48
|
-
// Apps — VTEX hooks: useUser/useCart/useWishlist → local hooks (react-query based @decocms/apps hooks crash Workers SSR)
|
|
48
|
+
// Apps — VTEX hooks: useUser/useCart/useWishlist → local hooks (react-query based @decocms/apps-vtex hooks crash Workers SSR)
|
|
49
49
|
[/^"apps\/vtex\/hooks\/useUser(?:\.ts)?"$/, `"~/hooks/useUser"`],
|
|
50
50
|
[/^"apps\/vtex\/hooks\/useCart(?:\.ts)?"$/, `"~/hooks/useCart"`],
|
|
51
51
|
[/^"apps\/vtex\/hooks\/useWishlist(?:\.ts)?"$/, `"~/hooks/useWishlist"`],
|
|
52
|
-
[/^"apps\/vtex\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
53
|
-
// Specific VTEX utils that moved to different paths in @decocms/apps
|
|
52
|
+
[/^"apps\/vtex\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/hooks/$1"`],
|
|
53
|
+
// Specific VTEX utils that moved to different paths in @decocms/apps-vtex
|
|
54
54
|
// fetchVTEX (generic fetchSafe + QS sanitization) lives at vtex/utils/fetch in apps-start.
|
|
55
|
-
[/^"apps\/vtex\/utils\/fetchVTEX(?:\.ts)?"$/, `"@decocms/apps
|
|
56
|
-
[/^"apps\/vtex\/utils\/client(?:\.ts)?"$/, `"@decocms/apps
|
|
57
|
-
[/^"apps\/vtex\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
58
|
-
[/^"apps\/vtex\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
55
|
+
[/^"apps\/vtex\/utils\/fetchVTEX(?:\.ts)?"$/, `"@decocms/apps-vtex/utils/fetch"`],
|
|
56
|
+
[/^"apps\/vtex\/utils\/client(?:\.ts)?"$/, `"@decocms/apps-vtex/client"`],
|
|
57
|
+
[/^"apps\/vtex\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/utils/$1"`],
|
|
58
|
+
[/^"apps\/vtex\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/actions/$1"`],
|
|
59
59
|
// Tier B loader path rewrites (apps-start has no `intelligentSearch/`, `legacy/<file>`, or `paths/` subdirs).
|
|
60
60
|
// Intelligent Search loaders moved to inline-loaders/.
|
|
61
61
|
[
|
|
62
62
|
/^"apps\/vtex\/loaders\/intelligentSearch\/productList(?:\.ts)?"$/,
|
|
63
|
-
`"@decocms/apps
|
|
63
|
+
`"@decocms/apps-vtex/inline-loaders/productList"`,
|
|
64
64
|
],
|
|
65
65
|
[
|
|
66
66
|
/^"apps\/vtex\/loaders\/intelligentSearch\/productListingPage(?:\.ts)?"$/,
|
|
67
|
-
`"@decocms/apps
|
|
67
|
+
`"@decocms/apps-vtex/inline-loaders/productListingPage"`,
|
|
68
68
|
],
|
|
69
69
|
[
|
|
70
70
|
/^"apps\/vtex\/loaders\/intelligentSearch\/productDetailsPage(?:\.ts)?"$/,
|
|
71
|
-
`"@decocms/apps
|
|
71
|
+
`"@decocms/apps-vtex/inline-loaders/productDetailsPage"`,
|
|
72
72
|
],
|
|
73
73
|
[
|
|
74
74
|
/^"apps\/vtex\/loaders\/intelligentSearch\/suggestions(?:\.ts)?"$/,
|
|
75
|
-
`"@decocms/apps
|
|
75
|
+
`"@decocms/apps-vtex/inline-loaders/suggestions"`,
|
|
76
76
|
],
|
|
77
77
|
// Legacy product loaders are consolidated into a single file (named exports).
|
|
78
78
|
[
|
|
79
79
|
/^"apps\/vtex\/loaders\/legacy\/(?:productList|productListingPage|productDetailsPage|search|category)(?:\.ts)?"$/,
|
|
80
|
-
`"@decocms/apps
|
|
80
|
+
`"@decocms/apps-vtex/loaders/legacy"`,
|
|
81
81
|
],
|
|
82
82
|
// Path-default loaders (sitemap seeds) don't exist in TanStack Start — paths resolve at request time.
|
|
83
83
|
[/^"apps\/vtex\/loaders\/paths\/(?:[^"]+)(?:\.ts)?"$/, null],
|
|
84
|
-
[/^"apps\/vtex\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
85
|
-
[/^"apps\/vtex\/types(?:\.ts)?"$/, `"@decocms/apps
|
|
84
|
+
[/^"apps\/vtex\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-vtex/loaders/$1"`],
|
|
85
|
+
[/^"apps\/vtex\/types(?:\.ts)?"$/, `"@decocms/apps-vtex/types"`],
|
|
86
86
|
[/^"apps\/vtex\/mod(?:\.ts)?"$/, `"~/types/vtex-app"`],
|
|
87
87
|
// Apps — Shopify (hooks, utils, actions, loaders)
|
|
88
|
-
[/^"apps\/shopify\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
89
|
-
[/^"apps\/shopify\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
90
|
-
[/^"apps\/shopify\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
91
|
-
[/^"apps\/shopify\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
88
|
+
[/^"apps\/shopify\/hooks\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-shopify/hooks/$1"`],
|
|
89
|
+
[/^"apps\/shopify\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-shopify/utils/$1"`],
|
|
90
|
+
[/^"apps\/shopify\/actions\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-shopify/actions/$1"`],
|
|
91
|
+
[/^"apps\/shopify\/loaders\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-shopify/loaders/$1"`],
|
|
92
92
|
// Apps — commerce (types, SDK, utils)
|
|
93
|
-
[/^"apps\/commerce\/sdk\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
94
|
-
[/^"apps\/commerce\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps
|
|
93
|
+
[/^"apps\/commerce\/sdk\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-commerce/sdk/$1"`],
|
|
94
|
+
[/^"apps\/commerce\/utils\/([^"]+?)(?:\.ts)?"$/, `"@decocms/apps-commerce/utils/$1"`],
|
|
95
95
|
|
|
96
96
|
// Apps — shared utils (STALE, fetchSafe, createHttpClient, etc.)
|
|
97
97
|
[/^"apps\/utils\/fetch(?:\.ts)?"$/, `"~/lib/fetch-utils"`],
|
|
@@ -116,7 +116,7 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
116
116
|
[/^"@std\/crypto"$/, null], // Use globalThis.crypto instead
|
|
117
117
|
|
|
118
118
|
// site/sdk/* → framework equivalents (before the catch-all site/ → ~/ rule)
|
|
119
|
-
[/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/
|
|
119
|
+
[/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/blocks/sdk/clx"`],
|
|
120
120
|
[/^"site\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
|
|
121
121
|
// useOffer and useVariantPossiblities kept as site files (~/sdk/)
|
|
122
122
|
[/^"site\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
|
|
@@ -127,10 +127,10 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
127
127
|
[/^"~\/account\.json"$/, `"~/constants/account"`],
|
|
128
128
|
|
|
129
129
|
// $store/ → ~/ (common Deno import map alias for project root)
|
|
130
|
-
[/^"\$store\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/
|
|
130
|
+
[/^"\$store\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/blocks/sdk/clx"`],
|
|
131
131
|
[/^"\$store\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
|
|
132
132
|
// useOffer and useVariantPossiblities kept as site files (~/sdk/)
|
|
133
|
-
[/^"\$store\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps
|
|
133
|
+
[/^"\$store\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps-commerce/sdk/formatPrice"`],
|
|
134
134
|
[/^"\$store\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
|
|
135
135
|
// islands → components (must be before $store catch-all)
|
|
136
136
|
[/^"\$store\/islands\/ui\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
|
|
@@ -141,10 +141,10 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
141
141
|
[/^"\$home\/(.+)"$/, `"~/$1"`],
|
|
142
142
|
|
|
143
143
|
// site/ → ~/
|
|
144
|
-
[/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/
|
|
144
|
+
[/^"site\/sdk\/clx(?:\.tsx?)?.*"$/, `"@decocms/blocks/sdk/clx"`],
|
|
145
145
|
[/^"site\/sdk\/useId(?:\.tsx?)?.*"$/, `"react"`],
|
|
146
146
|
// useOffer and useVariantPossiblities kept as site files (~/sdk/)
|
|
147
|
-
[/^"site\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps
|
|
147
|
+
[/^"site\/sdk\/format(?:\.tsx?)?.*"$/, `"@decocms/apps-commerce/sdk/formatPrice"`],
|
|
148
148
|
[/^"site\/sdk\/usePlatform(?:\.tsx?)?.*"$/, null],
|
|
149
149
|
// islands → components (must be before site/ catch-all)
|
|
150
150
|
[/^"site\/islands\/ui\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
|
|
@@ -155,10 +155,16 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
155
155
|
[/^"~\/islands\/ui\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/ui/$1"`],
|
|
156
156
|
[/^"~\/islands\/([^"]+?)(?:\.tsx?)?"$/, `"~/components/$1"`],
|
|
157
157
|
|
|
158
|
-
// @decocms/apps hooks → local hooks (react-query hooks crash Workers SSR at module eval)
|
|
158
|
+
// @decocms/apps-vtex hooks → local hooks (react-query hooks crash Workers SSR at module eval)
|
|
159
|
+
// Pre-7.x monolith path — kept for sites whose Deno import map already
|
|
160
|
+
// aliased directly to the npm specifier instead of the "apps/vtex/..." form.
|
|
159
161
|
[/^"@decocms\/apps\/vtex\/hooks\/useUser"$/, `"~/hooks/useUser"`],
|
|
160
162
|
[/^"@decocms\/apps\/vtex\/hooks\/useCart"$/, `"~/hooks/useCart"`],
|
|
161
163
|
[/^"@decocms\/apps\/vtex\/hooks\/useWishlist"$/, `"~/hooks/useWishlist"`],
|
|
164
|
+
// Post-7.x split package path — same rationale, current package name.
|
|
165
|
+
[/^"@decocms\/apps-vtex\/hooks\/useUser"$/, `"~/hooks/useUser"`],
|
|
166
|
+
[/^"@decocms\/apps-vtex\/hooks\/useCart"$/, `"~/hooks/useCart"`],
|
|
167
|
+
[/^"@decocms\/apps-vtex\/hooks\/useWishlist"$/, `"~/hooks/useWishlist"`],
|
|
162
168
|
];
|
|
163
169
|
|
|
164
170
|
/**
|
|
@@ -167,14 +173,14 @@ const IMPORT_RULES: Array<[RegExp, string | null]> = [
|
|
|
167
173
|
* The key is the ending of the import path, the value is the replacement specifier.
|
|
168
174
|
*/
|
|
169
175
|
const RELATIVE_SDK_REWRITES: Array<[RegExp, string]> = [
|
|
170
|
-
// sdk/clx → @decocms/
|
|
171
|
-
[/(?:\.\.\/)*sdk\/clx(?:\.tsx?)?$/, "@decocms/
|
|
176
|
+
// sdk/clx → @decocms/blocks/sdk/clx (framework utility)
|
|
177
|
+
[/(?:\.\.\/)*sdk\/clx(?:\.tsx?)?$/, "@decocms/blocks/sdk/clx"],
|
|
172
178
|
// sdk/useId → react (useId is built-in in React 19)
|
|
173
179
|
[/(?:\.\.\/)*sdk\/useId(?:\.tsx?)?$/, "react"],
|
|
174
180
|
// sdk/useOffer — kept as-is (sites customize offer logic)
|
|
175
181
|
// sdk/useVariantPossiblities — kept as-is (sites customize variant logic)
|
|
176
|
-
// sdk/format → @decocms/apps
|
|
177
|
-
[/(?:\.\.\/)*sdk\/format(?:\.tsx?)?$/, "@decocms/apps
|
|
182
|
+
// sdk/format → @decocms/apps-commerce/sdk/formatPrice
|
|
183
|
+
[/(?:\.\.\/)*sdk\/format(?:\.tsx?)?$/, "@decocms/apps-commerce/sdk/formatPrice"],
|
|
178
184
|
// sdk/usePlatform → remove entirely
|
|
179
185
|
[/(?:\.\.\/)*sdk\/usePlatform(?:\.tsx?)?$/, ""],
|
|
180
186
|
// static/adminIcons → deleted (icon loaders need rewriting)
|
|
@@ -234,16 +240,16 @@ export function transformImports(
|
|
|
234
240
|
/**
|
|
235
241
|
* Post-process: split @deco/deco/hooks imports.
|
|
236
242
|
* In the old stack, @deco/deco/hooks exported useDevice, useScript, useSection, etc.
|
|
237
|
-
* In @decocms/
|
|
243
|
+
* In @decocms/blocks, useDevice is at @decocms/blocks/sdk/useDevice.
|
|
238
244
|
* After import rewriting, we need to split lines like:
|
|
239
|
-
* import { useDevice, useScript } from "@decocms/
|
|
245
|
+
* import { useDevice, useScript } from "@decocms/blocks/sdk/useScript"
|
|
240
246
|
* into:
|
|
241
|
-
* import { useDevice } from "@decocms/
|
|
242
|
-
* import { useScript } from "@decocms/
|
|
247
|
+
* import { useDevice } from "@decocms/blocks/sdk/useDevice"
|
|
248
|
+
* import { useScript } from "@decocms/blocks/sdk/useScript"
|
|
243
249
|
*/
|
|
244
250
|
function splitDecoHooksImports(code: string): string {
|
|
245
251
|
return code.replace(
|
|
246
|
-
/^(import\s+(?:type\s+)?\{)([^}]*\buseDevice\b[^}]*)(\}\s+from\s+["']@decocms\/
|
|
252
|
+
/^(import\s+(?:type\s+)?\{)([^}]*\buseDevice\b[^}]*)(\}\s+from\s+["']@decocms\/blocks\/sdk\/useScript["'];?)$/gm,
|
|
247
253
|
(_match, _prefix, importList, _suffix) => {
|
|
248
254
|
const items = importList.split(",").map((s: string) => s.trim()).filter(Boolean);
|
|
249
255
|
const deviceItems = items.filter((s: string) => s.includes("useDevice"));
|
|
@@ -251,10 +257,10 @@ export function transformImports(
|
|
|
251
257
|
|
|
252
258
|
const lines: string[] = [];
|
|
253
259
|
if (deviceItems.length > 0) {
|
|
254
|
-
lines.push(`import { ${deviceItems.join(", ")} } from "@decocms/
|
|
260
|
+
lines.push(`import { ${deviceItems.join(", ")} } from "@decocms/blocks/sdk/useDevice";`);
|
|
255
261
|
}
|
|
256
262
|
if (otherItems.length > 0) {
|
|
257
|
-
lines.push(`import { ${otherItems.join(", ")} } from "@decocms/
|
|
263
|
+
lines.push(`import { ${otherItems.join(", ")} } from "@decocms/blocks/sdk/useScript";`);
|
|
258
264
|
}
|
|
259
265
|
return lines.join("\n");
|
|
260
266
|
},
|
|
@@ -356,7 +362,7 @@ export function transformImports(
|
|
|
356
362
|
if (afterSplit !== result) {
|
|
357
363
|
result = afterSplit;
|
|
358
364
|
changed = true;
|
|
359
|
-
notes.push("Split useDevice into separate import from @decocms/
|
|
365
|
+
notes.push("Split useDevice into separate import from @decocms/blocks/sdk/useDevice");
|
|
360
366
|
}
|
|
361
367
|
|
|
362
368
|
// Rewrite dynamic imports: route through rewriteSpecifier so sdk-specific
|
|
@@ -8,7 +8,7 @@ import type { SectionMeta, TransformResult } from "../types";
|
|
|
8
8
|
* Adds section convention exports (sync, eager, layout, cache)
|
|
9
9
|
* to section files based on metadata extracted during analysis.
|
|
10
10
|
*
|
|
11
|
-
* These exports are read by generate-sections.ts in @decocms/
|
|
11
|
+
* These exports are read by generate-sections.ts in @decocms/blocks-cli
|
|
12
12
|
* to build the sections.gen.ts registry.
|
|
13
13
|
*
|
|
14
14
|
* The set of section *names* that get hints applied is configurable
|
package/scripts/migrate/types.ts
CHANGED
|
@@ -122,7 +122,7 @@ export interface LoaderInfo {
|
|
|
122
122
|
hasCache: boolean;
|
|
123
123
|
/** Has export const cacheKey */
|
|
124
124
|
hasCacheKey: boolean;
|
|
125
|
-
/** Maps to a known @decocms/apps equivalent */
|
|
125
|
+
/** Maps to a known @decocms/apps-* equivalent */
|
|
126
126
|
appsEquivalent: string | null;
|
|
127
127
|
/** Is a custom loader that needs dynamic import in commerce-loaders */
|
|
128
128
|
isCustom: boolean;
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
* but turns it into something CI can actually run.
|
|
9
9
|
*
|
|
10
10
|
* Usage (from a migrated site directory):
|
|
11
|
-
* npx -p @decocms/
|
|
12
|
-
* npx -p @decocms/
|
|
11
|
+
* npx -p @decocms/blocks-cli deco-post-cleanup
|
|
12
|
+
* npx -p @decocms/blocks-cli deco-post-cleanup --json
|
|
13
13
|
*
|
|
14
14
|
* Options:
|
|
15
15
|
* --source <dir> Site directory to audit (default: current directory)
|
|
@@ -65,13 +65,13 @@ function parseArgs(args: string[]): CliOpts {
|
|
|
65
65
|
|
|
66
66
|
function showHelp() {
|
|
67
67
|
console.log(`
|
|
68
|
-
@decocms/
|
|
68
|
+
@decocms/blocks-cli — Post-Migration Cleanup Audit
|
|
69
69
|
|
|
70
70
|
Scans a migrated site for dead code and obsolete boilerplate that the
|
|
71
71
|
framework now owns. Read-only — prints findings, does not modify files.
|
|
72
72
|
|
|
73
73
|
Usage:
|
|
74
|
-
npx -p @decocms/
|
|
74
|
+
npx -p @decocms/blocks-cli deco-post-cleanup [options]
|
|
75
75
|
|
|
76
76
|
Options:
|
|
77
77
|
--source <dir> Site directory to audit (default: .)
|
|
@@ -88,10 +88,10 @@ function showHelp() {
|
|
|
88
88
|
--help, -h Show this help
|
|
89
89
|
|
|
90
90
|
Examples:
|
|
91
|
-
npx -p @decocms/
|
|
92
|
-
npx -p @decocms/
|
|
93
|
-
npx -p @decocms/
|
|
94
|
-
npx -p @decocms/
|
|
91
|
+
npx -p @decocms/blocks-cli deco-post-cleanup
|
|
92
|
+
npx -p @decocms/blocks-cli deco-post-cleanup --source ./my-site --json
|
|
93
|
+
npx -p @decocms/blocks-cli deco-post-cleanup --fix
|
|
94
|
+
npx -p @decocms/blocks-cli deco-post-cleanup --fix --strict # fail CI if anything left
|
|
95
95
|
|
|
96
96
|
See: .agents/skills/deco-to-tanstack-migration/references/post-migration-cleanup.md
|
|
97
97
|
`);
|