You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让esbuild转换独立文件时引用已打包的bundle?

问题

Node包遵循规范将源码放在src/目录,新增cli/runner.ts作为二进制命令入口调用包内静态方法。使用esbuild构建时:

  • 主源码已打包到dist/index.mjs
  • cli脚本仅做TS转JS不打包,但转换后的cli/runner.mjs中导入路径仍为../src/Tool.js,实际需要指向../dist/index.mjs

现有构建脚本如下:

import * as esbuild from "esbuild";

const build = async () => {
    try {
        await Promise.all([
            esbuild.build({
                entryPoints: ["src/index.ts"],
                bundle: true,
                outfile: "dist/index.mjs",
                format: "esm",
                target: "es2022",
                platform: "node",
                keepNames: true,
                external: [
                    "antlr4ng",
                    "commander",
                    "fast-printf",
                    "stringtempalte4ts",
                    "unicode-properties"
                ]
            }),
            esbuild.build({
                entryPoints: ["cli/**/*.ts"],
                bundle: false,
                outdir: "cli",
                format: "esm",
                platform: "node",
                target: "es2022",
                outExtension: { ".js": ".mjs" },
            })
        ]);

        console.log("Build completed successfully");
    } catch (error) {
        console.error("Build failed:", error);
        process.exit(1);
    }
};

await build();

cli源码:

#!/usr/bin/env node

import { Tool } from "../src/Tool.js";

Tool.main(process.argv.slice(2));

转换后cli脚本导入路径未修正,导致运行时找不到模块。


解决方案

方法1:规范导入路径,利用package.json解析

修改cli/runner.ts的导入路径为包根目录,让Node通过package.json配置自动解析到dist文件:

#!/usr/bin/env node

// 直接导入包自身,Node会读取package.json的main/exports字段定位到dist文件
import { Tool } from '..';

Tool.main(process.argv.slice(2));

然后在package.json中配置入口:

{
  "main": "./dist/index.mjs",
  "exports": "./dist/index.mjs",
  "type": "module"
}

这种方式不需要修改esbuild配置,转换后的cli脚本会保留import { Tool } from '..',运行时Node会自动找到正确的dist文件。

方法2:用esbuild插件替换导入路径

如果不想修改源码导入路径,可以编写一个简单的esbuild插件,在转换cli脚本时替换目标导入路径:

修改构建脚本,为cli的build配置添加插件:

import * as esbuild from "esbuild";
import * as fs from "fs";

// 自定义替换导入路径的插件
const replaceImportPlugin = {
  name: 'replace-import',
  setup(build) {
    build.onLoad({ filter: /cli\/.*\.ts$/ }, async args => {
      let content = await fs.promises.readFile(args.path, 'utf8');
      // 将../src/Tool.js替换为../dist/index.mjs
      content = content.replace('../src/Tool.js', '../dist/index.mjs');
      return { contents: content, loader: 'ts' };
    });
  }
};

const build = async () => {
    try {
        await Promise.all([
            // 主源码构建配置不变
            esbuild.build({
                entryPoints: ["src/index.ts"],
                bundle: true,
                outfile: "dist/index.mjs",
                format: "esm",
                target: "es2022",
                platform: "node",
                keepNames: true,
                external: [
                    "antlr4ng",
                    "commander",
                    "fast-printf",
                    "stringtempalte4ts",
                    "unicode-properties"
                ]
            }),
            // cli构建配置添加插件
            esbuild.build({
                entryPoints: ["cli/**/*.ts"],
                bundle: false,
                outdir: "cli",
                format: "esm",
                platform: "node",
                target: "es2022",
                outExtension: { ".js": ".mjs" },
                plugins: [replaceImportPlugin]
            })
        ]);

        console.log("Build completed successfully");
    } catch (error) {
        console.error("Build failed:", error);
        process.exit(1);
    }
};

await build();

这个插件会在加载cli目录下的TS文件时,替换指定的导入路径,转换后的cli脚本就会使用正确的../dist/index.mjs路径。

方法3:对cli脚本开启bundle并将包源码设为external

如果允许cli脚本被bundle,但不想包含包源码,可以开启bundle并将src目录设为external,同时配置路径解析:

修改cli的build配置:

esbuild.build({
    entryPoints: ["cli/**/*.ts"],
    bundle: true,
    outdir: "cli",
    format: "esm",
    platform: "node",
    target: "es2022",
    outExtension: { ".js": ".mjs" },
    // 将src目录下的模块设为外部依赖
    external: ['../src/*'],
    // 配置路径解析,将../src/Tool.js映射到../dist/index.mjs
    resolveExtensions: ['.ts', '.js', '.mjs'],
    alias: {
        '../src/Tool.js': '../dist/index.mjs'
    }
})

这种方式下,esbuild会将cli脚本中的../src/Tool.js替换为../dist/index.mjs,同时不会打包包源码,符合需求。


内容的提问来源于stack exchange,提问作者Mike Lischke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 03:27:03