Setting the file. One moment. Generate Route Plan · Wix Headless Replatform · wix/skills · Skills Docs28.10
Workflow
Script Generate Repeater Cms
scripts/generate-route-plan.mjs
JavaScript·143 lines·5 KB
;
15
16async function main() {
17 const args = parseArgs();
18 const url = normalizeUrl(args._[0] || args.url).toString();
19 const outputDir = resolveOutputDir(url, args.out);
20 const routes = await generateRoutePlan({ sourceUrl: url, outputDir });
21 await writeJson(path.join(docsDir(outputDir), "routes.json"), routes);
22 if (args.json) process.stdout.write(`${JSON.stringify(routes, null, 2)}\n`);
23}
24
25export async function generateRoutePlan({ sourceUrl, outputDir }) {
26 const dir = docsDir(outputDir);
27 const discovery = await safeRead(path.join(dir, "discovery.json"), { pages: [] });
28 const pages = (discovery.pages || []).filter((page) => page?.url && !isAssetLikeUrl(page.url));
29 const inScopeUrls = new Set((discovery.inScopePages || pages.filter((page) => page.inScope))
30 .filter((page) => page?.url && !isAssetLikeUrl(page.url))
31 .map((page) => page.url));
32 const routes = [];
33 const inScopeRoutes = [];
34 const preservedFallbackLinks = [];
35 const dynamicSeen = new Set();
36 for (const page of pages) {
37 const inScope = inScopeUrls.has(page.url) || discovery.scope !== "specific";
38 if (!inScope) {
39 const route = notMigratedRouteFor({ sourceUrl, page });
40 routes.push(route);
41 preservedFallbackLinks.push(route);
42 continue;
43 }
44 if (isDynamicArea(page.area)) {
45 const template = classifyTemplate(page.area);
46 if (dynamicSeen.has(template)) {
47 const existing = routes.find((route) => route.template === template);
48 existing?.representativeUrls?.push(page.url);
49 continue;
50 }
51 dynamicSeen.add(template);
52 const route = dynamicRouteFor(page, template);
53 routes.push(route);
54 inScopeRoutes.push(route);
55 continue;
56 }
57 if (page.area === "home" || inStaticScope(page.area)) {
58 const route = {
59 sourceUrl: page.url,
60 sourcePath: routePathFromUrl(sourceUrl, page.url),
61 targetRoute: routePathFromUrl(sourceUrl, page.url) || "/",
62 kind: "static",
63 area: page.area,
64 representativeUrls: [page.url],
65 };
66 routes.push(route);
67 inScopeRoutes.push(route);
68 continue;
69 }
70 const route = notMigratedRouteFor({ sourceUrl, page });
71 routes.push(route);
72 preservedFallbackLinks.push(route);
73 }
74 return {
75 sourceUrl,
76 routes,
77 inScopeRoutes,
78 preservedFallbackLinks,
79 dynamicRouteCount: routes.filter((route) => route.kind === "dynamic").length,
80 notMigratedCount: routes.filter((route) => route.kind === "not-migrated").length,
81 generatedAt: new Date().toISOString(),
82 };
83}
84
85function notMigratedRouteFor({ sourceUrl, page }) {
86 const sourcePath = routePathFromUrl(sourceUrl, page.url);
87 return {
88 sourceUrl: page.url,
89 sourcePath,
90 targetRoute: `/not-migrated?from=${encodeURIComponent(sourcePath)}`,
91 kind: "not-migrated",
92 area: page.area,
93 representativeUrls: [page.url],
94 };
95}
96
97function dynamicRouteFor(page, template) {
98 const config = {
99 "product-detail": { targetRoute: "/products/[slug]", dataSource: "wix-stores" },
100 "product-category": { targetRoute: "/categories/[slug]", dataSource: "wix-stores" },
101 "blog-post": { targetRoute: "/blog/[slug]", dataSource: "wix-blog" },
102 "blog-index": { targetRoute: "/blog", dataSource: "wix-blog" },
103 "cms-item": { targetRoute: "/content/[slug]", dataSource: "wix-cms" },
104 booking: { targetRoute: "/bookings/[slug]", dataSource: "wix-bookings" },
105 event: { targetRoute: "/events/[slug]", dataSource: "wix-events" },
106 "pricing-plan": { targetRoute: "/pricing", dataSource: "wix-pricing-plans" },
107 }[template] || { targetRoute: `/${page.area}/[slug]`, dataSource: "wix-sdk" };
108 return {
109 sourcePattern: patternFor(page),
110 sourcePath: page.path,
111 targetRoute: config.targetRoute,
112 kind: "dynamic",
113 area: page.area,
114 template,
115 dataSource: config.dataSource,
116 representativeUrls: [page.url],
117 };
118}
119
120function patternFor(page) {
121 const parts = String(page.path || "/").split("/").filter(Boolean);
122 if (parts.length <= 1) return page.path || "/";
123 return `/${parts.slice(0, -1).join("/")}/*`;
124}
125
126function inStaticScope(area) {
127 return ["about-contact", "legal", "docs", "other"].includes(area);
128}
129
130async function safeRead(filePath, fallback) {
131 try {
132 return await readJson(filePath);
133 } catch {
134 return fallback;
135 }
136}
137
138if (import.meta.url === `file://${process.argv[1]}`) {
139 main().catch((error) => {
140 console.error(error.stack || error.message);
141 process.exit(1);
142 });
143}