This repository was archived by the owner on May 21, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathbuild.ts
More file actions
121 lines (104 loc) · 3.22 KB
/
Copy pathbuild.ts
File metadata and controls
121 lines (104 loc) · 3.22 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
import { promises as fs } from 'node:fs'
import path from 'node:path'
import { build } from 'esbuild'
import { globby } from 'globby'
const ROUTE_PREFIX = '/mcp-use/widgets'
const SRC_DIR = 'resources'
const OUT_DIR = 'dist/resources'
function toRoute(file: string) {
const rel = file.replace(new RegExp(`^${SRC_DIR}/`), '').replace(/\.tsx?$/, '')
return `${ROUTE_PREFIX}/${rel}`
}
function outDirForRoute(route: string) {
return path.join(OUT_DIR, route.replace(/^\//, ''))
}
function htmlTemplate({ title, scriptPath }: { title: string, scriptPath: string }) {
return `<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>${title} Widget</title>
<style>
body {
margin: 0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
background: #f5f5f5;
}
#widget-root {
max-width: 1200px;
margin: 0 auto;
}
</style>
</head>
<body>
<div id="widget-root"></div>
<script type="module" src="${scriptPath}"></script>
</body>
</html>`
}
async function buildWidget(entry: string, projectPath: string, minify = true) {
const relativePath = path.relative(projectPath, entry)
const route = toRoute(relativePath)
const pageOutDir = path.join(projectPath, outDirForRoute(route))
const baseName = path.parse(entry).name
// Build JS/CSS chunks for this page
await build({
entryPoints: [entry],
bundle: true,
splitting: true,
format: 'esm',
platform: 'browser',
target: 'es2018',
sourcemap: !minify,
minify,
outdir: path.join(pageOutDir, 'assets'),
logLevel: 'silent',
loader: {
'.svg': 'file',
'.png': 'file',
'.jpg': 'file',
'.jpeg': 'file',
'.gif': 'file',
'.css': 'css',
},
entryNames: `[name]-[hash]`,
chunkNames: `chunk-[hash]`,
assetNames: `asset-[hash]`,
define: {
'process.env.NODE_ENV': minify ? '"production"' : '"development"',
},
})
// Find the main entry file name
const files = await fs.readdir(path.join(pageOutDir, 'assets'))
const mainJs = files.find(f => f.startsWith(`${baseName}-`) && f.endsWith('.js'))
if (!mainJs)
throw new Error(`Failed to locate entry JS for ${entry}`)
// Write an index.html that points to the entry
await fs.mkdir(pageOutDir, { recursive: true })
await fs.writeFile(
path.join(pageOutDir, 'index.html'),
htmlTemplate({
title: baseName,
scriptPath: `./assets/${mainJs}`,
}),
'utf8',
)
return { baseName, route }
}
export async function buildWidgets(projectPath: string) {
const srcDir = path.join(projectPath, SRC_DIR)
const outDir = path.join(projectPath, OUT_DIR)
// Clean dist
await fs.rm(outDir, { recursive: true, force: true })
// Find all TSX entries
const entries = await globby([`${srcDir}/**/*.tsx`])
console.log(`Building ${entries.length} widget files...`)
// Build once (production mode)
for (const entry of entries) {
const { baseName, route } = await buildWidget(entry, projectPath)
console.log(`\x1b[32m✓\x1b[0m Built ${baseName} -> ${route}`)
}
console.log('Build complete!')
}