scripts/build-components.ts 3.8 K raw
1
import { mkdir, copyFile, readFile, writeFile } from "node:fs/promises";
2
import { existsSync } from "node:fs";
3
4
async function buildComponents() {
5
	console.log("🔨 Building components...");
6
7
	// Ensure dist/components directory exists
8
	const distComponentsDir = "dist/components";
9
	if (!existsSync(distComponentsDir)) {
10
		await mkdir(distComponentsDir, { recursive: true });
11
	}
12
13
	// Copy connect-wallet.js as-is (no dependencies)
14
	await copyFile(
15
		"src/components/connect-wallet.js",
16
		"dist/components/connect-wallet.js",
17
	);
18
	console.log("✅ Copied connect-wallet.js");
19
20
	// Helper function to bundle and reorganize component with dependencies
21
	async function bundleComponent(componentName: string, className: string) {
22
		const result = await Bun.build({
23
			entrypoints: [`src/components/${componentName}.js`],
24
			target: "browser",
25
			format: "esm",
26
			minify: false,
27
			sourcemap: "none",
28
			splitting: false,
29
			outdir: "dist/components",
30
			naming: "[dir]/[name].[ext]",
31
			external: [], // Bundle all dependencies
32
			plugins: [
33
				{
34
					name: "Keep JSDocs",
35
					setup(build) {
36
						build.onLoad({ filter: /\.(js)$/ }, async ({ path }) => {
37
							let text = await Bun.file(path).text();
38
							// Replace '/**' with '/*! *' to mark comments as "important" for minifiers
39
							let contents = text.replaceAll("/**", "/*! *");
40
							return { contents, loader: "js" };
41
						});
42
					},
43
				},
44
			],
45
		});
46
47
		if (!result.success) {
48
			console.error(`❌ Build failed for ${componentName}:`);
49
			for (const log of result.logs) {
50
				console.error(log);
51
			}
52
			process.exit(1);
53
		}
54
55
		console.log(`✅ Bundled ${componentName}.js with dependencies`);
56
57
		// Read the bundled file
58
		const bundledContent = await readFile(
59
			`dist/components/${componentName}.js`,
60
			"utf8",
61
		);
62
63
		// Split content to move dependencies to bottom
64
		const lines = bundledContent.split("\n");
65
		const componentStartIndex = lines.findIndex((line) =>
66
			line.includes(`class ${className} extends HTMLElement`),
67
		);
68
69
		if (componentStartIndex > 0) {
70
			// Dependencies are at the top (before the component class)
71
			let dependencies = lines.slice(0, componentStartIndex).join("\n");
72
			let componentCode = lines.slice(componentStartIndex).join("\n");
73
74
			// Replace 'var' with 'const' in dependencies to prevent hoisting issues
75
			// This ensures variables are not hoisted as undefined when code is reorganized
76
			dependencies = dependencies.replace(/\bvar\s+/g, "const ");
77
78
			// Find and extract the customElements.define() call
79
			const defineRegex = /customElements\.define\([^)]+\);?\n?/;
80
			const defineMatch = componentCode.match(defineRegex);
81
			let defineCall = "";
82
83
			if (defineMatch) {
84
				defineCall = defineMatch[0];
85
				// Remove the define call from component code
86
				componentCode = componentCode.replace(defineRegex, "");
87
			}
88
89
			// Create new content wrapped in IIFE to avoid global namespace pollution
90
			const newContent = `// User-editable ${componentName} component
91
// @noble/hashes are bundled at the bottom of this file
92
93
(function() {
94
// ==========================================
95
// COMPONENT CODE
96
// ==========================================
97
98
${componentCode}
99
100
// ==========================================
101
// BUNDLED DEPENDENCIES BELOW
102
// ==========================================
103
104
${dependencies}
105
106
// Register custom element after dependencies are loaded
107
${defineCall}
108
})();`;
109
110
			await writeFile(
111
				`dist/components/${componentName}.js`,
112
				newContent,
113
				"utf8",
114
			);
115
			console.log(
116
				`✅ Reorganized ${componentName}.js (component code first, dependencies at bottom)`,
117
			);
118
		}
119
	}
120
121
	// Bundle contract-call.js with its dependencies
122
	await bundleComponent("contract-call", "ContractCall");
123
124
	// Bundle contract-read.js with its dependencies
125
	await bundleComponent("contract-read", "ContractRead");
126
127
	console.log("🎉 Component build complete!");
128
}
129
130
buildComponents().catch(console.error);