diff --git a/packages/web-templates/src/export-html/build.mjs b/packages/web-templates/src/export-html/build.mjs index 2f5c70bf29b..f2f01c1e9b1 100644 --- a/packages/web-templates/src/export-html/build.mjs +++ b/packages/web-templates/src/export-html/build.mjs @@ -1,40 +1,89 @@ import { readFile, writeFile, mkdir } from 'node:fs/promises'; +import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; import { dirname, join } from 'node:path'; import { build } from 'esbuild'; +import { build as viteBuild } from 'vite'; import { buildConfig } from './esbuild.config.mjs'; import prettier from 'prettier'; +const require = createRequire(import.meta.url); const assetsDir = dirname(fileURLToPath(import.meta.url)); const srcDir = join(assetsDir, 'src'); const assetsDistDir = join(assetsDir, 'dist'); const generatedDir = join(assetsDir, '..', 'generated'); +const webuiDir = join(assetsDir, '..', '..', '..', 'webui'); await mkdir(generatedDir, { recursive: true }); await mkdir(assetsDistDir, { recursive: true }); const templateModulePath = join(generatedDir, 'exportHtmlTemplate.ts'); -const packageJsonPath = join(assetsDir, 'package.json'); -const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf8')); -const dependencyVersions = packageJson?.dependencies ?? {}; - -const getDependencyVersion = (name) => { - const version = dependencyVersions[name]; - if (!version) { - throw new Error(`Missing ${name} dependency version in package.json.`); - } - // Handle various version formats: - // - "^0.1.0" -> "0.1.0" - // - "~1.2.3" -> "1.2.3" - // - "latest" -> "latest" - // - "^0.1.0@latest" -> "0.1.0" (remove npm tag suffix) - const versionWithoutPrefix = version.replace(/^[^0-9a-zA-Z]+/, ''); - // Remove npm tag suffix (e.g., "0.1.0@latest" -> "0.1.0") - return versionWithoutPrefix.replace(/@.+$/, ''); -}; -const webuiVersion = getDependencyVersion('@qwen-code/webui'); const reactUmdVersion = '18.2.0'; const reactDomUmdVersion = '18.2.0'; +// Build the @qwen-code/webui viewer as a UMD bundle from webui sources and +// inline it into the template below. The exported HTML must stay +// self-contained: @qwen-code/webui is not published as part of the release +// flow, and resolving it from a CDN at view time would retroactively change +// how every already-generated export renders. Building it here (instead of +// reading packages/webui/dist) keeps the template build independent of +// webui's own build, which runs after web-templates in scripts/build.js and +// is skipped entirely by --cli-only builds. +const webuiDistDir = join(assetsDistDir, 'webui'); +await viteBuild({ + configFile: false, + root: webuiDir, + logLevel: 'warn', + css: { + postcss: { + plugins: [ + require('tailwindcss')({ + presets: [require(join(webuiDir, 'tailwind.preset.cjs'))], + content: [join(webuiDir, 'src', '**', '*.{ts,tsx}')], + }), + require('autoprefixer'), + ], + }, + }, + build: { + outDir: webuiDistDir, + emptyOutDir: true, + minify: true, + sourcemap: false, + cssCodeSplit: false, + lib: { + entry: join(webuiDir, 'src', 'index.ts'), + name: 'QwenCodeWebUI', + formats: ['umd'], + fileName: () => 'index.umd.js', + }, + rollupOptions: { + external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime'], + output: { + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + 'react-dom/client': 'ReactDOM', + 'react/jsx-runtime': 'ReactJSXRuntime', + }, + assetFileNames: 'styles.[ext]', + }, + }, + }, +}); +const webuiJs = (await readFile(join(webuiDistDir, 'index.umd.js'), 'utf8')) + // Escape for inlining into a