|
| 1 | +import esbuild from "esbuild"; |
| 2 | +import builtins from "builtin-modules"; |
| 3 | +import path from "path"; |
| 4 | +import fs from "fs"; |
| 5 | +import { fileURLToPath } from "url"; |
| 6 | +import obsidianAliasPlugin from "../obsidian-alias/index.js"; |
| 7 | + |
| 8 | +const __dirname = path.dirname(fileURLToPath(import.meta.url)); |
| 9 | +const root = path.join(__dirname, ".."); |
| 10 | + |
| 11 | +const wasmPlugin = (config) => ({ |
| 12 | + name: "wasm", |
| 13 | + setup(build) { |
| 14 | + build.onResolve({ filter: /\.wasm$/ }, (args) => { |
| 15 | + if (args.resolveDir === "") return; |
| 16 | + return { |
| 17 | + path: path.isAbsolute(args.path) |
| 18 | + ? args.path |
| 19 | + : path.join(args.resolveDir, args.path), |
| 20 | + namespace: `wasm-${config.mode}`, |
| 21 | + }; |
| 22 | + }); |
| 23 | + build.onLoad({ filter: /.*/, namespace: "wasm-embed" }, async (args) => ({ |
| 24 | + contents: await fs.promises.readFile(args.path), |
| 25 | + loader: "binary", |
| 26 | + })); |
| 27 | + }, |
| 28 | +}); |
| 29 | + |
| 30 | +const result = await esbuild.build({ |
| 31 | + entryPoints: [path.join(root, "src/main.ts")], |
| 32 | + bundle: true, |
| 33 | + metafile: true, |
| 34 | + write: false, |
| 35 | + minify: true, |
| 36 | + plugins: [wasmPlugin({ mode: "embed" }), obsidianAliasPlugin()], |
| 37 | + inject: [path.join(root, "obsidian-alias/buffer-inject.mjs")], |
| 38 | + external: [ |
| 39 | + "obsidian", |
| 40 | + "electron", |
| 41 | + ...builtins.filter((m) => m !== "buffer"), |
| 42 | + "node:url", |
| 43 | + "node:fs", |
| 44 | + "node:fs/promises", |
| 45 | + "node:path", |
| 46 | + "node:module", |
| 47 | + "node:async_hooks", |
| 48 | + "node:crypto", |
| 49 | + "node:stream", |
| 50 | + "node:util", |
| 51 | + "node:events", |
| 52 | + "node:os", |
| 53 | + ], |
| 54 | + format: "cjs", |
| 55 | + target: "es2023", |
| 56 | + loader: { ".css": "empty" }, |
| 57 | + absWorkingDir: root, |
| 58 | +}); |
| 59 | + |
| 60 | +const outBytes = Object.values(result.metafile.outputs)[0]?.bytes ?? 0; |
| 61 | + |
| 62 | +function pkgFromPath(filePath) { |
| 63 | + const nm = filePath.split("node_modules/"); |
| 64 | + if (nm.length < 2) { |
| 65 | + if (filePath.includes("/src/")) return "(plugin src)"; |
| 66 | + return "(other)"; |
| 67 | + } |
| 68 | + let rest = nm[nm.length - 1]; |
| 69 | + if (rest.startsWith(".pnpm/")) { |
| 70 | + const match = rest.match(/node_modules\/((?:@[^/]+\/[^/]+|[^/]+))/); |
| 71 | + return match ? match[1] : rest.split("/")[0]; |
| 72 | + } |
| 73 | + if (rest.startsWith("@")) { |
| 74 | + const parts = rest.split("/"); |
| 75 | + return `${parts[0]}/${parts[1]}`; |
| 76 | + } |
| 77 | + return rest.split("/")[0]; |
| 78 | +} |
| 79 | + |
| 80 | +const byPkg = new Map(); |
| 81 | +for (const [file, meta] of Object.entries(result.metafile.inputs)) { |
| 82 | + const pkg = pkgFromPath(file); |
| 83 | + byPkg.set(pkg, (byPkg.get(pkg) ?? 0) + meta.bytes); |
| 84 | +} |
| 85 | + |
| 86 | +const sorted = [...byPkg.entries()].sort((a, b) => b[1] - a[1]); |
| 87 | + |
| 88 | +console.log(`\nBundled output (minified): ${(outBytes / 1024 / 1024).toFixed(2)} MB\n`); |
| 89 | +console.log("Top packages by contributed source bytes (pre-tree-shake input):\n"); |
| 90 | +console.log("Rank Size Package"); |
| 91 | +console.log("---- -------- -------"); |
| 92 | +sorted.slice(0, 40).forEach(([pkg, bytes], i) => { |
| 93 | + const mb = bytes / 1024 / 1024; |
| 94 | + const label = mb >= 0.1 ? `${mb.toFixed(2)} MB` : `${(bytes / 1024).toFixed(0)} KB`; |
| 95 | + console.log(`${String(i + 1).padStart(4)} ${label.padEnd(9)} ${pkg}`); |
| 96 | +}); |
| 97 | + |
| 98 | +const langchain = sorted.filter(([p]) => p.includes("langchain") || p === "langchain"); |
| 99 | +const lcTotal = langchain.reduce((s, [, b]) => s + b, 0); |
| 100 | +console.log(`\nLangChain family total (input bytes): ${(lcTotal / 1024 / 1024).toFixed(2)} MB across ${langchain.length} packages`); |
0 commit comments