Skip to main content
Bun 通过 Bun.Transpiler 类暴露其内部转译器。要创建一个 Bun 转译器的实例:
const transpiler = new Bun.Transpiler({
  loader: "tsx", // "js" | "jsx" | "ts" | "tsx"
});

.transformSync()

使用 .transformSync() 方法同步转译代码。模块不会被解析,代码也不会被执行。结果是一个纯净的 JavaScript 代码字符串。
const transpiler = new Bun.Transpiler({
  loader: 'tsx',
});

const code = `
import * as whatever from "./whatever.ts"
export function Home(props: {title: string}){
  return <p>{props.title}</p>;
}`;

const result = transpiler.transformSync(code);

要覆盖 new Bun.Transpiler() 构造函数中指定的默认加载器,可以给 .transformSync() 传入第二个参数。
transpiler.transformSync("<div>hi!</div>", "tsx");
当调用 .transformSync 时,转译器会在当前执行代码的相同线程中运行。如果使用了宏,它将在与转译器相同的线程中运行,但在一个独立的事件循环中,与应用程序的其余部分分开。当前,宏与普通代码之间共享全局变量,这意味着可以(但不推荐)在宏和普通代码间共享状态。试图在宏外部使用 AST 节点是未定义行为。

.transform()

transform() 方法是 .transformSync() 的异步版本,返回一个 Promise<string>
const transpiler = new Bun.Transpiler({ loader: "jsx" });
const result = await transpiler.transform("<div>hi!</div>");
console.log(result);
除非你要转译大量大文件,否则通常建议使用 Bun.Transpiler.transformSync。使用线程池的开销往往比实际转译代码花费的时间更久。
await transpiler.transform("<div>hi!</div>", "tsx");
.transform() 方法会在 Bun 的工作线程池中运行转译器,因此如果调用 100 次,会在 Math.floor($cpu_count * 0.8) 个线程间并行运行,不会阻塞主 JavaScript 线程。如果代码使用了宏,可能会在该新线程中启动 Bun JavaScript 运行时环境的新实例。

.scan()

Transpiler 实例还可以扫描某段源代码,返回其导入和导出列表及每个项的附加元数据。仅类型 的导入和导出会被忽略。
const transpiler = new Bun.Transpiler({
  loader: "tsx",
});

const code = `
import React from 'react';
import type {ReactNode} from 'react';
const val = require('./cjs.js')
import('./loader');

export const name = "hello";
`;

const result = transpiler.scan(code);
imports 数组中的每个导入项都有 pathkind。Bun 将导入分类如下:
  • import-statementimport React from 'react'
  • require-callconst val = require('./cjs.js')
  • require-resolverequire.resolve('./cjs.js')
  • dynamic-importimport('./loader')
  • import-rule@import 'foo.css'
  • url-tokenurl('./foo.png')

.scanImports()

对于性能敏感的代码,可以使用 .scanImports() 方法获取导入列表。由于一些性能优化,它比 .scan() 更快(尤其是大型文件),但准确度略低。
const transpiler = new Bun.Transpiler({
  loader: "tsx",
});

const code = `
import React from 'react';
import type {ReactNode} from 'react';
const val = require('./cjs.js')
import('./loader');

export const name = "hello";
`;

const result = transpiler.scanImports(code);

参考

See Typescript Definitions
type Loader = "jsx" | "js" | "ts" | "tsx";

interface TranspilerOptions {
  // 替换键为值,值必须是 JSON 字符串。
  // { "process.env.NODE_ENV": "\"production\"" }
  define?: Record<string, string>,

  // 此转译器的默认加载器
  loader?: Loader,

  // 默认目标平台
  // 影响 import 和/或 require 的使用方式
  target?: "browser" | "bun" | "node",

  // 指定 tsconfig.json 文件,字符串化的 JSON 或对象
  // 用于设置自定义的 JSX 工厂、片段或导入源
  // 例如,使用 Preact 替代 React,或使用 Emotion
  tsconfig?: string | TSConfig,

  // 用宏替换导入
  macro?: MacroMap,

  // 指定一组需剔除的导出项
  // 或重命名某些导出
  exports?: {
      eliminate?: string[];
      replace?: Record<string, string>;
  },

  // 是否从转译文件中移除未使用的导入
  // 默认: false
  trimUnusedImports?: boolean,

  // 是否启用一系列 JSX 优化
  // jsxOptimizationInline ...,

  // 实验性空白符压缩
  minifyWhitespace?: boolean,

  // 是否内联常量值
  // 通常提升性能并减少包大小
  // 默认: true
  inline?: boolean,
}

// 映射导入路径到宏
interface MacroMap {
  // {
  //   "react-relay": {
  //     "graphql": "bun-macro-relay/bun-macro-relay.tsx"
  //   }
  // }
  [packagePath: string]: {
    [importItemName: string]: string,
  },
}

class Bun.Transpiler {
  constructor(options: TranspilerOptions)

  transform(code: string, loader?: Loader): Promise<string>
  transformSync(code: string, loader?: Loader): string

  scan(code: string): {exports: string[], imports: Import}
  scanImports(code: string): Import[]
}

type Import = {
  path: string,
  kind:
  // JavaScript 中的 import foo from 'bar';
  | "import-statement"
  // JavaScript 中的 require("foo")
  | "require-call"
  // JavaScript 中的 require.resolve("foo")
  | "require-resolve"
  // JavaScript 中的动态 import()
  | "dynamic-import"
  // CSS 中的 @import()
  | "import-rule"
  // CSS 中的 url()
  | "url-token"
  // 导入是由 Bun 注入的
  | "internal"
  // 入口点(不常见)
  | "entry-point-build"
  | "entry-point-run"
}

const transpiler = new Bun.Transpiler({ loader: "jsx" });