API

API 可以使用三種語言中的其中一種存取:命令列、JavaScript 和 Go。這三種語言的概念和參數在很大程度上是相同的,因此它們將在此處一起呈現,而不是為每種語言提供單獨的文件。您可以使用每個程式碼範例右上角的 CLIJSGo 標籤在語言之間切換。每種語言的一些具體資訊

概觀

兩個最常用的 esbuild API 是 buildtransform。每個 API 都在下面以高階方式描述,接著是每個個別 API 選項的文件。

建置

這是 esbuild 的主要介面。您通常會傳遞一個或多個 進入點 檔案進行處理以及各種選項,然後 esbuild 會將結果寫回檔案系統。以下是一個啟用 套件輸出目錄 的簡單範例

CLI JS Go
esbuild app.ts --bundle --outdir=dist
import * as esbuild from 'esbuild'

let result = await esbuild.build({
  entryPoints: ['app.ts'],
  bundle: true,
  outdir: 'dist',
})
console.log(result)
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Bundle:      true,
    Outdir:      "dist",
  })
  if len(result.Errors) != 0 {
    os.Exit(1)
  }
}

進階使用 build API 包含設定一個長期執行的建置內容。此內容在 JS 和 Go 中是一個明確的物件,但在 CLI 中是隱含的。使用特定內容所執行的所有建置會共用相同的建置選項,後續的建置會以遞增方式執行(亦即,它們會重複使用先前建置的一些工作以提升效能)。這對於開發很有用,因為 esbuild 可以在你工作時在背景中為你重新建置你的應用程式。

有三個不同的遞增建置 API

CLI JS Go
esbuild app.ts --bundle --outdir=dist --watch
[watch] build finished, watching for changes...
let ctx = await esbuild.context({
  entryPoints: ['app.ts'],
  bundle: true,
  outdir: 'dist',
})

await ctx.watch()
ctx, err := api.Context(api.BuildOptions{
  EntryPoints: []string{"app.ts"},
  Bundle:      true,
  Outdir:      "dist",
})

err2 := ctx.Watch(api.WatchOptions{})
CLI JS Go
esbuild app.ts --bundle --outdir=dist --serve

 > Local:   http://127.0.0.1:8000/
 > Network: http://192.168.0.1:8000/

127.0.0.1:61302 - "GET /" 200 [1ms]
let ctx = await esbuild.context({
  entryPoints: ['app.ts'],
  bundle: true,
  outdir: 'dist',
})

let { host, port } = await ctx.serve()
ctx, err := api.Context(api.BuildOptions{
  EntryPoints: []string{"app.ts"},
  Bundle:      true,
  Outdir:      "dist",
})

server, err2 := ctx.Serve(api.ServeOptions{})
CLI JS Go
# The CLI does not have an API for "rebuild"
let ctx = await esbuild.context({
  entryPoints: ['app.ts'],
  bundle: true,
  outdir: 'dist',
})

for (let i = 0; i < 5; i++) {
  let result = await ctx.rebuild()
}
ctx, err := api.Context(api.BuildOptions{
  EntryPoints: []string{"app.ts"},
  Bundle:      true,
  Outdir:      "dist",
})

for i := 0; i < 5; i++ {
  result := ctx.Rebuild()
}

這三個遞增建置 API 可以結合使用。若要啟用 即時重新載入(在你編輯和儲存檔案時自動重新載入頁面),你需要在同一個內容上同時啟用 監控提供服務

當你完成使用內容物件時,你可以呼叫內容上的 dispose() 來等待現有的建置完成,停止監控和/或提供服務模式,並釋放資源。

建置和內容 API 都採用下列選項

轉換

這是 build 的一個受限特殊案例,它會轉換一個表示隔離環境中記憶體檔案的程式碼字串,該環境與任何其他檔案完全斷開連接。常見用途包括縮小程式碼和將 TypeScript 轉換為 JavaScript。以下是一個範例

CLI JS Go
echo 'let x: number = 1' | esbuild --loader=ts
let x = 1;
import * as esbuild from 'esbuild'

let ts = 'let x: number = 1'
let result = await esbuild.transform(ts, {
  loader: 'ts',
})
console.log(result)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  ts := "let x: number = 1"
  result := api.Transform(ts, api.TransformOptions{
    Loader: api.LoaderTS,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

在某些使用案例中,將字串而非檔案作為輸入更符合人體工學。檔案系統隔離有某些優點(例如在瀏覽器中運作,不受鄰近的 package.json 檔案影響)和某些缺點(例如無法與 bundlingplugins 搭配使用)。如果你的使用案例不符合轉換 API,則你應該改用更通用的 build API。

轉換 API 會採用下列選項

JS 特定的詳細資料

esbuild 的 JS API 有非同步和同步兩種形式。非同步 API 是建議使用的,因為它可以在所有環境中運作,而且速度更快、功能更強大。同步 API 只能在 node 中運作,而且只能執行某些操作,但有時在某些特定於 node 的情況下是必要的。詳細說明如下

非同步 API

非同步 API 呼叫會使用承諾傳回其結果。請注意,由於使用了 import 和頂層 await 關鍵字,你可能必須在 node 中使用 .mjs 檔案副檔名

import * as esbuild from 'esbuild'

let result1 = await esbuild.transform(code, options)
let result2 = await esbuild.build(options)

優點

缺點

同步 API

同步 API 呼叫會內嵌傳回其結果

let esbuild = require('esbuild')

let result1 = esbuild.transformSync(code, options)
let result2 = esbuild.buildSync(options)

優點

缺點

在瀏覽器中

esbuild API 也可以在 Web Worker 中使用 WebAssembly 在瀏覽器中執行。要利用這項優勢,您需要安裝 esbuild-wasm 套件,而不是 esbuild 套件

npm install esbuild-wasm

瀏覽器的 API 與節點的 API 類似,但您需要先呼叫 initialize(),並且需要傳遞 WebAssembly 二進位檔的 URL。API 的同步版本也不可用。假設您正在使用打包器,它看起來會像這樣

import * as esbuild from 'esbuild-wasm'

await esbuild.initialize({
  wasmURL: './node_modules/esbuild-wasm/esbuild.wasm',
})

let result1 = await esbuild.transform(code, options)
let result2 = esbuild.build(options)

如果您已經從工作人員執行此程式碼,並且不希望 initialize 建立另一個工作人員,您可以傳遞 worker: false 給它。然後它將在呼叫 initialize 的執行緒中建立一個 WebAssembly 模組。

您也可以在 HTML 檔案中使用 <script> 標籤將 esbuild 的 API 作為腳本標籤,而不需要使用打包器,方法是載入 lib/browser.min.js 檔案。在這種情況下,API 會建立一個名為 esbuild 的全域變數,其中包含 API 物件

<script src="./node_modules/esbuild-wasm/lib/browser.min.js"></script>
<script>
  esbuild.initialize({
    wasmURL: './node_modules/esbuild-wasm/esbuild.wasm',
  }).then(() => {
    ...
  })
</script>

如果您想將此 API 與 ECMAScript 模組一起使用,您應該匯入 esm/browser.min.js 檔案

<script type="module">
  import * as esbuild from './node_modules/esbuild-wasm/esm/browser.min.js'

  await esbuild.initialize({
    wasmURL: './node_modules/esbuild-wasm/esbuild.wasm',
  })

  ...
</script>

一般選項

套件

支援:建立

套件檔案表示將任何匯入的相依項內嵌到檔案本身中。這個程序是遞迴的,因此相依項的相依項(依此類推)也會被內嵌。預設情況下,esbuild 不會 套件輸入檔案。必須像這樣明確啟用套件

CLI JS Go
esbuild in.js --bundle
import * as esbuild from 'esbuild'

console.log(await esbuild.build({
  entryPoints: ['in.js'],
  bundle: true,
  outfile: 'out.js',
}))
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"in.js"},
    Bundle:      true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

請參閱 入門指南,以取得使用實際程式碼套件的範例。

請注意,套件與檔案串接不同。在啟用套件的情況下傳遞多個輸入檔案給 esbuild,將會建立多個獨立的套件,而不是將輸入檔案串接在一起。要使用 esbuild 將一組檔案串接在一起,請將它們全部匯入單一進入點檔案,並使用 esbuild 套件該檔案。

不可分析的匯入

匯入路徑目前僅在它們是字串文字或 glob 模式 時才會被套件。其他形式的匯入路徑不會被套件,而是會原樣保留在產生的輸出中。這是因為套件是編譯時期的作業,而 esbuild 不支援所有形式的執行時期路徑解析。以下是幾個範例

// Analyzable imports (will be bundled by esbuild)
import 'pkg';
import('pkg');
require('pkg');
import(`./locale-${foo}.json`);
require(`./locale-${foo}.json`);

// Non-analyzable imports (will not be bundled by esbuild)
import(`pkg/${foo}`);
require(`pkg/${foo}`);
['pkg'].map(require);

處理無法分析的匯入方式是將包含此問題程式碼的套件標記為 外部,使其不會包含在套件中。然後,您需要確保在執行階段,您的套件化程式碼可以使用外部套件的副本。

有些套件化器,例如 Webpack,會嘗試支援所有形式的執行階段路徑解析,方法是將所有可能存取的檔案包含在套件中,然後在執行階段模擬檔案系統。然而,執行階段檔案系統模擬不在範圍內,也不會在 esbuild 中實作。如果您真的需要套件化執行此操作的程式碼,您可能需要使用另一個套件化器,而不是 esbuild。

Glob 型式匯入

現在可以在某些有限的情況下套件化在執行階段評估的匯入路徑。匯入路徑表達式必須是字串串接的形式,且必須以 ./../ 開頭。字串串接鏈中的每個非字串表達式都會在 Glob 模式中變成萬用字元。一些範例

// These two forms are equivalent
const json1 = require('./data/' + kind + '.json')
const json2 = require(`./data/${kind}.json`)

當您執行此操作時,esbuild 會搜尋檔案系統中所有符合模式的檔案,並將所有檔案包含在套件中,以及一個將符合的匯入路徑對應到套件化模組的對應。匯入表達式將會替換為對應的查詢。如果匯入路徑不存在於對應中,則會在執行階段擲回錯誤。產生的程式碼看起來會像這樣(省略不重要的部分以簡潔)

// data/bar.json
var require_bar = ...;

// data/foo.json
var require_foo = ...;

// require("./data/**/*.json") in example.js
var globRequire_data_json = __glob({
  "./data/bar.json": () => require_bar(),
  "./data/foo.json": () => require_foo()
});

// example.js
var json1 = globRequire_data_json("./data/" + kind + ".json");
var json2 = globRequire_data_json(`./data/${kind}.json`);

此功能適用於 require(...)import(...),因為它們都可以接受執行階段表達式。它不適用於 importexport 陳述式,因為它們無法接受執行階段表達式。如果您要防止 esbuild 嘗試套件化這些匯入,您應該將字串串接表達式移到 require(...)import(...) 之外。例如

// This will be bundled
const json1 = require('./data/' + kind + '.json')

// This will not be bundled
const path = './data/' + kind + '.json'
const json2 = require(path)

請注意,使用此功能表示 esbuild 可能會執行大量檔案系統 I/O,以尋找所有可能符合模式的檔案。這是設計使然,而不是錯誤。如果這是個問題,有兩種方法可以減少 esbuild 執行的檔案系統 I/O 數量

  1. 最簡單的方法是將所有想要匯入的檔案,放入給定執行時間匯入表達式的子目錄中,然後將子目錄包含在模式中。由於 esbuild 在模式比對期間不會考慮 .. 路徑元素,因此這會限制 esbuild 在該子目錄中進行搜尋。

  2. 另一種方法是完全阻止 esbuild 搜尋任何子目錄。esbuild 使用的模式比對演算法僅允許萬用字元比對包含 / 路徑分隔符號的內容,前提是該萬用字元在模式中其前面有一個 /。因此,例如 './data/' + x + '.json' 會將 x 與任何子目錄中的任何內容比對,而 './data-' + x + '.json' 則只會將 x 與頂層目錄中的任何內容比對(但不會與任何子目錄中的內容比對)。

取消

支援:建立

如果您使用 重建 手動呼叫增量建置,您可能想要使用這個取消 API 來提早結束目前的建置,以便您可以開始新的建置。您可以這樣做

CLI JS Go
# The CLI does not have an API for "cancel"
import * as esbuild from 'esbuild'
import process from 'node:process'

let ctx = await esbuild.context({
  entryPoints: ['app.ts'],
  bundle: true,
  outdir: 'www',
  logLevel: 'info',
})

// Whenever we get some data over stdin
process.stdin.on('data', async () => {
  try {
    // Cancel the already-running build
    await ctx.cancel()

    // Then start a new build
    console.log('build:', await ctx.rebuild())
  } catch (err) {
    console.error(err)
  }
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  ctx, err := api.Context(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Bundle:      true,
    Outdir:      "www",
    LogLevel:    api.LogLevelInfo,
  })
  if err != nil {
    os.Exit(1)
  }

  // Whenever we get some data over stdin
  buf := make([]byte, 100)
  for {
    if n, err := os.Stdin.Read(buf); err != nil || n == 0 {
      break
    }
    go func() {
      // Cancel the already-running build
      ctx.Cancel()

      // Then start a new build
      result := ctx.Rebuild()
      fmt.Fprintf(os.Stderr, "build: %v\n", result)
    }()
  }
}

請務必等到取消操作完成後再開始新的建置(也就是在使用 JavaScript 時,await 傳回的承諾),否則下一個 重建 會提供給您剛剛取消但尚未結束的建置。請注意,無論建置是否已取消,外掛 on-end 回呼 仍會執行。

即時重新載入

支援:建立

即時重新載入是一種開發方法,您可以在程式碼編輯器中同時開啟並查看瀏覽器。當您編輯並儲存原始碼時,瀏覽器會自動重新載入,而重新載入的應用程式版本會包含您的變更。這表示您可以更快地進行反覆運算,因為您不必在每次變更後手動切換到瀏覽器、重新載入,然後再切換回程式碼編輯器。例如,在變更 CSS 時,這非常有幫助。

esbuild 沒有直接提供即時重新載入的 API。相反地,您可以透過結合 監控模式(在您編輯並儲存檔案時自動開始建置)和 提供模式(提供最新的建置,但會封鎖直到完成)以及一小段您只在開發期間新增到應用程式的用戶端 JavaScript 程式碼,來建構即時重新載入。

第一步是同時啟用 監控提供

CLI JS Go
esbuild app.ts --bundle --outdir=www --watch --servedir=www
import * as esbuild from 'esbuild'

let ctx = await esbuild.context({
  entryPoints: ['app.ts'],
  bundle: true,
  outdir: 'www',
})

await ctx.watch()

let { host, port } = await ctx.serve({
  servedir: 'www',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  ctx, err := api.Context(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Bundle:      true,
    Outdir:      "www",
  })
  if err != nil {
    os.Exit(1)
  }

  err2 := ctx.Watch(api.WatchOptions{})
  if err2 != nil {
    os.Exit(1)
  }

  result, err3 := ctx.Serve(api.ServeOptions{
    Servedir: "www",
  })
  if err3 != nil {
    os.Exit(1)
  }
}

第二步是在您的 JavaScript 中新增一些訂閱 /esbuild 伺服器傳送事件 來源的程式碼。當您收到 change 事件時,您可以重新載入頁面以取得最新版本的應用程式。您可以在一行程式碼中執行此操作

new EventSource('/esbuild').addEventListener('change', () => location.reload())

這樣就完成了!如果您在瀏覽器中載入應用程式,現在頁面應該會在您編輯並儲存檔案時自動重新載入(假設沒有建置錯誤)。

這只應在開發期間包含,不應包含在生產中。在生產中移除此程式碼的一種方法是使用 if 陳述式(例如 if (!window.IS_PRODUCTION))來保護它,然後使用 定義 在生產中將 window.IS_PRODUCTION 設定為 true

即時重新載入注意事項

像這樣實作即時重新載入有一些已知的注意事項

CSS 的熱重新載入

change 事件還包含其他資訊,以啟用更進階的使用案例。它目前包含 addedremovedupdated 陣列,其中包含自上次建置以來已變更檔案的路徑,可以用以下 TypeScript 介面來描述

interface ChangeEvent {
  added: string[]
  removed: string[]
  updated: string[]
}

以下程式碼範例啟用了 CSS 的「熱重新載入」,這表示 CSS 會自動在原處更新,而不會重新載入頁面。如果出現的事件與 CSS 無關,則整個頁面將會重新載入作為備用方案

new EventSource('/esbuild').addEventListener('change', e => {
  const { added, removed, updated } = JSON.parse(e.data)

  if (!added.length && !removed.length && updated.length === 1) {
    for (const link of document.getElementsByTagName("link")) {
      const url = new URL(link.href)

      if (url.host === location.host && url.pathname === updated[0]) {
        const next = link.cloneNode()
        next.href = updated[0] + '?' + Math.random().toString(36).slice(2)
        next.onload = () => link.remove()
        link.parentNode.insertBefore(next, link.nextSibling)
        return
      }
    }
  }

  location.reload()
})

JavaScript 的熱重新載入

esbuild 目前尚未實作 JavaScript 的熱重新載入。因為 CSS 是無狀態的,所以可以透明地實作 CSS 的熱重新載入,但 JavaScript 是有狀態的,因此你無法像對待 CSS 那樣透明地實作 JavaScript 的熱重新載入。

一些其他開發伺服器還是實作了 JavaScript 的熱重新載入,但它需要額外的 API,有時需要特定於架構的破解,有時會在編輯階段引入暫態狀態相關錯誤。這超出了 esbuild 的範圍。如果你需要 JavaScript 的熱重新載入,歡迎使用其他工具,而不是 esbuild。

不過,透過 esbuild 的即時重新載入,你可以將應用程式的目前 JavaScript 狀態保留在 sessionStorage 中,以便在重新載入頁面後更輕鬆地還原應用程式的 JavaScript 狀態。如果你的應用程式載入得很快(為了使用者的利益,它本來就應該很快),使用 JavaScript 即時重新載入幾乎和使用 JavaScript 熱重新載入一樣快。

平台

支援:建置轉換

預設情況下,esbuild 的捆綁器設定為產生供瀏覽器使用的程式碼。如果你的捆綁程式碼打算在節點中執行,你應該將平台設定為 node

CLI JS Go
esbuild app.js --bundle --platform=node
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  platform: 'node',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Platform:    api.PlatformNode,
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

當平台設定為 browser(預設值)時

當平台設定為 node

當平台設定為 neutral

另請參閱 瀏覽器套件節點套件

重建

支援:建立

如果你的使用案例涉及重複使用相同的選項呼叫 esbuild 的 build API,你可能需要使用此 API。例如,如果你正在實作自己的檔案監控服務,這會很有用。重建比重新建置更有效率,因為前次建置的一些資料會快取,如果自上次建置以來原始檔案沒有變更,就可以重複使用。重建 API 目前使用兩種快取形式

以下是執行重建的方法

CLI JS Go
# The CLI does not have an API for "rebuild"
import * as esbuild from 'esbuild'

let ctx = await esbuild.context({
  entryPoints: ['app.js'],
  bundle: true,
  outfile: 'out.js',
})

// Call "rebuild" as many times as you want
for (let i = 0; i < 5; i++) {
  let result = await ctx.rebuild()
}

// Call "dispose" when you're done to free up resources
ctx.dispose()
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  ctx, err := api.Context(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Outfile:     "out.js",
  })
  if err != nil {
    os.Exit(1)
  }

  // Call "Rebuild" as many times as you want
  for i := 0; i < 5; i++ {
    result := ctx.Rebuild()
    if len(result.Errors) > 0 {
      os.Exit(1)
    }
  }

  // Call "Dispose" when you're done to free up resources
  ctx.Dispose()
}

提供服務

支援:建立

如果你希望在編輯時自動重新載入你的應用程式,你應該閱讀有關 即時重新載入 的資訊。它結合提供服務模式和 監控模式 來偵聽檔案系統的變更。

提供服務模式會啟動一個網路伺服器,將你的程式碼提供給裝置上的瀏覽器。以下是一個範例,它將 src/app.ts 套件成 www/js/app.js,然後透過 http://localhost:8000/ 提供 www 目錄。

CLI JS Go
esbuild src/app.ts --outdir=www/js --bundle --servedir=www
import * as esbuild from 'esbuild'

let ctx = await esbuild.context({
  entryPoints: ['src/app.ts'],
  outdir: 'www/js',
  bundle: true,
})

let { host, port } = await ctx.serve({
  servedir: 'www',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  ctx, err := api.Context(api.BuildOptions{
    EntryPoints: []string{"src/app.ts"},
    Outdir:     "www/js",
    Bundle:      true,
  })
  if err != nil {
    os.Exit(1)
  }

  server, err2 := ctx.Serve(api.ServeOptions{
    Servedir: "www",
  })
  if err2 != nil {
    os.Exit(1)
  }

  // Returning from main() exits immediately in Go.
  // Block forever so we keep serving and don't exit.
  <-make(chan struct{})
}

如果你使用以下內容建立 www/index.html 檔案,當你導覽至 http://localhost:8000/ 時,儲存在 src/app.ts 中的程式碼就會載入。

<script src="js/app.js"></script>

使用 esbuild 內建的網路伺服器而不是其他網路伺服器的一個好處是,每當你重新載入時,esbuild 提供的檔案總是最新。其他開發設定不一定會這樣。一個常見的設定是執行一個本機檔案監控器,在輸入檔案變更時重建輸出檔案,然後再單獨執行一個本機檔案伺服器來提供這些輸出檔案。但是,這表示在編輯後重新載入可能會重新載入舊的輸出檔案,如果重建尚未完成。使用 esbuild 的網路伺服器,每個傳入的請求都會在尚未進行中時啟動重建,然後等待目前的重建完成後再提供檔案。這表示 esbuild 永遠不會提供過期的建置結果。

請注意,此網路伺服器僅供開發使用。請勿在生產環境中使用。

引數

提供服務 API 的引數如下

CLI JS Go
# Enable serve mode
--serve

# Set the port
--serve=9000

# Set the host and port (IPv4)
--serve=127.0.0.1:9000

# Set the host and port (IPv6)
--serve=[::1]:9000

# Set the directory to serve
--servedir=www

# Enable HTTPS
--keyfile=your.key --certfile=your.cert

# Specify a fallback HTML file
--serve-fallback=some-file.html
interface ServeOptions {
  port?: number
  host?: string
  servedir?: string
  keyfile?: string
  certfile?: string
  fallback?: string
  onRequest?: (args: ServeOnRequestArgs) => void
}

interface ServeOnRequestArgs {
  remoteAddress: string
  method: string
  path: string
  status: number
  timeInMS: number
}
type ServeOptions struct {
  Port      uint16
  Host      string
  Servedir  string
  Keyfile   string
  Certfile  string
  Fallback  string
  OnRequest func(ServeOnRequestArgs)
}

type ServeOnRequestArgs struct {
  RemoteAddress string
  Method        string
  Path          string
  Status        int
  TimeInMS      int
}

傳回值

CLI JS Go
# The CLI will print the host and port like this:

 > Local: http://127.0.0.1:8000/
interface ServeResult {
  host: string
  port: number
}
type ServeResult struct {
  Host string
  Port uint16
}

啟用 HTTPS

預設情況下,esbuild 的網路伺服器使用 http:// 協定。然而,某些現代網路功能無法在 HTTP 網站使用。如果您想要使用這些功能,您需要告訴 esbuild 改用 https:// 協定。

使用 esbuild 啟用 HTTPS

  1. 產生自簽憑證。有許多方法可以做到這一點。以下提供一種方法,假設您已安裝 openssl 指令

     openssl req -x509 -newkey rsa:4096 -keyout your.key -out your.cert -days 9999 -nodes -subj /CN=127.0.0.1 
  2. 使用 keyfilecertfile 提供參數your.keyyour.cert 傳遞給 esbuild。

  3. 當您載入您的網頁時,按掉瀏覽器中令人害怕的警告(自簽憑證不安全,但這沒關係,因為我們只是在進行本機開發)。

如果您有比這更複雜的需求,您仍然可以 在 esbuild 之前放置一個代理程式,並改用它來使用 HTTPS。請注意,如果您在載入網頁時看到訊息 Client sent an HTTP request to an HTTPS server,表示您使用的是不正確的協定。在瀏覽器的網址列中將 http:// 替換為 https://

請記住,esbuild 的 HTTPS 支援與安全性無關。在 esbuild 中啟用 HTTPS 的唯一原因是瀏覽器讓在不使用這些額外步驟的情況下使用某些現代網路功能進行本機開發變得不可能。請勿將 esbuild 的開發伺服器用於任何需要安全性的用途。它僅用於本機開發,而且完全沒有考慮到生產環境。

自訂伺服器行為

無法連接到 esbuild 的本機伺服器以自訂伺服器本身的行為。相反地,應該在 esbuild 之前放置一個代理程式來自訂行為。

以下是使用節點內建的 http 模組建立代理伺服器的簡單範例。它會新增自訂的 404 頁面,取代 esbuild 的預設 404 頁面

import * as esbuild from 'esbuild'
import http from 'node:http'

// Start esbuild's server on a random local port
let ctx = await esbuild.context({
  // ... your build options go here ...
})

// The return value tells us where esbuild's local server is
let { host, port } = await ctx.serve({ servedir: '.' })

// Then start a proxy server on port 3000
http.createServer((req, res) => {
  const options = {
    hostname: host,
    port: port,
    path: req.url,
    method: req.method,
    headers: req.headers,
  }

  // Forward each incoming request to esbuild
  const proxyReq = http.request(options, proxyRes => {
    // If esbuild returns "not found", send a custom 404 page
    if (proxyRes.statusCode === 404) {
      res.writeHead(404, { 'Content-Type': 'text/html' })
      res.end('<h1>A custom 404 page</h1>')
      return
    }

    // Otherwise, forward the response from esbuild to the client
    res.writeHead(proxyRes.statusCode, proxyRes.headers)
    proxyRes.pipe(res, { end: true })
  })

  // Forward the body of the request to esbuild
  req.pipe(proxyReq, { end: true })
}).listen(3000)

這段程式碼會在隨機的本機埠上啟動 esbuild 的伺服器,然後在埠 3000 上啟動代理伺服器。在開發期間,你會在瀏覽器中載入 http://localhost:3000,它會與代理伺服器通訊。此範例示範如何在 esbuild 處理請求後修改回應,但你也可以在 esbuild 處理請求之前修改或取代請求。

你可以使用像這樣的代理伺服器做很多事情,包括

如果你有更進階的需求,也可以使用像 nginx 這樣的真實代理伺服器。

Tsconfig

支援:建立

通常,build API 會自動偵測 tsconfig.json 檔案,並在建置期間讀取其內容。但是,你也可以設定自訂的 tsconfig.json 檔案來使用。如果你需要使用不同的設定對同一份程式碼進行多次建置,這會很有用

CLI JS Go
esbuild app.ts --bundle --tsconfig=custom-tsconfig.json
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.ts'],
  bundle: true,
  tsconfig: 'custom-tsconfig.json',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Bundle:      true,
    Tsconfig:    "custom-tsconfig.json",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

Tsconfig raw

支援:建置轉換

此選項可用於將你的 tsconfig.json 檔案傳遞給 transform API,它不會存取檔案系統。它也可以用於將你的 tsconfig.json 檔案的內容內嵌傳遞給 build API,而不用將它寫入檔案。使用它的方式如下

CLI JS Go
echo 'class Foo { foo }' | esbuild --loader=ts --tsconfig-raw='{"compilerOptions":{"useDefineForClassFields":false}}'
import * as esbuild from 'esbuild'

let ts = 'class Foo { foo }'
let result = await esbuild.transform(ts, {
  loader: 'ts',
  tsconfigRaw: `{
    "compilerOptions": {
      "useDefineForClassFields": false,
    },
  }`,
})
console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  ts := "class Foo { foo }"

  result := api.Transform(ts, api.TransformOptions{
    Loader: api.LoaderTS,
    TsconfigRaw: `{
      "compilerOptions": {
        "useDefineForClassFields": false,
      },
    }`,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

監控

支援:建立

啟用監控模式會指示 esbuild 監聽檔案系統的變更,並在可能使建置失效的檔案變更時自動重新建置。使用它的方式如下

CLI JS Go
esbuild app.js --outfile=out.js --bundle --watch
[watch] build finished, watching for changes...
import * as esbuild from 'esbuild'

let ctx = await esbuild.context({
  entryPoints: ['app.js'],
  outfile: 'out.js',
  bundle: true,
})

await ctx.watch()
console.log('watching...')
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  ctx, err := api.Context(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Outfile:     "out.js",
    Bundle:      true,
    Write:       true,
  })
  if err != nil {
    os.Exit(1)
  }

  err2 := ctx.Watch(api.WatchOptions{})
  if err2 != nil {
    os.Exit(1)
  }
  fmt.Printf("watching...\n")

  // Returning from main() exits immediately in Go.
  // Block forever so we keep watching and don't exit.
  <-make(chan struct{})
}

如果你想要在未來某個時間點停止監控模式,你可以呼叫 context 物件上的 dispose 來終止檔案監控器

CLI JS Go
# Use Ctrl+C to stop the CLI in watch mode
import * as esbuild from 'esbuild'

let ctx = await esbuild.context({
  entryPoints: ['app.js'],
  outfile: 'out.js',
  bundle: true,
})

await ctx.watch()
console.log('watching...')

await new Promise(r => setTimeout(r, 10 * 1000))
await ctx.dispose()
console.log('stopped watching')
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"
import "time"

func main() {
  ctx, err := api.Context(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Outfile:     "out.js",
    Bundle:      true,
    Write:       true,
  })
  if err != nil {
    os.Exit(1)
  }

  err2 := ctx.Watch(api.WatchOptions{})
  if err2 != nil {
    os.Exit(1)
  }
  fmt.Printf("watching...\n")

  time.Sleep(10 * time.Second)
  ctx.Dispose()
  fmt.Printf("stopped watching\n")
}

esbuild 中的監控模式使用輪詢,而非作業系統特定的檔案系統 API,以實現可攜性。輪詢系統的設計是使用相對較少的 CPU,相較於一次掃描整個目錄樹的傳統輪詢系統。檔案系統仍會定期掃描,但每次掃描只會檢查檔案的隨機子集,這表示檔案的變更會在變更後不久被偵測到,但並不一定會立即偵測到。

根據目前的啟發法,大型專案應每 2 秒左右完全掃描一次,因此在最糟的情況下,可能需要長達 2 秒才能偵測到變更。但是,在偵測到變更後,變更的路徑會被加入最近變更路徑的簡短清單中,並在每次掃描時檢查,因此最近變更檔案的後續變更應會幾乎立即被偵測到。

請注意,如果你不想使用基於輪詢的方法,仍然可以使用 esbuild 的 重建 API 和你選擇的檔案監控程式庫,自行實作監控模式。

如果你使用 CLI,請記住當 esbuild 的標準輸入關閉時,監控模式將會終止。這可防止 esbuild 意外地比父程序存活更久,並意外地繼續消耗系統資源。如果你有需要 esbuild 在父程序結束後仍持續監控的用例,你可以使用 --watch=forever,而非 --watch

輸入

進入點

支援:建立

這是一個檔案陣列,每個檔案都作為綑綁演算法的輸入。它們被稱為「進入點」,因為每個進入點都應為要評估的初始腳本,然後載入它所代表的程式碼的所有其他面向。你無需使用 <script> 標籤在你的網頁中載入許多程式庫,而是可以使用 import 陳述式將它們匯入你的進入點(或匯入到然後匯入你的進入點的另一個檔案中)。

簡單的應用程式只需要一個進入點,但如果有多個邏輯上獨立的程式碼群組,例如主執行緒和工作執行緒,或具有獨立且關聯性較低的區域(例如登入頁、編輯器頁和設定頁)的應用程式,則其他進入點會很有用。分開的進入點有助於引入關注點分離,並有助於減少瀏覽器需要下載的不必要程式碼量。如果適用,啟用 程式碼分割 可以進一步減少下載大小,當瀏覽進入點與已瀏覽的第一個頁面共用一些已下載程式碼的第二個頁面時。

指定進入點的簡單方法是傳遞檔案路徑陣列

CLI JS Go
esbuild home.ts settings.ts --bundle --outdir=out
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['home.ts', 'settings.ts'],
  bundle: true,
  write: true,
  outdir: 'out',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"home.ts", "settings.ts"},
    Bundle:      true,
    Write:       true,
    Outdir:      "out",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

這將產生兩個輸出檔案,out/home.jsout/settings.js,分別對應於兩個進入點 home.tssettings.ts

若要進一步控制輸出檔案的路徑如何從對應的輸入進入點衍生,你應該查看這些選項

此外,您也可以使用替代的進入點語法,為每個個別進入點指定完全自訂的輸出路徑

CLI JS Go
esbuild out1=home.ts out2=settings.ts --bundle --outdir=out
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: [
    { out: 'out1', in: 'home.ts'},
    { out: 'out2', in: 'settings.ts'},
  ],
  bundle: true,
  write: true,
  outdir: 'out',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPointsAdvanced: []api.EntryPoint{{
      OutputPath: "out1",
      InputPath:  "home.ts",
    }, {
      OutputPath: "out2",
      InputPath:  "settings.ts",
    }},
    Bundle: true,
    Write:  true,
    Outdir: "out",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

這會產生兩個輸出檔案,out/out1.jsout/out2.js,分別對應於兩個進入點 home.tssettings.ts

載入器

支援:建置轉換

此選項會變更給定的輸入檔案的解譯方式。例如,js 載入器會將檔案解譯為 JavaScript,而 css 載入器會將檔案解譯為 CSS。請參閱 內容類型 頁面,以取得所有內建載入器的完整清單。

為給定的檔案類型設定載入器,讓您可以使用 import 陳述式或 require 呼叫載入該檔案類型。例如,將 .png 檔案副檔名設定為使用 資料 URL 載入器,表示匯入 .png 檔案會提供給您包含該影像內容的資料 URL

import url from './example.png'
let image = new Image
image.src = url
document.body.appendChild(image)

import svg from './example.svg'
let doc = new DOMParser().parseFromString(svg, 'application/xml')
let node = document.importNode(doc.documentElement, true)
document.body.appendChild(node)

可以使用 build API 呼叫,以類似下列方式組合上述程式碼

CLI JS Go
esbuild app.js --bundle --loader:.png=dataurl --loader:.svg=text
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  loader: {
    '.png': 'dataurl',
    '.svg': 'text',
  },
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".png": api.LoaderDataURL,
      ".svg": api.LoaderText,
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

如果您使用 stdin 中的輸入來使用 build API,則此選項的指定方式會有所不同,因為 stdin 沒有檔案副檔名。使用 build API 為 stdin 設定載入器如下所示

CLI JS Go
echo 'import pkg = require("./pkg")' | esbuild --loader=ts --bundle
import * as esbuild from 'esbuild'

await esbuild.build({
  stdin: {
    contents: 'import pkg = require("./pkg")',
    loader: 'ts',
    resolveDir: '.',
  },
  bundle: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    Stdin: &api.StdinOptions{
      Contents:   "import pkg = require('./pkg')",
      Loader:     api.LoaderTS,
      ResolveDir: ".",
    },
    Bundle: true,
  })
  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

transform API 呼叫只會使用單一載入器,因為它不涉及與檔案系統的互動,因此不會處理檔案副檔名。為 transform API 設定載入器(在本例中為 ts 載入器)如下所示

CLI JS Go
echo 'let x: number = 1' | esbuild --loader=ts
let x = 1;
import * as esbuild from 'esbuild'

let ts = 'let x: number = 1'
let result = await esbuild.transform(ts, {
  loader: 'ts',
})
console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  ts := "let x: number = 1"
  result := api.Transform(ts, api.TransformOptions{
    Loader: api.LoaderTS,
  })
  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

Stdin

支援:建立

通常 build API 呼叫會將一個或多個檔案名稱作為輸入。不過,此選項可用於在檔案系統中完全沒有模組的情況下執行組合。它稱為「stdin」,因為它對應於在命令列上將檔案導向 stdin。

除了指定 stdin 檔案的內容外,您還可以選擇指定解析目錄(用於確定相對匯入的位置)、sourcefile(錯誤訊息和來源對應中要使用的檔案名稱)以及 loader(用於確定如何詮釋檔案內容)。CLI 沒有辦法指定解析目錄。相反地,它會自動設定為目前的作業目錄。

以下是使用此功能的方法

CLI JS Go
echo 'export * from "./another-file"' | esbuild --bundle --sourcefile=imaginary-file.js --loader=ts --format=cjs
import * as esbuild from 'esbuild'

let result = await esbuild.build({
  stdin: {
    contents: `export * from "./another-file"`,

    // These are all optional:
    resolveDir: './src',
    sourcefile: 'imaginary-file.js',
    loader: 'ts',
  },
  format: 'cjs',
  write: false,
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    Stdin: &api.StdinOptions{
      Contents: "export * from './another-file'",

      // These are all optional:
      ResolveDir: "./src",
      Sourcefile: "imaginary-file.js",
      Loader:     api.LoaderTS,
    },
    Format: api.FormatCommonJS,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

輸出內容

支援:建置轉換

使用此功能在產生的 JavaScript 和 CSS 檔案開頭插入任意字串。這通常用於插入註解

CLI JS Go
esbuild app.js --banner:js=//comment --banner:css=/*comment*/
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  banner: {
    js: '//comment',
    css: '/*comment*/',
  },
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Banner: map[string]string{
      "js":  "//comment",
      "css": "/*comment*/",
    },
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

這類似於 頁尾,但會插入在結尾而不是開頭。

請注意,如果您要將非註解程式碼插入 CSS 檔案,請注意 CSS 會忽略所有出現在非 @import 規則(@charset 規則除外)之後的 @import 規則,因此使用標語來注入 CSS 規則可能會意外停用外部樣式表的匯入。

字元集

支援:建置轉換

預設情況下,esbuild 的輸出僅限於 ASCII。任何非 ASCII 字元都會使用反斜線跳脫序列進行跳脫。原因之一是因為非 ASCII 字元預設會被瀏覽器誤解,這會造成混淆。您必須明確將 <meta charset="utf-8"> 新增到您的 HTML 或使用正確的 Content-Type 標頭提供服務,以避免瀏覽器破壞您的程式碼。另一個原因是非 ASCII 字元可能會大幅 降低瀏覽器的剖析器速度。但是,使用跳脫序列會讓產生的輸出稍微變大,也更難閱讀。

如果您希望 esbuild 在不使用跳脫序列的情況下列印原始字元,並且您已確保瀏覽器會將您的程式碼詮釋為 UTF-8,您可以透過設定字元集來停用字元跳脫

CLI JS Go
echo 'let π = Math.PI' | esbuild
let \u03C0 = Math.PI;
echo 'let π = Math.PI' | esbuild --charset=utf8
let π = Math.PI;
import * as esbuild from 'esbuild'
let js = 'let π = Math.PI'
(await esbuild.transform(js)).code
'let \\u03C0 = Math.PI;\n'
(await esbuild.transform(js, {
  charset: 'utf8',
})).code
'let π = Math.PI;\n'
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "let π = Math.PI"

  result1 := api.Transform(js, api.TransformOptions{})

  if len(result1.Errors) == 0 {
    fmt.Printf("%s", result1.Code)
  }

  result2 := api.Transform(js, api.TransformOptions{
    Charset: api.CharsetUTF8,
  })

  if len(result2.Errors) == 0 {
    fmt.Printf("%s", result2.Code)
  }
}

一些注意事項

支援:建置轉換

使用此選項在產生的 JavaScript 和 CSS 檔案結尾插入任意字串。這通常用於插入註解

CLI JS Go
esbuild app.js --footer:js=//comment --footer:css=/*comment*/
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  footer: {
    js: '//comment',
    css: '/*comment*/',
  },
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Footer: map[string]string{
      "js":  "//comment",
      "css": "/*comment*/",
    },
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

這類似於 標題,它會插入在開頭而不是結尾。

格式

支援:建置轉換

這會設定產生 JavaScript 檔案的輸出格式。目前可以設定三個可能值:iifecjsesm。當未指定輸出格式時,如果已啟用 套件(如下所述),esbuild 會為您挑選輸出格式,或如果已停用 套件,則不會進行任何格式轉換。

IIFE

iife 格式代表「立即呼叫函式表達式」,並預計在瀏覽器中執行。將您的程式碼包裝在函式表達式中可確保程式碼中的任何變數不會意外與全域範圍中的變數衝突。如果您的進入點有您想要在瀏覽器中公開為全域的匯出,您可以使用 全域名稱 設定來設定該全域的名稱。當未指定輸出格式、已啟用 套件,且 平台 設定為 browser(這是預設值)時,iife 格式會自動啟用。指定 iife 格式如下所示

CLI JS Go
echo 'alert("test")' | esbuild --format=iife
(() => {
  alert("test");
})();
import * as esbuild from 'esbuild'

let js = 'alert("test")'
let result = await esbuild.transform(js, {
  format: 'iife',
})
console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "alert(\"test\")"

  result := api.Transform(js, api.TransformOptions{
    Format: api.FormatIIFE,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

CommonJS

cjs 格式代表「CommonJS」,並預計在 node 中執行。它假設環境包含 exportsrequiremodule。使用 ECMAScript 模組語法的匯出進入點會轉換為模組,並在 exports 上為每個匯出名稱提供 getter。當未指定輸出格式、已啟用 套件,且 平台 設定為 node 時,cjs 格式會自動啟用。指定 cjs 格式如下所示

CLI JS Go
echo 'export default "test"' | esbuild --format=cjs
...
var stdin_exports = {};
__export(stdin_exports, {
  default: () => stdin_default
});
module.exports = __toCommonJS(stdin_exports);
var stdin_default = "test";
import * as esbuild from 'esbuild'

let js = 'export default "test"'
let result = await esbuild.transform(js, {
  format: 'cjs',
})
console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "export default 'test'"

  result := api.Transform(js, api.TransformOptions{
    Format: api.FormatCommonJS,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

ESM

esm 格式代表「ECMAScript 模組」。它假設環境支援 importexport 語法。以 CommonJS 模組語法匯出的進入點將轉換為 module.exports 值的單一 default 匯出。當未指定輸出格式、啟用 綑綁,且 平台 設定為 neutral 時,esm 格式將自動啟用。指定 esm 格式如下所示

CLI JS Go
echo 'module.exports = "test"' | esbuild --format=esm
...
var require_stdin = __commonJS({
  "<stdin>"(exports, module) {
    module.exports = "test";
  }
});
export default require_stdin();
import * as esbuild from 'esbuild'

let js = 'module.exports = "test"'
let result = await esbuild.transform(js, {
  format: 'esm',
})
console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "module.exports = 'test'"

  result := api.Transform(js, api.TransformOptions{
    Format: api.FormatESModule,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

esm 格式可以在瀏覽器或 node 中使用,但您必須明確將其載入為模組。如果您從另一個模組 import 它,則會自動執行此操作。否則

全域名稱

支援:建置轉換

此選項僅在 格式 設定為 iife(代表立即呼叫函式表達式)時才重要。它設定用於儲存進入點匯出的全域變數名稱

CLI JS Go
echo 'module.exports = "test"' | esbuild --format=iife --global-name=xyz
import * as esbuild from 'esbuild'

let js = 'module.exports = "test"'
let result = await esbuild.transform(js, {
  format: 'iife',
  globalName: 'xyz',
})
console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "module.exports = 'test'"

  result := api.Transform(js, api.TransformOptions{
    Format:     api.FormatIIFE,
    GlobalName: "xyz",
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

使用 iife 格式指定全域名稱將產生類似以下的程式碼

var xyz = (() => {
  ...
  var require_stdin = __commonJS((exports, module) => {
    module.exports = "test";
  });
  return require_stdin();
})();

全域名稱也可以是複合屬性表達式,在這種情況下,esbuild 將產生具有該屬性的全域變數。現有的全域變數衝突不會被覆寫。這可以用於實作「命名空間」,其中多個獨立腳本將其匯出新增到同一個全域物件。例如

CLI JS Go
echo 'module.exports = "test"' | esbuild --format=iife --global-name='example.versions["1.0"]'
import * as esbuild from 'esbuild'

let js = 'module.exports = "test"'
let result = await esbuild.transform(js, {
  format: 'iife',
  globalName: 'example.versions["1.0"]',
})
console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "module.exports = 'test'"

  result := api.Transform(js, api.TransformOptions{
    Format:     api.FormatIIFE,
    GlobalName: `example.versions["1.0"]`,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

上面使用的複合全域名稱會產生類似以下的程式碼

var example = example || {};
example.versions = example.versions || {};
example.versions["1.0"] = (() => {
  ...
  var require_stdin = __commonJS((exports, module) => {
    module.exports = "test";
  });
  return require_stdin();
})();

支援:建置轉換

「法律評論」被視為 JS 中的任何陳述級評論或 CSS 中的規則級評論,其中包含 @license@preserve,或以 //!/*! 開頭。預設情況下,這些評論會保留在輸出檔案中,因為這符合原始程式碼作者的意圖。但是,可以使用以下選項之一來設定此行為

當啟用 打包 時,預設行為為 檔案結束,否則為 內嵌。設定法律評論模式如下所示

CLI JS Go
esbuild app.js --legal-comments=eof
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  legalComments: 'eof',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:   []string{"app.js"},
    LegalComments: api.LegalCommentsEndOfFile,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

請注意,JS 的「陳述層級」和 CSS 的「規則層級」表示評論必須出現在允許多個陳述或規則的上下文中,例如頂層範圍或陳述或規則區塊。因此,表達式內或宣告層級的評論不被視為法律評論。

行數限制

支援:建置轉換

此設定是一種防止 esbuild 產生具有超長行的輸出檔案的方法,這有助於在執行不良的文字編輯器中編輯效能。將此設定為正整數,以指示 esbuild 在超過該位元組數後不久結束特定行。例如,這會在長行超過約 80 個字元後立即換行

CLI JS Go
esbuild app.ts --line-limit=80
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.ts'],
  lineLimit: 80,
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    LineLimit:   80,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

行數會在超過限制後才被截斷,而不是在之前,因為在限制被超過時檢查會比預測限制即將被超過時來得簡單,而且在產生輸出檔案時避免備份和重寫會更快。因此,限制僅為近似值。

此設定適用於 JavaScript 和 CSS,即使在停用最小化時也能運作。請注意,啟用此設定會使檔案變大,因為額外的換行符號會佔用檔案中的額外空間(即使在 gzip 壓縮後)。

分割

支援:建立

程式碼分割仍是進行中的工作。它目前僅適用於 esm 輸出 格式。在跨程式碼分割區塊的 import 陳述中也有一個已知的 排序問題。你可以追蹤 追蹤問題 以取得此功能的更新資訊。

這會啟用「程式碼分割」,其有兩個目的

啟用程式碼拆分時,也必須使用 outdir 設定來設定輸出目錄

CLI JS Go
esbuild home.ts about.ts --bundle --splitting --outdir=out --format=esm
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['home.ts', 'about.ts'],
  bundle: true,
  splitting: true,
  outdir: 'out',
  format: 'esm',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"home.ts", "about.ts"},
    Bundle:      true,
    Splitting:   true,
    Outdir:      "out",
    Format:      api.FormatESModule,
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

輸出位置

允許覆寫

支援:建立

啟用此設定允許輸出檔案覆寫輸入檔案。它預設未啟用,因為這樣做表示覆寫您的原始碼,如果您的程式碼未簽入,可能會導致資料遺失。但支援此功能可避免需要暫存目錄,讓某些工作流程更輕鬆。因此,當您想要故意覆寫原始碼時,可以啟用此功能

CLI JS Go
esbuild app.js --outdir=. --allow-overwrite
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  outdir: '.',
  allowOverwrite: true,
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:    []string{"app.js"},
    Outdir:         ".",
    AllowOverwrite: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

資產名稱

支援:建立

loader 設為 file 時,此選項會控制產生額外輸出檔案的檔案名稱。它使用範本來設定輸出路徑,其中包含會在產生輸出路徑時以特定於檔案的值取代的佔位符。例如,指定資產名稱範本為 assets/[name]-[hash] 會將所有資產放入輸出目錄內的子目錄 assets 中,並在檔案名稱中包含資產的內容雜湊。這樣做看起來像這樣

CLI JS Go
esbuild app.js --asset-names=assets/[name]-[hash] --loader:.png=file --bundle --outdir=out
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  assetNames: 'assets/[name]-[hash]',
  loader: { '.png': 'file' },
  bundle: true,
  outdir: 'out',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    AssetNames:  "assets/[name]-[hash]",
    Loader: map[string]api.Loader{
      ".png": api.LoaderFile,
    },
    Bundle: true,
    Outdir: "out",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

資產路徑範本中可以使用四個佔位符

資產路徑範本不需要包含副檔名。資產的原始副檔名會在範本替換後自動新增到輸出路徑的結尾。

此選項類似於區塊名稱進入點名稱選項。

區塊名稱

支援:建立

此選項控制在啟用區塊分割時自動產生的共用程式碼區塊的檔案名稱。它使用包含佔位符的範本設定輸出路徑,這些佔位符會在產生輸出路徑時替換為區塊的特定值。例如,指定區塊名稱範本為 chunks/[name]-[hash]會將所有產生的區塊放入輸出目錄內的 chunks 子目錄中,並在檔案名稱中包含區塊的內容雜湊。這樣做看起來像這樣

CLI JS Go
esbuild app.js --chunk-names=chunks/[name]-[hash] --bundle --outdir=out --splitting --format=esm
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  chunkNames: 'chunks/[name]-[hash]',
  bundle: true,
  outdir: 'out',
  splitting: true,
  format: 'esm',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    ChunkNames:  "chunks/[name]-[hash]",
    Bundle:      true,
    Outdir:      "out",
    Splitting:   true,
    Format:      api.FormatESModule,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

區塊路徑範本中可以使用三個佔位符

區塊路徑範本不需要包含檔案副檔名。適當內容類型的已設定輸出副檔名會在範本替換後自動新增到輸出路徑的結尾。

請注意,此選項僅控制自動產生的共用程式碼區塊的名稱。它控制與進入點相關的輸出檔案名稱。這些名稱目前是由原始進入點檔案相對於輸出基礎目錄的路徑決定的,而且此行為無法變更。未來會新增一個額外的 API 選項,讓您可以變更進入點輸出檔案的檔案名稱。

此選項類似於資產名稱進入點名稱選項。

條目名稱

支援:建立

此選項控制與每個輸入條目點檔案對應的輸出檔案的檔案名稱。它使用含有佔位符的範本設定輸出路徑,這些佔位符將在產生輸出路徑時以特定於檔案的值替換。例如,指定條目名稱範本為 [dir]/[name]-[hash] 會在檔案名稱中包含輸出檔案的雜湊,並將檔案放入輸出目錄中,可能在子目錄下(請參閱下方有關 [dir] 的詳細資訊)。執行此操作如下所示

CLI JS Go
esbuild src/main-app/app.js --entry-names=[dir]/[name]-[hash] --outbase=src --bundle --outdir=out
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['src/main-app/app.js'],
  entryNames: '[dir]/[name]-[hash]',
  outbase: 'src',
  bundle: true,
  outdir: 'out',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"src/main-app/app.js"},
    EntryNames:  "[dir]/[name]-[hash]",
    Outbase:     "src",
    Bundle:      true,
    Outdir:      "out",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

條目路徑範本中可以使用四個佔位符

入口路徑範本不需要包含檔案副檔名。適當的 輸出副檔名 會在範本替換後自動新增到輸出路徑的結尾,根據檔案類型而定。

此選項類似於 資產名稱區塊名稱 選項。

輸出副檔名

支援:建立

此選項可讓您自訂 esbuild 所產生檔案的檔案副檔名,改為 .js.css 以外的副檔名。特別是 .mjs.cjs 檔案副檔名在節點中具有特殊意義(它們分別表示 ESM 和 CommonJS 格式的檔案)。如果您使用 esbuild 來產生多個檔案,而且必須使用 輸出目錄 選項而非 輸出檔案 選項,則此選項會很有用。您可以像這樣使用它

CLI JS Go
esbuild app.js --bundle --outdir=dist --out-extension:.js=.mjs
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  outdir: 'dist',
  outExtension: { '.js': '.mjs' },
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Outdir:      "dist",
    OutExtension: map[string]string{
      ".js": ".mjs",
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

輸出基礎

支援:建立

如果您的組建包含多個位於不同目錄中的入口點,則目錄結構將複製到 輸出目錄 中,相對於輸出基礎目錄。例如,如果有兩個入口點 src/pages/home/index.tssrc/pages/about/index.ts,而輸出基礎目錄是 src,則輸出目錄將包含 pages/home/index.jspages/about/index.js。以下是使用方法

CLI JS Go
esbuild src/pages/home/index.ts src/pages/about/index.ts --bundle --outdir=out --outbase=src
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: [
    'src/pages/home/index.ts',
    'src/pages/about/index.ts',
  ],
  bundle: true,
  outdir: 'out',
  outbase: 'src',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{
      "src/pages/home/index.ts",
      "src/pages/about/index.ts",
    },
    Bundle:  true,
    Outdir:  "out",
    Outbase: "src",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

如果未指定輸出基礎目錄,則它預設為所有輸入入口點路徑中的 最低共同祖先 目錄。在上面的範例中,這是 src/pages,這表示輸出目錄預設將包含 home/index.jsabout/index.js

輸出目錄

支援:建立

此選項設定組建作業的輸出目錄。例如,此指令將產生一個稱為 out 的目錄

CLI JS Go
esbuild app.js --bundle --outdir=out
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  outdir: 'out',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Outdir:      "out",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

如果輸出目錄尚未存在,則將產生該目錄,但如果其中已包含一些檔案,則不會清除該目錄。任何產生的檔案都將靜默覆寫具有相同名稱的現有檔案。如果您希望輸出目錄僅包含來自目前 esbuild 執行的檔案,則您應該在執行 esbuild 之前清除輸出目錄。

如果你的組建包含多個位於不同目錄中的進入點,目錄結構將從所有輸入進入點路徑中的 最低共祖 目錄開始複製到輸出目錄。例如,如果有兩個進入點 src/home/index.tssrc/about/index.ts,輸出目錄將包含 home/index.jsabout/index.js。如果你想自訂此行為,你應該變更 outbase 目錄

輸出檔案

支援:建立

此選項設定組建操作的輸出檔案名稱。這僅適用於單一進入點的情況。如果有多個進入點,你必須使用 outdir 選項來指定輸出目錄。使用 outfile 的方式如下

CLI JS Go
esbuild app.js --bundle --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Outdir:      "out.js",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

公開路徑

支援:建立

這與 外部檔案 載入器結合使用時很有用。預設情況下,該載入器使用 default 輸出將匯入檔案的名稱作為字串輸出。公開路徑選項讓你可以在此載入器載入的每個檔案的匯出字串前面加上一個基本路徑

CLI JS Go
esbuild app.js --bundle --loader:.png=file --public-path=https://www.example.com/v1 --outdir=out
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  loader: { '.png': 'file' },
  publicPath: 'https://www.example.com/v1',
  outdir: 'out',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Loader: map[string]api.Loader{
      ".png": api.LoaderFile,
    },
    Outdir:     "out",
    PublicPath: "https://www.example.com/v1",
    Write:      true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

寫入

支援:建立

組建 API 呼叫可以寫入檔案系統或將會寫入的檔案作為記憶體中的緩衝區傳回。預設情況下,CLI 和 JavaScript API 會寫入檔案系統,而 Go API 則不會。若要使用記憶體中的緩衝區

JS Go
import * as esbuild from 'esbuild'

let result = await esbuild.build({
  entryPoints: ['app.js'],
  sourcemap: 'external',
  write: false,
  outdir: 'out',
})

for (let out of result.outputFiles) {
  console.log(out.path, out.contents, out.hash, out.text)
}
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Sourcemap:   api.SourceMapExternal,
    Write:       false,
    Outdir:      "out",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }

  for _, out := range result.OutputFiles {
    fmt.Printf("%v %v %s\n", out.Path, out.Contents, out.Hash)
  }
}

hash 屬性是 contents 欄位的雜湊,已提供以供方便。雜湊演算法(目前為 XXH64)取決於實作,且可能在 esbuild 版本之間隨時變更。

路徑解析

別名

支援:建立

此功能讓你可以在打包時將一個套件替換為另一個套件。以下範例將套件 oldpkg 替換為套件 newpkg

CLI JS Go
esbuild app.js --bundle --alias:oldpkg=newpkg
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  write: true,
  alias: {
    'oldpkg': 'newpkg',
  },
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Write:       true,
    Alias: map[string]string{
      "oldpkg": "newpkg",
    },
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

這些新的替換會在 esbuild 的所有其他路徑解析邏輯之前先發生。此功能的一個用例是在你無法控制的第三方程式碼中將僅限節點的套件替換為瀏覽器友善的套件。

請注意,當使用別名替換匯入路徑時,產生的匯入路徑會在工作目錄中解析,而不是在包含具有匯入路徑的來源檔案的目錄中解析。如有需要,可以使用 工作目錄 功能設定 esbuild 使用的工作目錄。

條件

支援:建立

此功能控制如何詮釋 package.json 中的 exports 欄位。可以使用條件設定新增自訂條件。您可以指定任意數量的條件,而這些條件的意義完全取決於套件作者。目前 Node 只核准 developmentproduction 自訂條件供推薦使用。以下是新增自訂條件 custom1custom2 的範例

CLI JS Go
esbuild src/app.js --bundle --conditions=custom1,custom2
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['src/app.js'],
  bundle: true,
  conditions: ['custom1', 'custom2'],
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"src/app.js"},
    Bundle:      true,
    Conditions:  []string{"custom1", "custom2"},
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

條件運作方式

條件讓您可以在不同的情況下將相同的匯入路徑重新導向到不同的檔案位置。包含條件和路徑的重新導向對應儲存在套件 package.json 檔案中的 exports 欄位。例如,這會使用 importrequire 條件將 require('pkg/foo') 重新對應到 pkg/required.cjs,並將 import 'pkg/foo' 重新對應到 pkg/imported.mjs

{
  "name": "pkg",
  "exports": {
    "./foo": {
      "import": "./imported.mjs",
      "require": "./required.cjs",
      "default": "./fallback.js"
    }
  }
}

條件會按照它們在 JSON 檔案中出現的順序進行檢查。因此,上述範例的行為類似於這樣

if (importPath === './foo') {
  if (conditions.has('import')) return './imported.mjs'
  if (conditions.has('require')) return './required.cjs'
  return './fallback.js'
}

預設情況下,esbuild 內建五個具有特殊行為的條件,且無法停用

platform 設為 browsernode 且未設定任何自訂條件時,也會自動包含以下條件。如果設定了任何自訂條件(即使是空清單),此條件將不再自動包含

請注意,當您使用 requireimport 條件時,您的套件可能會多次出現在套件中!這是一個微妙的問題,除了會讓產生的套件過大之外,還會因為程式碼狀態的重複副本而導致錯誤。這通常稱為 雙重套件風險

避免雙重套件風險的一種方法,適用於打包器和在 node 中原生執行,是將所有程式碼放入 require 條件中,作為 CommonJS,並讓 import 條件只是一個輕量的 ESM 封裝,它會對您的套件呼叫 require 並使用 ESM 語法重新匯出套件。然而,此方法無法提供良好的樹狀搖晃,因為 esbuild 不會對 CommonJS 模組進行樹狀搖晃。

避免雙重套件風險的另一種方法是使用特定於打包器的 module 條件,以指示打包器始終載入套件的 ESM 版本,同時讓 node 始終回退到套件的 CommonJS 版本。importmodule 都用於 ESM,但與 import 不同,即使使用 require 呼叫載入匯入路徑,module 條件也會始終啟動。這對於打包器來說效果很好,因為打包器支援使用 require 載入 ESM,但這不是 node 可以做到的,因為 node 故意不實作使用 require 載入 ESM。

外部

支援:建立

您可以將檔案或套件標記為外部,以將其從您的建置中排除。匯入將被保留(對 iifecjs 格式使用 require,對 esm 格式使用 import),而不是被打包,並將在執行時進行評估。

這有幾個用途。首先,它可用於修剪套件中不必要的程式碼,以取得您知道永遠不會執行的程式碼路徑。例如,套件可能包含僅在 node 中執行的程式碼,但您只會在瀏覽器中使用該套件。它還可用於在執行時從無法打包的套件中匯入 node 中的程式碼。例如,fsevents 套件包含一個原生擴充功能,而 esbuild 不支援。將某個東西標記為外部如下所示

CLI JS Go
echo 'require("fsevents")' > app.js
esbuild app.js --bundle --external:fsevents --platform=node
// app.js
require("fsevents");
import * as esbuild from 'esbuild'
import fs from 'node:fs'

fs.writeFileSync('app.js', 'require("fsevents")')

await esbuild.build({
  entryPoints: ['app.js'],
  outfile: 'out.js',
  bundle: true,
  platform: 'node',
  external: ['fsevents'],
})
package main

import "io/ioutil"
import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  ioutil.WriteFile("app.js", []byte("require(\"fsevents\")"), 0644)

  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Outfile:     "out.js",
    Bundle:      true,
    Write:       true,
    Platform:    api.PlatformNode,
    External:    []string{"fsevents"},
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

您還可以在外部路徑中使用 * 萬用字元,將與該模式相符的所有檔案標記為外部。例如,您可以使用 *.png 移除所有 .png 檔案,或使用 /images/* 移除所有從 /images/ 開始的路徑

CLI JS Go
esbuild app.js --bundle "--external:*.png" "--external:/images/*"
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  outfile: 'out.js',
  bundle: true,
  external: ['*.png', '/images/*'],
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Outfile:     "out.js",
    Bundle:      true,
    Write:       true,
    External:    []string{"*.png", "/images/*"},
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

外部路徑會在路徑解析前後套用,讓您可以比對原始碼中的匯入路徑和絕對檔案系統路徑。如果外部路徑在任一情況下都符合,則該路徑會被視為外部。具體行為如下

主要欄位

支援:建立

當您在 node 中匯入套件時,該套件 package.json 檔案中的 main 欄位會決定要匯入哪個檔案(以及 許多其他規則)。包含 esbuild 在內的主要 JavaScript 捆綁器讓您可以在解析套件時指定其他要嘗試的 package.json 欄位。至少有三個此類欄位普遍使用

預設主欄位取決於目前的 平台 設定。這些預設值應與現有的套件生態系統最相容。但如果你願意,可以像這樣自訂它們

CLI JS Go
esbuild app.js --bundle --main-fields=module,main
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  mainFields: ['module', 'main'],
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    MainFields:  []string{"module", "main"},
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

套件作者

如果你想要撰寫一個套件,在其中結合使用 browser 欄位和 module 欄位,那麼你可能想要填寫完整 CommonJS 與 ESM 和瀏覽器與節點相容性矩陣中的所有四個項目。為此,你需要使用 browser 欄位的擴充形式,它是一個映射,而不仅仅是一個字串

{
  "main": "./node-cjs.js",
  "module": "./node-esm.js",
  "browser": {
    "./node-cjs.js": "./browser-cjs.js",
    "./node-esm.js": "./browser-esm.js"
  }
}

預期 main 欄位是 CommonJS,而 module 欄位預期是 ESM。關於要使用哪個模組格式的決定,與是否使用瀏覽器特定變體或節點特定變體的決定無關。如果你省略這四個項目中的任何一個,則有風險會選擇錯誤的變體。例如,如果你省略 CommonJS 瀏覽器建置的項目,則可能會選擇 CommonJS 節點建置。

請注意,使用 mainmodulebrowser 是舊方法。還有一個較新的方法可以執行此操作,你可能更喜歡使用它:package.json 中的 exports 欄位。它提供了不同的權衡。例如,它讓你對套件中所有子路徑的匯入有更精確的控制(而 main 欄位只讓你控制進入點),但它可能會導致你的套件根據你設定的方式被匯入多次。

節點路徑

支援:建立

節點的模組解析演算法支援一個名為 NODE_PATH 的環境變數,其中包含解析匯入路徑時要使用的全域目錄清單。除了所有父目錄中的 node_modules 目錄之外,還會在這些路徑中搜尋套件。你可以使用 CLI 中的環境變數和 JS 和 Go API 中的陣列,將此目錄清單傳遞給 esbuild

CLI JS Go
NODE_PATH=someDir esbuild app.js --bundle --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  nodePaths: ['someDir'],
  entryPoints: ['app.js'],
  bundle: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    NodePaths:   []string{"someDir"},
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Outfile:     "out.js",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

如果你正在使用 CLI,並且想要使用 NODE_PATH 傳遞多個目錄,則必須在 Unix 上使用 : 分隔它們,在 Windows 上使用 ; 分隔它們。這是節點本身使用的相同格式。

套件

支援:建立

使用此設定可將所有套件的相依性從套件中排除。這在 為 Node.js 套件 時很有用,因為許多 npm 套件使用 esbuild 在套件時不支援的 Node.js 特定功能(例如 __dirnameimport.meta.urlfs.readFileSync*.node 原生二進制模組)。使用方式如下

CLI JS Go
esbuild app.js --bundle --packages=external
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  packages: 'external',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Packages:    api.PackagesExternal,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

啟用此設定會自動將看起來像 npm 套件的所有匯入路徑(即不以 ... 路徑元件開頭且不是絕對路徑)標記為外部。它與手動將每個相依性傳遞給 external 的效果相同,但更簡潔。如果您想要自訂哪些相依性是外部的,哪些不是,則應使用 external,而不是此設定。

請注意,此設定僅在啟用 套件 時才有作用。此外,請注意,在匯入路徑被任何已設定的 別名 改寫後,才會將匯入路徑標記為外部,因此當使用此設定時,別名功能仍然有效。

支援:建立

此設定反映 Node.js 中的 --preserve-symlinks 設定。如果您使用該設定(或 Webpack 中類似的 resolve.symlinks 設定),您可能也需要在 esbuild 中啟用此設定。可以這樣啟用

CLI JS Go
esbuild app.js --bundle --preserve-symlinks --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  preserveSymlinks: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:      []string{"app.js"},
    Bundle:           true,
    PreserveSymlinks: true,
    Outfile:          "out.js",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

啟用此設定會導致 esbuild 透過原始檔案路徑(即未追蹤符號連結的路徑)而不是實際檔案路徑(即追蹤符號連結後的路徑)來確定檔案身分。這在某些目錄結構中可能很有用。請記住,這表示如果有多個符號連結指向一個檔案,則該檔案可能會被賦予多個身分,這可能會導致它在產生的輸出檔案中出現多次。

注意:「符號連結」一詞是指 符號連結,是指檔案系統中路徑可以重新導向到另一個路徑的功能。

解析副檔名

支援:建立

node 使用的 解析演算法 支援隱含的檔案副檔名。您可以使用 require('./file'),它會依序檢查 ./file./file.js./file.json./file.node。包含 esbuild 在內的現代套件管理工具也將此概念延伸到其他檔案類型。esbuild 中隱含檔案副檔名的完整順序可以使用 resolve extensions 設定進行自訂,其預設值為 .tsx,.ts,.jsx,.js,.css,.json

CLI JS Go
esbuild app.js --bundle --resolve-extensions=.ts,.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  resolveExtensions: ['.ts', '.js'],
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:       []string{"app.js"},
    Bundle:            true,
    ResolveExtensions: []string{".ts", ".js"},
    Write:             true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

請注意,esbuild 故意不將新的 .mjs.cjs 副檔名包含在此清單中。Node 的解析演算法不會將這些副檔名視為隱含檔案副檔名,因此 esbuild 也不會。如果您想匯入具有這些副檔名的檔案,您應該在匯入路徑中明確加入副檔名,或變更此設定以包含您想要隱含的其他副檔名。

工作目錄

支援:建立

此 API 選項讓您可以指定要使用於建置的工作目錄。它通常預設為您用來呼叫 esbuild API 的程序的目前 工作目錄。esbuild 會將工作目錄用於多項不同的用途,包括將作為 API 選項提供的相對路徑解析為絕對路徑,以及在記錄訊息中將絕對路徑美化列印為相對路徑。以下是自訂 esbuild 工作目錄的方法

CLI JS Go
cd "/var/tmp/custom/working/directory"
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['file.js'],
  absWorkingDir: '/var/tmp/custom/working/directory',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:   []string{"file.js"},
    AbsWorkingDir: "/var/tmp/custom/working/directory",
    Outfile:       "out.js",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

注意:如果您正在使用 Yarn Plug'n'Play,請記住此工作目錄用於搜尋 Yarn 的清單檔案。如果您從不相關的目錄執行 esbuild,您必須將此工作目錄設定為包含清單檔案的目錄(或其子目錄),才能讓 esbuild 找到清單檔案。

轉換

JSX

支援:建置轉換

此選項會告訴 esbuild 如何處理 JSX 語法。以下是可用的選項

以下是如何將 JSX 轉換設定為 preserve 的範例

CLI JS Go
echo '<div/>' | esbuild --jsx=preserve --loader=jsx
<div />;
import * as esbuild from 'esbuild'

let result = await esbuild.transform('<div/>', {
  jsx: 'preserve',
  loader: 'jsx',
})

console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  result := api.Transform("<div/>", api.TransformOptions{
    JSX:    api.JSXPreserve,
    Loader: api.LoaderJSX,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

JSX dev

支援:建置轉換

如果 JSX 轉換已設定為 automatic,啟用此設定會導致 esbuild 自動將檔案名稱和來源位置注入到每個 JSX 元素中。您的 JSX 函式庫可以使用此資訊來協助除錯。如果 JSX 轉換已設定為 automatic 以外的設定,此設定不會執行任何動作。以下是如何啟用此設定的範例

CLI JS Go
echo '<a/>' | esbuild --loader=jsx --jsx=automatic
import { jsx } from "react/jsx-runtime";
/* @__PURE__ */ jsx("a", {});
echo '<a/>' | esbuild --loader=jsx --jsx=automatic --jsx-dev
import { jsxDEV } from "react/jsx-dev-runtime";
/* @__PURE__ */ jsxDEV("a", {}, void 0, false, {
  fileName: "<stdin>",
  lineNumber: 1,
  columnNumber: 1
}, this);
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.jsx'],
  jsxDev: true,
  jsx: 'automatic',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.jsx"},
    JSXDev:      true,
    JSX:         api.JSXAutomatic,
    Outfile:     "out.js",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

JSX 工廠

支援:建置轉換

這會設定針對每個 JSX 元素呼叫的函式。一般而言,此類 JSX 表達式

<div>Example text</div>

會編譯成類似這樣呼叫 React.createElement 的函式

React.createElement("div", null, "Example text");

您可以透過變更 JSX 工廠來呼叫 React.createElement 以外的函式。例如,改為呼叫函式 h(由其他函式庫使用,例如 Preact

CLI JS Go
echo '<div/>' | esbuild --jsx-factory=h --loader=jsx
/* @__PURE__ */ h("div", null);
import * as esbuild from 'esbuild'

let result = await esbuild.transform('<div/>', {
  jsxFactory: 'h',
  loader: 'jsx',
})

console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  result := api.Transform("<div/>", api.TransformOptions{
    JSXFactory: "h",
    Loader:     api.LoaderJSX,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

或者,如果您使用 TypeScript,您可以透過將以下內容新增到 tsconfig.json 檔案中來設定 TypeScript 的 JSX,esbuild 應該會自動擷取它,而不需要設定

{
  "compilerOptions": {
    "jsxFactory": "h"
  }
}

如果您要根據每個檔案設定此設定,您可以使用 // @jsx h 註解。請注意,當 JSX 轉換已設定為 automatic 時,此設定不適用。

JSX 片段

支援:建置轉換

這會設定呼叫每個 JSX 片段的函式。一般來說,像這樣的 JSX 片段表達式

<>Stuff</>

會編譯成使用 React.Fragment 元件,如下所示

React.createElement(React.Fragment, null, "Stuff");

您可以透過變更 JSX 片段來使用 React.Fragment 以外的元件。例如,改用 Fragment 元件(其他函式庫使用,例如 Preact

CLI JS Go
echo '<>x</>' | esbuild --jsx-fragment=Fragment --loader=jsx
/* @__PURE__ */ React.createElement(Fragment, null, "x");
import * as esbuild from 'esbuild'

let result = await esbuild.transform('<>x</>', {
  jsxFragment: 'Fragment',
  loader: 'jsx',
})

console.log(result.code)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  result := api.Transform("<>x</>", api.TransformOptions{
    JSXFragment: "Fragment",
    Loader:      api.LoaderJSX,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

或者,如果您使用 TypeScript,您可以透過將以下內容新增到 tsconfig.json 檔案中來設定 TypeScript 的 JSX,esbuild 應該會自動擷取它,而不需要設定

{
  "compilerOptions": {
    "jsxFragmentFactory": "Fragment"
  }
}

如果您想針對每個檔案設定這個設定,您可以使用 // @jsxFrag Fragment 註解來執行。請注意,當 JSX 轉換設定為 automatic 時,這個設定不會套用。

JSX 匯入來源

支援:建置轉換

如果 JSX 轉換已設定為 automatic,設定這個設定可讓您變更 esbuild 用來自動匯入其 JSX 輔助函式的函式庫。請注意,這只適用於 特定於 React 17+ 的 JSX 轉換。如果您將 JSX 匯入來源設定為 your-pkg,則該套件必須至少公開下列匯出

import { createElement } from "your-pkg"
import { Fragment, jsx, jsxs } from "your-pkg/jsx-runtime"
import { Fragment, jsxDEV } from "your-pkg/jsx-dev-runtime"

/jsx-runtime/jsx-dev-runtime 子路徑是根據設計硬編碼,無法變更。jsxjsxs 匯入會在 JSX 開發模式 關閉時使用,而 jsxDEV 匯入會在 JSX 開發模式開啟時使用。這些的意義在 React 有關其新 JSX 轉換的說明文件 中有說明。createElement 匯入會在元素有屬性散佈接著是 key 屬性時使用,如下所示

return <div {...props} key={key} />

以下是將 JSX 匯入來源設定為 preact 的範例

CLI JS Go
esbuild app.jsx --jsx-import-source=preact --jsx=automatic
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.jsx'],
  jsxImportSource: 'preact',
  jsx: 'automatic',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:     []string{"app.jsx"},
    JSXImportSource: "preact",
    JSX:             api.JSXAutomatic,
    Outfile:         "out.js",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

或者,如果您使用 TypeScript,您可以透過將這個內容新增到 tsconfig.json 檔案中來設定 TypeScript 的 JSX 匯入來源,而 esbuild 應該會自動選取它,無需設定

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact"
  }
}

如果您想針對每個檔案控制這個設定,您可以使用每個檔案中的 // @jsxImportSource your-pkg 註解來執行。您可能也需要新增 // @jsxRuntime automatic 註解,如果 JSX 轉換尚未透過其他方式設定,或者您也想要針對每個檔案設定它。

JSX 副作用

支援:建置轉換

預設情況下,esbuild 會假設 JSX 表達式沒有副作用,這表示它們會加上 /* @__PURE__ */ 註解,並在未使用的狀況下於打包時移除。這遵循了 JSX 用於虛擬 DOM 的常見用法,並適用於絕大多數 JSX 函式庫。不過,有些人撰寫了沒有這個屬性的 JSX 函式庫(特別是 JSX 表達式可能會有任意的副作用,且無法在未使用的狀況下移除)。如果你使用的是此類函式庫,你可以使用這個設定告訴 esbuild,JSX 表達式有副作用

CLI JS Go
esbuild app.jsx --jsx-side-effects
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.jsx'],
  outfile: 'out.js',
  jsxSideEffects: true,
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:    []string{"app.jsx"},
    Outfile:        "out.js",
    JSXSideEffects: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

支援

支援:建置轉換

這個設定讓你可以在個別語法功能層級自訂 esbuild 不支援的語法功能集。例如,你可以使用這個設定告訴 esbuild,BigInt 不受支援,這樣當你嘗試使用 BigInt 時,esbuild 會產生錯誤。通常,當你使用 target 設定時,會為你設定這個設定,你通常應該使用這個設定,而不是這個設定。如果除了這個設定之外,還指定了目標,這個設定會覆寫目標所指定的任何設定。

以下是說明為什麼你可能想要使用這個設定,而不是設定目標,或除了設定目標之外,還使用這個設定的一些範例

如果你希望 esbuild 將某個語法功能視為不受支援,你可以這樣指定

CLI JS Go
esbuild app.js --supported:bigint=false
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  supported: {
    'bigint': false,
  },
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Supported: map[string]bool{
      "bigint": false,
    },
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

語法功能使用 esbuild 特有的功能名稱指定。完整的 feature names 如下

JavaScript

CSS

目標

支援:建置轉換

這會設定產生 JavaScript 和/或 CSS 程式碼的目標環境。它會告訴 esbuild 將對這些環境來說太新的 JavaScript 語法轉換成舊版的 JavaScript 語法,讓這些環境可以使用。例如,?? 這個運算子是在 Chrome 80 中推出的,因此 esbuild 在目標為 Chrome 79 或更早版本時,會將它轉換成等效的(但更冗長的)條件式運算式。

請注意,這僅與語法功能有關,不與 API 有關。它不會自動為這些環境未使用的新的 API 新增 多重載入。您必須明確匯入您需要的 API 的多重載入(例如,透過匯入 core-js)。自動多重載入注入不在 esbuild 的範圍內。

每個目標環境都是環境名稱,後接版本號碼。目前支援下列環境名稱

此外,您也可以指定 JavaScript 語言版本,例如 es2020。預設目標是 esnext,這表示預設情況下,esbuild 會假設支援所有最新的 JavaScript 和 CSS 功能。以下是設定多個目標環境的範例。您不需要指定所有目標環境;您只要指定專案關心的目標環境子集即可。您也可以更精確地指定版本號碼(例如,node12.19.0,而非僅 node12

CLI JS Go
esbuild app.js --target=es2020,chrome58,edge16,firefox57,node12,safari11
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  target: [
    'es2020',
    'chrome58',
    'edge16',
    'firefox57',
    'node12',
    'safari11',
  ],
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Target:      api.ES2020,
    Engines: []api.Engine{
      {Name: api.EngineChrome, Version: "58"},
      {Name: api.EngineEdge, Version: "16"},
      {Name: api.EngineFirefox, Version: "57"},
      {Name: api.EngineNode, Version: "12"},
      {Name: api.EngineSafari, Version: "11"},
    },
    Write: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

您可以參閱 JavaScript 載入器,以了解哪些語法功能與哪些語言版本一起推出。請記住,儘管 JavaScript 語言版本(例如 es2020)會以年份來識別,但那是規範通過的年份。這與所有主要瀏覽器實作該規範的年份無關,後者通常會早於或晚於該年份。

如果您使用 esbuild 尚未支援轉換為您目前的語言目標的語法功能,esbuild 會在使用不支援的語法的地方產生錯誤。例如,當目標是 es5 語言版本時,通常會發生這種情況,因為 esbuild 僅支援將大多數較新的 JavaScript 語法功能轉換為 es6

如果您需要自訂支援的語法功能集,針對個別功能層級,除了或取代 target 提供的內容,您可以使用 supported 設定來執行此操作。

最佳化

定義

支援:建置轉換

此功能提供一種方式,可以用常數表達式取代全域識別碼。這是一種在不變更程式碼本身的情況下,在不同建置之間變更部分程式碼行為的方式

CLI JS Go
echo 'hooks = DEBUG && require("hooks")' | esbuild --define:DEBUG=true
hooks = require("hooks");
echo 'hooks = DEBUG && require("hooks")' | esbuild --define:DEBUG=false
hooks = false;
import * as esbuild from 'esbuild'let js = 'hooks = DEBUG && require("hooks")'(await esbuild.transform(js, {
  define: { DEBUG: 'true' },
})).code
'hooks = require("hooks");\n'
(await esbuild.transform(js, {
  define: { DEBUG: 'false' },
})).code
'hooks = false;\n'
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "hooks = DEBUG && require('hooks')"

  result1 := api.Transform(js, api.TransformOptions{
    Define: map[string]string{"DEBUG": "true"},
  })

  if len(result1.Errors) == 0 {
    fmt.Printf("%s", result1.Code)
  }

  result2 := api.Transform(js, api.TransformOptions{
    Define: map[string]string{"DEBUG": "false"},
  })

  if len(result2.Errors) == 0 {
    fmt.Printf("%s", result2.Code)
  }
}

每個 define 項目將識別碼對應到包含表達式的程式碼字串。字串中的表達式必須為 JSON 物件(null、布林值、數字、字串、陣列或物件)或單一識別碼。陣列和物件以外的替換表達式會內嵌替換,表示它們可以參與常數摺疊。陣列和物件替換表達式會儲存在變數中,然後使用識別碼來參照,而不是內嵌替換,這樣可以避免替換重複的值,但表示這些值不會參與常數摺疊。

如果您想用字串常數替換某個項目,請記住傳遞給 esbuild 的替換值本身必須包含引號,因為每個 define 項目都對應到包含程式碼的字串。省略引號表示替換值是識別碼。以下範例會加以說明

CLI JS Go
echo 'id, str' | esbuild --define:id=text --define:str=\"text\"
text, "text";
import * as esbuild from 'esbuild'(await esbuild.transform('id, str', {
  define: { id: 'text', str: '"text"' },
})).code
'text, "text";\n'
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  result := api.Transform("id, text", api.TransformOptions{
    Define: map[string]string{
      "id":  "text",
      "str": "\"text\"",
    },
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

如果您使用 CLI,請記住不同的殼層有不同的規則來跳脫雙引號字元(當替換值是字串時需要)。請使用 \" 反斜線跳脫,因為它在 bash 和 Windows 命令提示字元中都能運作。其他在 bash 中運作的雙引號跳脫方法,例如用單引號將其包圍,在 Windows 中將無法運作,因為 Windows 命令提示字元不會移除單引號。這與從 package.json 檔案中的 npm 腳本使用 CLI 有關,人們會希望它能在所有平台上運作

{
  "scripts": {
    "build": "esbuild --define:process.env.NODE_ENV=\\\"production\\\" app.js"
  }
}

如果您在使用不同的殼層時仍遇到跨平台引號跳脫問題,您可能會想要改用 JavaScript API。您可以在其中使用正規的 JavaScript 語法來消除跨平台差異。

如果您正在尋找更進階的 define 功能,可以將表達式替換為非常數的項目(例如,將全域變數替換為 shim),您可能會可以使用類似的 inject 功能來執行此操作。

刪除

支援:建置轉換

這會指示 esbuild 在建置前編輯您的原始碼,以刪除某些建構。目前有兩項可能被刪除的項目

CLI JS Go
esbuild app.js --drop:debugger
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  drop: ['debugger'],
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Drop:        api.DropDebugger,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}
CLI JS Go
esbuild app.js --drop:console
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  drop: ['console'],
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Drop:        api.DropConsole,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

移除標籤

支援:建置轉換

這會指示 esbuild 在建置之前編輯您的原始程式碼,以移除具有特定標籤名稱的 標籤陳述式。例如,考慮以下程式碼

function example() {
  DEV: doAnExpensiveCheck()
  return normalCodePath()
}

如果您使用此選項移除所有名為 DEV 的標籤,則 esbuild 會提供以下結果

function example() {
  return normalCodePath();
}

您可以像這樣設定此功能(這將移除 DEVTEST 標籤)

CLI JS Go
esbuild app.js --drop-labels=DEV,TEST
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  dropLabels: ['DEV', 'TEST'],
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    DropLabels:  []string{"DEV", "TEST"},
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

請注意,這不是移除程式碼的唯一方法。另一種更常見的方法是使用 定義 功能,以布林值取代特定的全域變數。例如,考慮以下程式碼

function example() {
  DEV && doAnExpensiveCheck()
  return normalCodePath()
}

如果您將 DEV 定義為 false,則 esbuild 會提供以下結果

function example() {
  return normalCodePath();
}

這與使用標籤幾乎相同。然而,使用標籤而不是全域變數來有條件地移除程式碼的優點是,您不必擔心全域變數未定義,因為有人忘記設定 esbuild 以將其替換為其他東西。使用標籤方法的一些缺點是,當標籤移除時,有條件地移除程式碼會變得難以閱讀,而且它不適用於嵌入在巢狀表達式中的程式碼。對於特定專案使用哪種方法取決於個人偏好。

忽略註解

支援:建置轉換

由於 JavaScript 是一種動態語言,編譯器有時很難辨識未使用的程式碼,因此社群開發了特定註解,以協助編譯器了解哪些程式碼應視為沒有副作用,且可以移除。目前 esbuild 支援兩種形式的副作用註解

這些註解可能會造成問題,因為編譯器完全依賴開發人員的準確性,而開發人員偶爾會發佈註解不正確的套件。對於開發人員來說,sideEffects 欄位特別容易出錯,因為預設情況下,如果沒有使用任何匯入,則會將套件中的所有檔案視為無效程式碼。如果您新增一個包含副作用的新檔案,卻忘記更新該欄位,則當人們嘗試將其打包時,您的套件可能會中斷。

這就是為什麼 esbuild 包含忽略副作用註解的方法。只有在遇到問題時,才應啟用此功能,因為必要的程式碼意外從套件中移除,導致套件中斷

CLI JS Go
esbuild app.js --bundle --ignore-annotations
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  ignoreAnnotations: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:       []string{"app.js"},
    Bundle:            true,
    IgnoreAnnotations: true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

啟用此功能表示 esbuild 將不再遵循 /* @__PURE__ */ 註解或 sideEffects 欄位。不過,它仍會自動 tree shaking 未使用的匯入,因為這不依賴開發人員的註解。理想情況下,此標記僅為暫時解決方案。您應將這些問題回報給套件維護者,以進行修復,因為它們表示套件有問題,並且也可能會讓其他人絆倒。

注入

支援:建立

此選項允許您自動使用來自另一個檔案的匯入取代全域變數。這對於將您無法控制的程式碼調整到新環境來說,可能是一個有用的工具。例如,假設您有一個名為 process-cwd-shim.js 的檔案,它使用匯出名稱 process.cwd 匯出一個 shim

// process-cwd-shim.js
let processCwdShim = () => ''
export { processCwdShim as 'process.cwd' }
// entry.js
console.log(process.cwd())

這旨在取代對節點 process.cwd() 函式的使用,以防止呼叫它的套件在瀏覽器中執行時發生崩潰。您可以使用注入功能,將對全域屬性 process.cwd 的所有參照替換為從該檔案匯入的內容

CLI JS Go
esbuild entry.js --inject:./process-cwd-shim.js --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['entry.js'],
  inject: ['./process-cwd-shim.js'],
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"entry.js"},
    Inject:      []string{"./process-cwd-shim.js"},
    Outfile:     "out.js",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

結果類似這樣

// out.js
var processCwdShim = () => "";
console.log(processCwdShim());

你可以將注入功能視為類似於 定義 功能,但它會用匯入檔案來取代表達式,而不是用常數,且要取代的表達式會使用檔案中的匯出名稱來指定,而不是使用 esbuild API 中的內嵌字串。

自動匯入 JSX

React(最初建立 JSX 語法的函式庫)有一種稱為 automatic 的模式,在這種模式下,你不需要 import 任何東西就能使用 JSX 語法。JSX 轉 JS 轉譯器會自動為你匯入正確的 JSX 工廠函式。你可以使用 esbuild 的 jsx 設定來啟用 automatic JSX 模式。如果你想要自動匯入 JSX,且你正在使用夠新的 React 版本,那麼你應該使用 automatic JSX 模式。

然而,將 jsx 設定為 automatic 不幸地也表示你正在使用高度 React 特定的 JSX 轉譯,而不是預設的通用 JSX 轉譯。這表示撰寫 JSX 工廠函式會更複雜,也表示 automatic 模式無法與預期與標準 JSX 轉譯一起使用的函式庫搭配使用(包括舊版本的 React)。

當 JSX 轉譯未設定為 automatic 時,你可以使用 esbuild 的注入功能來自動匯入 JSX 表達式的 工廠片段。以下是可注入來執行此動作的範例檔案

const { createElement, Fragment } = require('react')
export {
  createElement as 'React.createElement',
  Fragment as 'React.Fragment',
}

此程式碼使用 React 函式庫作為範例,但你也可以使用此方法搭配其他 JSX 函式庫,只要進行適當的變更即可。

注入沒有匯入的檔案

你也可以將此功能與沒有匯出的檔案搭配使用。在這種情況下,注入的檔案會出現在其他輸出之前,就好像每個輸入檔案都包含 import "./file.js" 一樣。由於 ECMAScript 模組的工作方式,此注入仍然是「衛生的」,也就是說,不同檔案中具有相同名稱的符號會重新命名,因此它們不會彼此衝突。

有條件地注入檔案

如果你想要有條件地僅在實際使用匯出時匯入檔案,你應該將注入的檔案標記為沒有副作用,方法是將它放入套件中,並在該套件的 package.json 檔案中加入 "sideEffects": false。此設定是 Webpack 的慣例,esbuild 尊重任何匯入的檔案,而不僅是與注入一起使用的檔案。

保留名稱

支援:建置轉換

在 JavaScript 中,函式和類別上的 name 屬性預設為原始碼中鄰近的識別碼。這些語法形式全部將函式的 name 屬性設定為 "fn"

function fn() {}
let fn = function() {};
fn = function() {};
let [fn = function() {}] = [];
let {fn = function() {}} = {};
[fn = function() {}] = [];
({fn = function() {}} = {});

然而,縮小會重新命名符號以縮小程式碼大小,而套件打包有時需要重新命名符號以避免衝突。這會變更許多此類情況的 name 屬性值。這通常沒問題,因為 name 屬性通常只用於偵錯。然而,有些架構依賴 name 屬性進行註冊和繫結。如果是這種情況,你可以啟用此選項,以保留縮小程式碼中的原始 name

CLI JS Go
esbuild app.js --minify --keep-names
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  minify: true,
  keepNames: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:       []string{"app.js"},
    MinifyWhitespace:  true,
    MinifyIdentifiers: true,
    MinifySyntax:      true,
    KeepNames:         true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

請注意,如果已將目標設定為不允許 esbuild 變更函式和類別上 name 屬性的舊環境,則此功能將不可用。這是因為這些環境不支援 ES6。

混淆屬性

支援:建置轉換

使用此功能可能會以微妙的方式損壞你的程式碼。除非你知道自己在做什麼,而且確切知道它將如何影響你的程式碼和所有依賴項,否則請勿使用此功能。

此設定讓你傳遞正規表示式給 esbuild,以告知 esbuild 自動重新命名所有符合此正規表示式的屬性。當你想要縮小程式碼中的某些屬性名稱以縮小產生的程式碼大小,或稍微混淆程式碼意圖時,這很有用。

以下是一個使用正規表示式 _$ 來混淆所有以底線結尾的屬性的範例,例如 foo_。這會將 print({ foo_: 0 }.foo_) 混淆成 print({ a: 0 }.a)

CLI JS Go
esbuild app.js --mangle-props=_$
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  mangleProps: /_$/,
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    MangleProps: "_$",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

只混淆以底線結尾的屬性是一個合理的啟發法,因為一般的 JS 程式碼通常不包含此類識別碼。瀏覽器 API 也不使用此命名慣例,因此這也能避免與瀏覽器 API 發生衝突。如果你想要避免混淆例如 __defineGetter__ 等名稱,你可以考慮使用更複雜的正規表示式,例如 [^_]_$(即必須以非底線結尾,後接底線)。

這是一個獨立的設定,而不是 縮小 設定的一部分,因為它是一個不安全的轉換,無法用於任意的 JavaScript 程式碼。它僅在所提供的正規表示式與您想要混淆的所有屬性相符,且與您不想要混淆的任何屬性都不相符時才有效。它也僅在您在任何情況下都不間接地參照混淆的屬性時才有效。例如,這表示您不能使用 obj[prop] 來參照屬性,其中 prop 是包含屬性名稱的字串。特別是,以下語法結構是唯一符合屬性混淆條件的結構

語法 範例
點屬性存取 x.foo_
點選用鏈 x?.foo_
物件屬性 x = { foo_: y }
物件方法 x = { foo_() {} }
類別欄位 class x { foo_ = y }
類別方法 class x { foo_() {} }
物件解構繫結 let { foo_: x } = y
物件解構指定 ({ foo_: x } = y)
JSX 元素成員表達式 <X.foo_></X.foo_>
JSX 屬性名稱 <X foo_={y} />
TypeScript 命名空間匯出 namespace x { export let foo_ = y }
TypeScript 參數屬性 class x { constructor(public foo_) {} }

使用此功能時,請記住屬性名稱僅在單一的 esbuild API 呼叫中持續混淆,但不會跨越 esbuild API 呼叫。每個 esbuild API 呼叫都會執行獨立的屬性混淆操作,因此由兩個不同的 API 呼叫產生的輸出檔案可能會將同一個屬性混淆成兩個不同的名稱,這可能會導致產生的程式碼行為不正確。

帶引號的屬性

預設情況下,esbuild 不會修改字串文字的內容。這表示您可以透過將個別屬性加上引號作為字串的方式,來避免屬性混淆。但是,您必須在所有地方持續對給定的屬性使用引號或不使用引號,才能讓此方法有效。例如,print({ foo_: 0 }.foo_) 會被混淆成 print({ a: 0 }.a),而 print({ 'foo_': 0 }['foo_']) 則不會被混淆。

如果您希望 esbuild 也混淆字串文字的內容,您可以明確啟用此行為,如下所示

CLI JS Go
esbuild app.js --mangle-props=_$ --mangle-quoted
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  mangleProps: /_$/,
  mangleQuoted: true,
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:  []string{"app.js"},
    MangleProps:  "_$",
    MangleQuoted: api.MangleQuotedTrue,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

啟用此功能後,以下語法結構也符合屬性混淆的條件

語法 範例
帶引號的屬性存取 x['foo_']
帶引號的選用鏈 x?.['foo_']
引號物件屬性 x = { 'foo_': y }
引號物件方法 x = { 'foo_'() {} }
引號類別欄位 class x { 'foo_' = y }
引號類別方法 class x { 'foo_'() {} }
引號物件解構繫結 let { 'foo_': x } = y
引號物件解構指派 ({ 'foo_': x } = y)
字串文字在 in 的左側 'foo_' in x

混淆其他字串

混淆 引號屬性 仍然只會混淆屬性名稱位置的字串。有時你可能還需要混淆程式碼中其他任意位置的字串中的屬性名稱。為此,你可以使用 /* @__KEY__ */ 註解作為字串的前綴,告訴 esbuild 應將字串的內容視為可以混淆的屬性名稱。例如

let obj = {}
Object.defineProperty(
  obj,
  /* @__KEY__ */ 'foo_',
  { get: () => 123 },
)
console.log(obj.foo_)

這將導致字串 'foo_' 的內容作為屬性名稱混淆(假設 屬性混淆 已啟用且 foo_ 符合重新命名的條件)。/* @__KEY__ */ 註解是 Terser 的慣例,Terser 是一款具備類似屬性混淆功能的熱門 JavaScript 壓縮器。

防止重新命名

如果你想將某些屬性排除在混淆之外,可以使用其他設定保留它們。例如,這使用正規表示式 ^__.*__$ 保留所有以兩個底線開頭和結尾的屬性,例如 __foo__

CLI JS Go
esbuild app.js --mangle-props=_$ "--reserve-props=^__.*__$"
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  mangleProps: /_$/,
  reserveProps: /^__.*__$/,
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:  []string{"app.js"},
    MangleProps:  "_$",
    ReserveProps: "^__.*__$",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

保留重新命名決策

屬性混淆功能的高階用法包括將從原始名稱到混淆名稱的對應儲存在永久快取中。啟用後,所有混淆的屬性重新命名都會在初始建置期間記錄在快取中。後續建置會重複使用儲存在快取中的重新命名,並為任何新增加的屬性新增其他重新命名。這會產生一些後果

例如,考慮以下輸入檔案

console.log({
  someProp_: 1,
  customRenaming_: 2,
  disabledRenaming_: 3
});

如果我們希望將 customRenaming_ 重新命名為 cR_,並且我們不希望重新命名 disabledRenaming_,我們可以將以下混淆快取 JSON 傳遞給 esbuild

{
  "customRenaming_": "cR_",
  "disabledRenaming_": false
}

混淆快取 JSON 可以像這樣傳遞給 esbuild

CLI JS Go
esbuild app.js --mangle-props=_$ --mangle-cache=cache.json
import * as esbuild from 'esbuild'

let result = await esbuild.build({
  entryPoints: ['app.js'],
  mangleProps: /_$/,
  mangleCache: {
    customRenaming_: "cR_",
    disabledRenaming_: false
  },
})

console.log('updated mangle cache:', result.mangleCache)
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    MangleProps: "_$",
    MangleCache: map[string]interface{}{
      "customRenaming_":   "cR_",
      "disabledRenaming_": false,
    },
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }

  fmt.Println("updated mangle cache:", result.MangleCache)
}

當啟用屬性命名時,將產生以下輸出檔案

console.log({
  a: 1,
  cR_: 2,
  disabledRenaming_: 3
});

以及以下更新的混淆快取

{
  "customRenaming_": "cR_",
  "disabledRenaming_": false,
  "someProp_": "a"
}

縮小

支援:建置轉換

啟用後,將縮小產生的程式碼,而不是以美化格式列印。縮小的程式碼通常等同於未縮小的程式碼,但較小,這表示下載速度較快,但較難偵錯。通常您會在生產環境中縮小程式碼,但在開發環境中不會。

在 esbuild 中啟用縮小如下所示

CLI JS Go
echo 'fn = obj => { return obj.x }' | esbuild --minify
fn=n=>n.x;
import * as esbuild from 'esbuild'var js = 'fn = obj => { return obj.x }'
(await esbuild.transform(js, {
  minify: true,
})).code
'fn=n=>n.x;\n'
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "fn = obj => { return obj.x }"

  result := api.Transform(js, api.TransformOptions{
    MinifyWhitespace:  true,
    MinifyIdentifiers: true,
    MinifySyntax:      true,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

此選項會結合執行三項不同的工作:移除空白、將您的語法改寫為更精簡,以及將區域變數重新命名為較短的名稱。通常您會想要執行所有這些工作,但必要時也可以個別啟用這些選項

CLI JS Go
echo 'fn = obj => { return obj.x }' | esbuild --minify-whitespace
fn=obj=>{return obj.x};
echo 'fn = obj => { return obj.x }' | esbuild --minify-identifiers
fn = (n) => {
  return n.x;
};
echo 'fn = obj => { return obj.x }' | esbuild --minify-syntax
fn = (obj) => obj.x;
import * as esbuild from 'esbuild'var js = 'fn = obj => { return obj.x }'
(await esbuild.transform(js, {
  minifyWhitespace: true,
})).code
'fn=obj=>{return obj.x};\n'
(await esbuild.transform(js, {
  minifyIdentifiers: true,
})).code
'fn = (n) => {\n  return n.x;\n};\n'
(await esbuild.transform(js, {
  minifySyntax: true,
})).code
'fn = (obj) => obj.x;\n'
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  css := "div { color: yellow }"

  result1 := api.Transform(css, api.TransformOptions{
    Loader:           api.LoaderCSS,
    MinifyWhitespace: true,
  })

  if len(result1.Errors) == 0 {
    fmt.Printf("%s", result1.Code)
  }

  result2 := api.Transform(css, api.TransformOptions{
    Loader:            api.LoaderCSS,
    MinifyIdentifiers: true,
  })

  if len(result2.Errors) == 0 {
    fmt.Printf("%s", result2.Code)
  }

  result3 := api.Transform(css, api.TransformOptions{
    Loader:       api.LoaderCSS,
    MinifySyntax: true,
  })

  if len(result3.Errors) == 0 {
    fmt.Printf("%s", result3.Code)
  }
}

這些相同的概念也適用於 CSS,而不僅僅是 JavaScript

CLI JS Go
echo 'div { color: yellow }' | esbuild --loader=css --minify
div{color:#ff0}
import * as esbuild from 'esbuild'var css = 'div { color: yellow }'
(await esbuild.transform(css, {
  loader: 'css',
  minify: true,
})).code
'div{color:#ff0}\n'
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  css := "div { color: yellow }"

  result := api.Transform(css, api.TransformOptions{
    Loader:            api.LoaderCSS,
    MinifyWhitespace:  true,
    MinifyIdentifiers: true,
    MinifySyntax:      true,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

esbuild 中的 JavaScript 縮小演算法通常會產生非常接近業界標準 JavaScript 縮小工具的縮小輸出大小的輸出。 此基準測試有一個範例比較不同縮小工具之間的輸出大小。儘管 esbuild 並非在所有情況下都是最佳的 JavaScript 縮小工具(也並未嘗試成為最佳),但它力求為大多數程式碼產生縮小輸出,大小在專用縮小工具的幾百分比之內,當然也比其他工具快得多。

注意事項

在使用 esbuild 作為縮小工具時,請記住以下事項

純粹

支援:建置轉換

各種 JavaScript 工具使用慣例,其中包含 /* @__PURE__ *//* #__PURE__ */ 的特殊註解,表示在 new 或呼叫表達式之前,如果結果值未使用,則可以移除該表達式。它看起來像這樣

let button = /* @__PURE__ */ React.createElement(Button, null);

此資訊會由 esbuild 等打包器在樹狀搖晃(又稱移除無用程式碼)期間使用,以在打包器無法自行證明移除是安全的(由於 JavaScript 程式碼的動態特性)時,精細地移除模組邊界之間未使用的匯入。

請注意,雖然註解寫著「純粹」,但令人困惑的是,它並未表示正在呼叫的函式是純粹的。例如,它並未表示可以快取重複呼叫該函式。該名稱基本上只是「如果未使用,則可以移除」的抽象簡寫。

某些表達式,例如 JSX 和某些內建全域變數,會在 esbuild 中自動註解為 /* @__PURE__ */。你也可以設定其他全域變數標記為 /* @__PURE__ */。例如,你可以將全域變數 document.createElement 函式標記為這樣,只要結果未使用,就可以在打包時自動從你的套件中移除。

值得一提的是,註解的效果僅延伸到呼叫本身,而不是引數。即使啟用了最小化,仍會保留具有副作用的引數

CLI JS Go
echo 'document.createElement(elemName())' | esbuild --pure:document.createElement
/* @__PURE__ */ document.createElement(elemName());
echo 'document.createElement(elemName())' | esbuild --pure:document.createElement --minify
elemName();
import * as esbuild from 'esbuild'let js = 'document.createElement(elemName())'
(await esbuild.transform(js, {
  pure: ['document.createElement'],
})).code
'/* @__PURE__ */ document.createElement(elemName());\n'
(await esbuild.transform(js, {
  pure: ['document.createElement'],
  minify: true,
})).code
'elemName();\n'
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "document.createElement(elemName())"

  result1 := api.Transform(js, api.TransformOptions{
    Pure: []string{"document.createElement"},
  })

  if len(result1.Errors) == 0 {
    fmt.Printf("%s", result1.Code)
  }

  result2 := api.Transform(js, api.TransformOptions{
    Pure:         []string{"document.createElement"},
    MinifySyntax: true,
  })

  if len(result2.Errors) == 0 {
    fmt.Printf("%s", result2.Code)
  }
}

請注意,如果你嘗試移除對 console API 方法(例如 console.log)的所有呼叫,並且還想要移除對具有副作用的引數的評估,則有一個特別案例可用於此:你可以使用 drop 功能,而不是將 console API 呼叫標記為純粹。但是,此機制特定於 console API,不適用於其他呼叫表達式。

樹狀搖晃

支援:建置轉換

樹狀搖晃是 JavaScript 社群用於移除無用程式碼的術語,這是一種常見的編譯器最佳化,可以自動移除無法到達的程式碼。在 esbuild 中,此術語特別指宣告層級的無用程式碼移除。

樹狀搖晃最容易透過範例說明。考慮以下檔案。有一個已使用的函式和一個未使用的函式

// input.js
function one() {
  console.log('one')
}
function two() {
  console.log('two')
}
one()

如果你使用 esbuild --bundle input.js --outfile=output.js 將此檔案打包,則未使用的函式將自動捨棄,留下以下輸出

// input.js
function one() {
  console.log("one");
}
one();

即使我們將函數拆分到一個獨立的函式庫檔案中,並使用 import 語句匯入它們,這也能運作

// lib.js
export function one() {
  console.log('one')
}
export function two() {
  console.log('two')
}
// input.js
import * as lib from './lib.js'
lib.one()

如果您使用 esbuild --bundle input.js --outfile=output.js 將此檔案打包,未使用的函數和未使用的匯入仍然會自動捨棄,讓您得到以下輸出

// lib.js
function one() {
  console.log("one");
}

// input.js
one();

透過這種方式,esbuild 只會打包您實際使用的套件部分,有時這能大幅節省大小。請注意,esbuild 的 tree shaking 實作仰賴 ECMAScript 模組 importexport 語句。它不適用於 CommonJS 模組。npm 上的許多套件都包含這兩種格式,而 esbuild 預設會嘗試選擇適用於 tree shaking 的格式。您可以使用 main 欄位 和/或 條件 選項自訂 esbuild 選擇的格式,視套件而定。

預設情況下,tree shaking 僅在啟用 打包 時或將輸出 格式 設定為 iife 時才會啟用,否則 tree shaking 會停用。您可以將其設定為 true 以強制啟用 tree shaking

CLI JS Go
esbuild app.js --tree-shaking=true
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  treeShaking: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    TreeShaking: api.TreeShakingTrue,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

您也可以將其設定為 false 以強制停用 tree shaking

CLI JS Go
esbuild app.js --tree-shaking=false
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  treeShaking: false,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    TreeShaking: api.TreeShakingFalse,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

Tree shaking 和副作用

用於 tree shaking 的副作用偵測是保守的,這表示 esbuild 僅在確定沒有隱藏的副作用時,才會將程式碼視為可移除的無用程式碼。例如,12.34"abcd" 等原始文字沒有副作用,可以移除,而 "ab" + cdfoo.bar 等表達式則有副作用(串接字串會呼叫 toString(),這可能會產生副作用,而成員存取可能會呼叫 getter,這也可能會產生副作用)。即使參照全域識別碼也視為副作用,因為如果沒有同名的全域變數,它會擲出 ReferenceError。以下是範例

// These are considered side-effect free
let a = 12.34;
let b = "abcd";
let c = { a: a };

// These are not considered side-effect free
// since they could cause some code to run
let x = "ab" + cd;
let y = foo.bar;
let z = { [x]: x };

有時,即使無法自動判斷某段程式碼沒有副作用,也希望允許程式碼被 tree shaken。這可以使用 pure 註解註解 來達成,它會告知 esbuild 信任程式碼作者,註解程式碼中沒有副作用。註解註解為 /* @__PURE__ */,且只能置於 new 或 call 表達式之前。您可以註解立即呼叫函式表達式,並在函式本體中放置任意副作用

// This is considered side-effect free due to
// the annotation, and will be removed if unused
let gammaTable = /* @__PURE__ */ (() => {
  // Side-effect detection is skipped in here
  let table = new Uint8Array(256);
  for (let i = 0; i < 256; i++)
    table[i] = Math.pow(i / 255, 2.2) * 255;
  return table;
})();

雖然 /* @__PURE__ */ 僅適用於 call 表達式的事實有時會使程式碼更冗長,但此語法的最大好處是它可以在 JavaScript 生態系統中的許多其他工具中移植,包括熱門的 UglifyJSTerser JavaScript 壓縮器(其他主要工具使用這些壓縮器,包括 WebpackParcel)。

請注意,註解會導致 esbuild 假設註解程式碼沒有副作用。如果註解錯誤,而程式碼實際上確實有重要的副作用,這些註解可能會導致程式碼損毀。如果您正在綑綁註解錯誤的第三方程式碼,您可能需要啟用 忽略註解,以確保綑綁程式碼正確無誤。

原始碼地圖

原始碼根目錄

支援:建置轉換

此功能僅在啟用 原始碼地圖 時才相關。它讓您設定原始碼地圖中 sourceRoot 欄位的數值,該欄位指定原始碼地圖中所有其他路徑的相對路徑。如果此欄位不存在,原始碼地圖中的所有路徑都會被解釋為相對於包含原始碼地圖的目錄。

您可以這樣設定 sourceRoot

CLI JS Go
esbuild app.js --sourcemap --source-root=https://raw.githubusercontent.com/some/repo/v1.2.3/
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  sourcemap: true,
  sourceRoot: 'https://raw.githubusercontent.com/some/repo/v1.2.3/',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Sourcemap:   api.SourceMapInline,
    SourceRoot:  "https://raw.githubusercontent.com/some/repo/v1.2.3/",
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

原始碼檔案

支援:建置轉換

此選項會在使用沒有檔名的輸入時設定檔名。這會在使用轉換 API 和在使用建置 API 與 stdin 時發生。設定的檔名會反映在錯誤訊息和原始碼地圖中。如果沒有設定,檔名預設為 <stdin>。它可以這樣設定

CLI JS Go
cat app.js | esbuild --sourcefile=example.js --sourcemap
import * as esbuild from 'esbuild'
import fs from 'node:fs'

let js = fs.readFileSync('app.js', 'utf8')
let result = await esbuild.transform(js, {
  sourcefile: 'example.js',
  sourcemap: 'inline',
})

console.log(result.code)
package main

import "fmt"
import "io/ioutil"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js, err := ioutil.ReadFile("app.js")
  if err != nil {
    panic(err)
  }

  result := api.Transform(string(js),
    api.TransformOptions{
      Sourcefile: "example.js",
      Sourcemap:  api.SourceMapInline,
    })

  if len(result.Errors) == 0 {
    fmt.Printf("%s %s", result.Code)
  }
}

原始碼地圖

支援:建置轉換

原始碼地圖可以讓您更容易除錯您的程式碼。它們編碼從已產生輸出檔中的行/欄位偏移量轉換回對應原始輸入檔中的行/欄位偏移量所需的資訊。如果您的已產生程式碼與您的原始程式碼有顯著不同(例如,您的原始程式碼是 TypeScript 或您啟用了 縮小),這會很有用。如果您偏好於在瀏覽器的開發人員工具中查看個別檔案,而不是一個大的綑綁檔案,這也會很有用。

請注意,JavaScript 和 CSS 都支援原始碼對應輸出,且兩者套用相同的選項。以下所有關於 .js 檔案的說明,也同樣適用於 .css 檔案。

原始碼對應產生有四種不同的模式

  1. 連結

    此模式表示原始碼對應會產生於 .js 輸出檔案旁的獨立 .js.map 輸出檔案中,且 .js 輸出檔案包含指向 .js.map 輸出檔案的特殊 //# sourceMappingURL= 註解。如此一來,當您開啟偵錯工具時,瀏覽器便會知道在哪裡找到特定檔案的原始碼對應。請使用下列方式使用 linked 原始碼對應模式

CLI JS Go
esbuild app.ts --sourcemap --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.ts'],
  sourcemap: true,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Sourcemap:   api.SourceMapLinked,
    Outfile:     "out.js",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}
  1. 外部

    此模式表示原始碼對應會產生於 .js 輸出檔案旁的獨立 .js.map 輸出檔案中,但與 linked 模式不同的是,.js 輸出檔案不包含 //# sourceMappingURL= 註解。請使用下列方式使用 external 原始碼對應模式

CLI JS Go
esbuild app.ts --sourcemap=external --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.ts'],
  sourcemap: 'external',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Sourcemap:   api.SourceMapExternal,
    Outfile:     "out.js",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}
  1. 內嵌

    此模式表示原始碼對應會附加在 .js 輸出檔案的結尾,作為 //# sourceMappingURL= 註解內的 base64 payload。不會產生其他 .js.map 輸出檔案。請記住,原始碼對應通常非常大,因為它們包含所有原始原始碼,因此您通常不希望傳送包含 inline 原始碼對應的程式碼。若要從原始碼對應中移除原始碼(僅保留檔案名稱和行/欄位對應),請使用 原始碼內容 選項。請使用下列方式使用 inline 原始碼對應模式

CLI JS Go
esbuild app.ts --sourcemap=inline --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.ts'],
  sourcemap: 'inline',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Sourcemap:   api.SourceMapInline,
    Outfile:     "out.js",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}
  1. both

    此模式結合了 inlineexternal。原始碼對應會以內嵌方式附加在 .js 輸出檔案的結尾,且相同原始碼對應的另一個副本會寫入 .js 輸出檔案旁的獨立 .js.map 輸出檔案中。請使用下列方式使用 both 原始碼對應模式

CLI JS Go
esbuild app.ts --sourcemap=both --outfile=out.js
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.ts'],
  sourcemap: 'both',
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.ts"},
    Sourcemap:   api.SourceMapInlineAndExternal,
    Outfile:     "out.js",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

上述 build API 支援所有四種原始碼對應模式,但 transform API 不支援 linked 模式。這是因為從 transform API 回傳的輸出沒有關聯的檔案名稱。如果您希望 transform API 的輸出具有原始碼對應註解,您可以自行附加一個。此外,transform API 的 CLI 形式僅支援 inline 模式,因為輸出會寫入 stdout,因此無法產生多個輸出檔案。

如果您想「一窺究竟」原始程式碼地圖的作用(或偵錯原始程式碼地圖的問題),您可以上傳相關的輸出檔案和關聯的原始程式碼地圖:原始程式碼地圖視覺化

使用原始程式碼地圖

在瀏覽器中,只要啟用了原始程式碼地圖設定,瀏覽器的開發人員工具就會自動擷取原始程式碼地圖。請注意,瀏覽器只會在堆疊追蹤記錄到主控台時,使用原始程式碼地圖來變更堆疊追蹤的顯示。堆疊追蹤本身並未修改,因此在您的程式碼中檢查 error.stack 仍會提供包含已編譯程式碼的未對應堆疊追蹤。以下是如何在瀏覽器的開發人員工具中啟用此設定

在 node 中,原始程式碼地圖原生支援從 版本 v12.12.0 開始。此功能預設停用,但可以使用旗標啟用。與瀏覽器不同,實際堆疊追蹤也會在 node 中修改,因此在您的程式碼中檢查 error.stack 會提供包含原始原始程式碼的對應堆疊追蹤。以下是如何在 node 中啟用此設定(--enable-source-maps 旗標必須在指令碼檔案名稱之前)

node --enable-source-maps app.js

來源內容

支援:建置轉換

原始程式碼地圖 使用 版本 3 的原始程式碼地圖格式產生,這是迄今為止支援最廣泛的變體。每個原始程式碼地圖看起來都像這樣

{
  "version": 3,
  "sources": ["bar.js", "foo.js"],
  "sourcesContent": ["bar()", "foo()\nimport './bar'"],
  "mappings": ";AAAA;;;ACAA;",
  "names": []
}

sourcesContent 欄位是一個選用欄位,其中包含所有原始原始程式碼。這有助於偵錯,因為這表示原始原始程式碼將在偵錯器中可用。

但是,在某些情況下並不需要。例如,如果您只是在製作環境中使用原始程式碼地圖來產生包含原始檔案名稱的堆疊追蹤,則不需要原始原始程式碼,因為沒有涉及偵錯器。在這種情況下,可以省略 sourcesContent 欄位以縮小原始程式碼地圖

CLI JS Go
esbuild --bundle app.js --sourcemap --sources-content=false
import * as esbuild from 'esbuild'

await esbuild.build({
  bundle: true,
  entryPoints: ['app.js'],
  sourcemap: true,
  sourcesContent: false,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    Bundle:         true,
    EntryPoints:    []string{"app.js"},
    Sourcemap:      api.SourceMapInline,
    SourcesContent: api.SourcesContentExclude,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

建置元資料

分析

支援:建立

如果您正在尋找互動式視覺化,請改用 esbuild 的 套件大小分析器。您可以上傳您的 esbuild 元檔案 以查看套件大小細目。

使用分析功能會產生一份關於您的套件內容的易於閱讀的報告

CLI JS Go
esbuild --bundle example.jsx --outfile=out.js --minify --analyze

  out.js                                                                    27.6kb  100.0%
   ├ node_modules/react-dom/cjs/react-dom-server.browser.production.min.js  19.2kb   69.8%
   ├ node_modules/react/cjs/react.production.min.js                          5.9kb   21.4%
   ├ node_modules/object-assign/index.js                                     962b     3.4%
   ├ example.jsx                                                             137b     0.5%
   ├ node_modules/react-dom/server.browser.js                                 50b     0.2%
   └ node_modules/react/index.js                                              50b     0.2%

...
import * as esbuild from 'esbuild'

let result = await esbuild.build({
  entryPoints: ['example.jsx'],
  outfile: 'out.js',
  minify: true,
  metafile: true,
})

console.log(await esbuild.analyzeMetafile(result.metafile))
package main

import "github.com/evanw/esbuild/pkg/api"
import "fmt"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:       []string{"example.jsx"},
    Outfile:           "out.js",
    MinifyWhitespace:  true,
    MinifyIdentifiers: true,
    MinifySyntax:      true,
    Metafile:          true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }

  fmt.Printf("%s", api.AnalyzeMetafile(result.Metafile, api.AnalyzeMetafileOptions{}))
}

此資訊顯示每個輸出檔案中包含的輸入檔案,以及這些檔案在輸出檔案中所佔的百分比。如果您需要其他資訊,可以啟用「詳細」模式。此模式目前會顯示從進入點到每個輸入檔案的匯入路徑,告訴您為何會將特定輸入檔案包含在套件中

CLI JS Go
esbuild --bundle example.jsx --outfile=out.js --minify --analyze=verbose

  out.js ─────────────────────────────────────────────────────────────────── 27.6kb ─ 100.0%
   ├ node_modules/react-dom/cjs/react-dom-server.browser.production.min.js ─ 19.2kb ── 69.8%
   │  └ node_modules/react-dom/server.browser.js
   │     └ example.jsx
   ├ node_modules/react/cjs/react.production.min.js ───────────────────────── 5.9kb ── 21.4%
   │  └ node_modules/react/index.js
   │     └ example.jsx
   ├ node_modules/object-assign/index.js ──────────────────────────────────── 962b ──── 3.4%
   │  └ node_modules/react-dom/cjs/react-dom-server.browser.production.min.js
   │     └ node_modules/react-dom/server.browser.js
   │        └ example.jsx
   ├ example.jsx ──────────────────────────────────────────────────────────── 137b ──── 0.5%
   ├ node_modules/react-dom/server.browser.js ──────────────────────────────── 50b ──── 0.2%
   │  └ example.jsx
   └ node_modules/react/index.js ───────────────────────────────────────────── 50b ──── 0.2%
      └ example.jsx

...
import * as esbuild from 'esbuild'

let result = await esbuild.build({
  entryPoints: ['example.jsx'],
  outfile: 'out.js',
  minify: true,
  metafile: true,
})

console.log(await esbuild.analyzeMetafile(result.metafile, {
  verbose: true,
}))
package main

import "github.com/evanw/esbuild/pkg/api"
import "fmt"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints:       []string{"example.jsx"},
    Outfile:           "out.js",
    MinifyWhitespace:  true,
    MinifyIdentifiers: true,
    MinifySyntax:      true,
    Metafile:          true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }

  fmt.Printf("%s", api.AnalyzeMetafile(result.Metafile, api.AnalyzeMetafileOptions{
    Verbose: true,
  }))
}

此分析只是 元檔案 中資訊的可視化呈現。如果此分析無法完全滿足您的需求,歡迎您使用元檔案中的資訊建立自己的可視化呈現。

請注意,此格式化的分析摘要是針對人類而非機器所設計。特定格式可能會隨著時間而變更,這可能會損壞嘗試解析它的任何工具。您不應撰寫工具來解析此資料。您應改用 JSON 元資料檔案 中的資訊。此可視化呈現中的所有內容都來自 JSON 元資料,因此您不會因為不解析 esbuild 的格式化分析摘要而遺失任何資訊。

元檔案

支援:建立

此選項會指示 esbuild 以 JSON 格式產生一些關於建置的元資料。以下範例會將元資料放入名為 meta.json 的檔案中

CLI JS Go
esbuild app.js --bundle --metafile=meta.json --outfile=out.js
import * as esbuild from 'esbuild'
import fs from 'node:fs'

let result = await esbuild.build({
  entryPoints: ['app.js'],
  bundle: true,
  metafile: true,
  outfile: 'out.js',
})

fs.writeFileSync('meta.json', JSON.stringify(result.metafile))
package main

import "io/ioutil"
import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    Bundle:      true,
    Metafile:    true,
    Outfile:     "out.js",
    Write:       true,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }

  ioutil.WriteFile("meta.json", []byte(result.Metafile), 0644)
}

此資料接著可以由其他工具進行分析。對於互動式可視化呈現,您可以使用 esbuild 自己的 套件大小分析器。對於快速文字分析,您可以使用 esbuild 內建的 分析 功能。或者,您可以撰寫自己的分析,使用此資訊。

元資料 JSON 格式如下(使用 TypeScript 介面描述)

interface Metafile {
  inputs: {
    [path: string]: {
      bytes: number
      imports: {
        path: string
        kind: string
        external?: boolean
        original?: string
        with?: Record<string, string>
      }[]
      format?: string
      with?: Record<string, string>
    }
  }
  outputs: {
    [path: string]: {
      bytes: number
      inputs: {
        [path: string]: {
          bytesInOutput: number
        }
      }
      imports: {
        path: string
        kind: string
        external?: boolean
      }[]
      exports: string[]
      entryPoint?: string
      cssBundle?: string
    }
  }
}

記錄

色彩

支援:建置轉換

此選項會啟用或停用 esbuild 寫入終端機中的 stderr 檔案描述符的錯誤和警告訊息中的色彩。預設情況下,如果 stderr 是 TTY 會話,色彩會自動啟用,否則會自動停用。esbuild 中的彩色輸出如下所示

[WARNING] The "typeof" operator will never evaluate to "null" [impossible-typeof]

    example.js:2:16:
      2 │ log(typeof x == "null")
        ╵                 ~~~~~~

  The expression "typeof x" actually evaluates to "object" in JavaScript, not "null". You need to
  use "x === null" to test for null.

[ERROR] Could not resolve "logger"

    example.js:1:16:
      1 │ import log from "logger"~~~~~~~~

  You can mark the path "logger" as external to exclude it from the bundle, which will remove this
  error and leave the unresolved path in the bundle.

可以透過將色彩設定為 true 來強制啟用彩色輸出。如果您要將 esbuild 的 stderr 輸出導向 TTY,這會很有用

CLI JS Go
echo 'typeof x == "null"' | esbuild --color=true 2> stderr.txt
import * as esbuild from 'esbuild'

let js = 'typeof x == "null"'
await esbuild.transform(js, {
  color: true,
})
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "typeof x == 'null'"

  result := api.Transform(js, api.TransformOptions{
    Color: api.ColorAlways,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

也可以將彩色輸出設定為 false 來停用色彩。

格式化訊息

支援:建置轉換

此 API 呼叫可用於格式化 建置 API 和 轉換 API 傳回的記錄錯誤和警告,並使用 esbuild 本身使用的相同格式化作為字串。如果您想要自訂 esbuild 記錄運作的方式,例如在列印記錄訊息之前處理這些訊息或將它們列印到主控台以外的位置,這會很有用。以下是一個範例

JS Go
import * as esbuild from 'esbuild'

let formatted = await esbuild.formatMessages([
  {
    text: 'This is an error',
    location: {
      file: 'app.js',
      line: 10,
      column: 4,
      length: 3,
      lineText: 'let foo = bar',
    },
  },
], {
  kind: 'error',
  color: false,
  terminalWidth: 100,
})

console.log(formatted.join('\n'))
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "strings"

func main() {
  formatted := api.FormatMessages([]api.Message{
    {
      Text: "This is an error",
      Location: &api.Location{
        File:     "app.js",
        Line:     10,
        Column:   4,
        Length:   3,
        LineText: "let foo = bar",
      },
    },
  }, api.FormatMessagesOptions{
    Kind:          api.ErrorMessage,
    Color:         false,
    TerminalWidth: 100,
  })

  fmt.Printf("%s", strings.Join(formatted, "\n"))
}

選項

可以提供以下選項來控制格式化

JS Go
interface FormatMessagesOptions {
  kind: 'error' | 'warning';
  color?: boolean;
  terminalWidth?: number;
}
type FormatMessagesOptions struct {
  Kind          MessageKind
  Color         bool
  TerminalWidth int
}

日誌層級

支援:建置轉換

可以變更日誌層級以防止 esbuild 將警告和/或錯誤訊息列印到終端。六個日誌層級是

可以這樣設定日誌層級

CLI JS Go
echo 'typeof x == "null"' | esbuild --log-level=error
import * as esbuild from 'esbuild'

let js = 'typeof x == "null"'
await esbuild.transform(js, {
  logLevel: 'error',
})
package main

import "fmt"
import "github.com/evanw/esbuild/pkg/api"

func main() {
  js := "typeof x == 'null'"

  result := api.Transform(js, api.TransformOptions{
    LogLevel: api.LogLevelError,
  })

  if len(result.Errors) == 0 {
    fmt.Printf("%s", result.Code)
  }
}

日誌限制

支援:建置轉換

預設情況下,esbuild 在報告 10 則訊息後停止報告日誌訊息。這可以避免意外產生大量日誌訊息,這很容易鎖定較慢的終端機模擬器,例如 Windows 命令提示字元。它還可以避免意外用完具有有限捲動緩衝區的終端機模擬器的整個捲動緩衝區。

可以將日誌限制變更為其他值,也可以透過將其設定為零來完全停用。這將顯示所有日誌訊息

CLI JS Go
esbuild app.js --log-limit=0
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  logLimit: 0,
  outfile: 'out.js',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    LogLimit:    0,
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

日誌覆寫

支援:建置轉換

此功能可讓你變更個別類型日誌訊息的日誌層級。你可以使用它來取消特定類型的警告,啟用預設情況下未啟用的其他警告,甚至將警告轉換為錯誤。

例如,在鎖定舊瀏覽器時,esbuild 會自動將使用對這些瀏覽器來說太新的功能的正規表示式文字轉換為 new RegExp() 呼叫,以允許產生的程式碼執行,而不會被瀏覽器視為語法錯誤。但是,如果你沒有為 RegExp 新增多載,這些呼叫在執行時仍會擲回,因為正規表示式語法仍然不受支援。如果你希望 esbuild 在你使用較新的不受支援正規表示式語法時產生警告,你可以這樣做

CLI JS Go
esbuild app.js --log-override:unsupported-regexp=warning --target=chrome50
import * as esbuild from 'esbuild'

await esbuild.build({
  entryPoints: ['app.js'],
  logOverride: {
    'unsupported-regexp': 'warning',
  },
  target: 'chrome50',
})
package main

import "github.com/evanw/esbuild/pkg/api"
import "os"

func main() {
  result := api.Build(api.BuildOptions{
    EntryPoints: []string{"app.js"},
    LogOverride: map[string]api.LogLevel{
      "unsupported-regexp": api.LogLevelWarning,
    },
    Engines: []api.Engine{
      {Name: api.EngineChrome, Version: "50"},
    },
  })

  if len(result.Errors) > 0 {
    os.Exit(1)
  }
}

每個訊息類型的記錄層級都可以覆寫成 記錄層級 設定支援的任何值。目前所有可用的訊息類型都列在下方(按一下每個類型以取得範例記錄訊息)

這些訊息類型應相當穩定,但未來可能會新增新的訊息類型,偶爾也會移除舊的訊息類型。如果移除某個訊息類型,該訊息類型的任何覆寫都會被靜默略過。