build.mjs 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. // Build script for @fmode/wechat-cli.
  2. //
  3. // Bundles src/index.ts + src/commands/*.ts into a single self-contained
  4. // dist/wecli.js with a node shebang. The tgz/zip this plugin gets copied
  5. // into only needs that one file to be executable via `node dist/wecli.js`
  6. // or (once `npm link`ed) via plain `wecli`.
  7. //
  8. // Intentionally zero runtime deps — this CLI uses Node 20+ globals
  9. // (fetch, util.parseArgs, fs/promises, os, path) only, so the bundle is
  10. // tiny and has no cross-platform native binding quirks.
  11. import { build } from "esbuild";
  12. import { readFileSync, rmSync, mkdirSync, existsSync, chmodSync } from "node:fs";
  13. import { fileURLToPath } from "node:url";
  14. import { dirname, resolve } from "node:path";
  15. const __dirname = dirname(fileURLToPath(import.meta.url));
  16. const projectRoot = resolve(__dirname, "..");
  17. const pkg = JSON.parse(
  18. readFileSync(resolve(projectRoot, "package.json"), "utf8")
  19. );
  20. const outfile = resolve(projectRoot, "dist", "wecli.js");
  21. const distDir = resolve(projectRoot, "dist");
  22. if (existsSync(distDir)) {
  23. rmSync(distDir, { recursive: true, force: true });
  24. }
  25. mkdirSync(distDir, { recursive: true });
  26. const result = await build({
  27. entryPoints: [resolve(projectRoot, "src", "index.ts")],
  28. outfile,
  29. bundle: true,
  30. format: "esm",
  31. platform: "node",
  32. target: "node20",
  33. sourcemap: "inline",
  34. legalComments: "none",
  35. minify: false,
  36. keepNames: true,
  37. external: ["node:*"],
  38. mainFields: ["module", "main"],
  39. conditions: ["import", "node", "default"],
  40. logLevel: "info",
  41. metafile: true,
  42. treeShaking: true,
  43. banner: {
  44. // Shebang first so `chmod +x dist/wecli.js && ./dist/wecli.js` works on
  45. // Unix. On Windows the shebang is harmless; `npm link` writes its own
  46. // `.cmd` shim regardless.
  47. js: `#!/usr/bin/env node\n// @fmode/wechat-cli v${pkg.version} — generated by scripts/build.mjs. Do not edit by hand.`,
  48. },
  49. });
  50. // Make the built file executable on POSIX. No-op on Windows (chmod succeeds
  51. // silently; the bit is irrelevant because Windows ignores it).
  52. try {
  53. chmodSync(outfile, 0o755);
  54. } catch {
  55. /* best-effort */
  56. }
  57. const inputCount = Object.keys(result.metafile.inputs).length;
  58. const bundled = readFileSync(outfile, "utf8");
  59. const outputSize = bundled.length;
  60. console.log(
  61. `\n\u2705 bundled ${inputCount} modules -> dist/wecli.js (${(outputSize / 1024).toFixed(1)} KiB)`
  62. );
  63. console.log(
  64. ` Run with: node dist/wecli.js --help (or \`npm link\` + \`wecli --help\`)`
  65. );