-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmigrate.ts
More file actions
146 lines (125 loc) · 4.66 KB
/
migrate.ts
File metadata and controls
146 lines (125 loc) · 4.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
import { basename, extname } from "path";
const SITE_DIR = "./site";
const ASSETS_DIR = `${SITE_DIR}/assets`;
const CDN_BASE =
"https://cdn.prod.website-files.com/640f4a7e6dfcea721c5ea20f/";
const pages = ["index.html", "design.html", "development.html", "pricing.html"];
// 1. Read all HTML files and extract CDN URLs
const allUrls = new Set<string>();
for (const page of pages) {
const html = readFileSync(`${SITE_DIR}/${page}`, "utf-8");
// Match CDN URLs - handle parentheses in filenames
const regex = new RegExp(
`https://cdn\\.prod\\.website-files\\.com/640f4a7e6dfcea721c5ea20f/[^"'\\s>]+`,
"g"
);
let match;
while ((match = regex.exec(html)) !== null) {
allUrls.add(match[0]);
}
}
console.log(`Found ${allUrls.size} unique asset URLs`);
// 2. Create a mapping from CDN URL to local path
const urlMap = new Map<string, string>();
for (const url of allUrls) {
const relativePath = url.replace(CDN_BASE, "");
const decoded = decodeURIComponent(relativePath);
if (decoded.includes("css/")) {
urlMap.set(url, `assets/css/${basename(decoded)}`);
} else if (decoded.includes("js/")) {
// Skip Webflow JS
continue;
} else {
// Clean up filename
const clean = decoded.replace(/[() ]/g, (c) =>
c === " " ? "-" : c === "(" ? "" : ""
);
urlMap.set(url, `assets/images/${clean}`);
}
}
// 3. Download all assets
mkdirSync(`${ASSETS_DIR}/images`, { recursive: true });
mkdirSync(`${ASSETS_DIR}/css`, { recursive: true });
async function downloadAll() {
const entries = [...urlMap.entries()];
console.log(`Downloading ${entries.length} assets...`);
for (const [url, localPath] of entries) {
const fullPath = `${SITE_DIR}/${localPath}`;
if (existsSync(fullPath)) {
continue;
}
try {
const resp = await fetch(url);
if (!resp.ok) {
console.error(`Failed to download ${url}: ${resp.status}`);
continue;
}
const buffer = Buffer.from(await resp.arrayBuffer());
writeFileSync(fullPath, buffer);
console.log(` Downloaded: ${localPath}`);
} catch (e: any) {
console.error(` Error downloading ${url}: ${e.message}`);
}
}
// 4. Rewrite HTML files
for (const page of pages) {
let html = readFileSync(`${SITE_DIR}/${page}`, "utf-8");
// Sort URLs by length descending to avoid partial replacements
const sortedEntries = [...urlMap.entries()].sort(
(a, b) => b[0].length - a[0].length
);
for (const [url, localPath] of sortedEntries) {
html = html.replaceAll(url, localPath);
}
// Remove Webflow JS script tags (but keep CSS)
html = html.replace(
/<script\s+src="[^"]*webflow[^"]*\.js"[^>]*><\/script>/g,
""
);
// Remove WebFont loader script tag
html = html.replace(
/<script\s+src="[^"]*webfont\.js"[^>]*><\/script>/g,
""
);
// Remove Webflow class manipulation inline script
html = html.replace(
/<script\s+type="text\/javascript">!function\(o,c\).*?<\/script>/gs,
""
);
// Remove WebFont.load inline script
html = html.replace(
/<script\s+type="text\/javascript">WebFont\.load\(.*?\);<\/script>/gs,
""
);
// Replace Google Fonts preconnect with proper CSS link
html = html.replace(
'<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin="anonymous"/>',
'<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin="anonymous"/>\n<link href="https://fonts.googleapis.com/css2?family=Epilogue:wght@400;600&display=swap" rel="stylesheet"/>'
);
// Remove jQuery (loaded for Webflow)
html = html.replace(
/<script\s+src="[^"]*jquery[^"]*\.js[^"]*"[^>]*><\/script>/g,
""
);
// Add our slider JS before </body>
html = html.replace(
"</body>",
'<script src="assets/js/slider.js"></script></body>'
);
// Remove Webflow badge / data attributes
html = html.replace(/\s*data-wf-[a-z-]+="[^"]*"/g, "");
html = html.replace(/\s*data-w-id="[^"]*"/g, "");
// Fix internal links: /design -> /design.html etc. (for local serving)
// Actually for Cloudflare Pages, clean URLs work. Let's keep them as-is.
// But we need /design to serve design.html. Cloudflare Pages does this automatically.
// Update www.bento-ds.com links to relative
html = html.replace(/https:\/\/www\.bento-ds\.com\//g, "/");
html = html.replace(/https:\/\/www\.bento-ds\.com#/g, "/#");
html = html.replace(/https:\/\/www\.bento-ds\.com/g, "/");
writeFileSync(`${SITE_DIR}/${page}`, html);
console.log(`Rewrote ${page}`);
}
console.log("\nDone! Static site is in ./site/");
}
downloadAll();