新しい非常に大きな魔法のガジェット-esbuild

GitHubアドレス:https : //github.com/yyx990803/esbuild

エスビルド

これは、JavaScriptのバンドルおよび縮小機能です。JavaScriptとTypeScriptコードをパッケージ化して、Webで配布します。

どうして?

なぜ別のJavaScriptビルドツールをビルドするのですか?現在のWeb用のビルドツールは、本来あるべき速度よりも少なくとも1桁遅いです。このプロジェクトが、私たちのJavaScriptツールがはるかに高速であり得ることの「存在証明」として役立つことを願っています。

ベンチマーク

私が念頭に置いているユースケースは、本番用に大規模なコードベースをパッケージ化することです。これには、ネットワーク転送時間を短縮するコードの縮小、および本番環境でのデバッグエラーにとって重要なソースマップの作成が含まれます。理想的には、ビルドツールは、最初にキャッシュをウォームアップする必要なく、すばやくビルドする必要もあります。

現在、esbuildのパフォーマンスを測定するために使用している2つのベンチマークがあります。これらのベンチマークでは、esbuildは、私がテストした他のJavaScriptバンドルより10〜100倍高速です。

ここに画像の説明を挿入

各ベンチマークの詳細は次のとおりです。

  • JavaScriptベンチマーク

    このベンチマークは、three.jsライブラリを10回複製し、キャッシュなしで単一のバンドルを最初から構築することにより、大規模なJavaScriptコードベースを概算しますベンチマークはで実行できますmake bench-three

    バンドラー 時間 相対的な減速 絶対速度 出力サイズ
    エスビルド 0.54秒 1x 1013.8 kloc / s 5.83mb
    ロールアップ+ terer 40.48秒 75倍 13.5 kloc / s 5.80mb
    ウェブパック 46.46秒 86倍 11.8 kloc / s 5.97mb
    小包 124.65秒 231x 4.4 kloc / s 5.90mb
    ヒューズボックス@次 172.56s 320x 3.2 kloc / s 6.55MB

    報告された時間は、3回の実行のうち最高のものです。でesbuildを実行してい--bundle --minify --sourcemapます。rollup-plugin-terserロールアップ自体は縮小をサポートしていないため、私はプラグインを使用しましたWebpackはを使用し--mode=production --devtool=sourcemapます。区画はデフォルトのオプションを使用します。FuseBoxはで構成されuseSingleBundle: trueます。絶対速度は、コメントと空白行を含む合計行数に基づいており、現在は547,441です。テストは、16 GBのRAMを搭載した6コアの2019 MacBook Proで行われました。

  • TypeScriptベンチマーク

    このベンチマークはRomeビルドツールを使用して、大規模なTypeScriptコードベースを概算します。すべてのコードは、ソースマップを含む1つの縮小されたバンドルに組み合わせる必要があり、結果のバンドルは正しく機能する必要があります。ベンチマークはで実行できますmake bench-rome

    バンドラー 時間 相対的な減速 絶対速度 出力サイズ
    エスビルド 0.13秒 1x 1014.1 kloc / s 0.99 MB
    小包 15.89秒 122x 8.3 kloc / s 1.50mb
    ウェブパック 18.37秒 141x 7.2 kloc / s 1.22 MB

    報告された時間は、3回の実行のうち最高のものです。でesbuildを実行してい--bundle --minify --sourcemap --platform=nodeます。WebPACKの用途ts-loaderを持つtranspileOnly: true--mode=production --devtool=sourcemap小包はを使用し--target node --bundle-node-modulesます。絶対速度はコメントと空白行を含む合計行数に基づいており、現在は131,836です。テストは、16 GBのRAMを搭載した6コアの2019 MacBook Proで行われました。

    機能させることができなかったため、結果にはロールアップが含まれていません。私が試したrollup-plugin-typescript@rollup/plugin-typescript@rollup/plugin-sucrase彼らはすべての活字体のコンパイルに関連するさまざまな理由で動作しませんでした。そして、私はFuseBoxに慣れていないので、組み込みノードモジュールの使用によるビルドエラーの回避策がわかりません。

なぜそれが速いのですか?

いくつかの理由:

  • ネイティブコードにコンパイルする言語であるGoで記述されている
  • 解析、印刷、ソースマップの生成はすべて完全に並列化されています
  • すべてが非常に少ないパスで実行され、高価なデータ変換は不要
  • コードは速度を考慮して書かれており、不要な割り当てを回避しようとします

状態

現在サポートされています:

  • JavaScriptおよびTypeScript構文
  • CommonJSおよびES6モジュール
  • JSXからJavaScriptへの変換
  • ES6モジュールの静的バインディングとのバンドル --bundle
  • --minify(空白、識別子、マングリング)による完全な縮小
  • --sourcemapが有効な場合の完全なソースマップサポート
  • コンパイル時の識別子の置換 --define
  • browserフィールドを使用したパス置換package.json
  • 自動検出baseUrltsconfig.json

構文サポート:

構文変換は、古いブラウザーで使用するために、新しいJavaScript構文を古いJavaScript構文に変換します。言語ターゲットは--targetフラグで設定できます。フラグはES6までさかのぼります。esbuildが現在の言語ターゲットへの変換をまだサポートしていない構文機能を使用する場合、esbuildは引き続き正常にビルドされますが、サポートされていない構文が使用されている場合は警告が生成され、変換されないまま構文が渡されます。

これらの構文機能は、古いブラウザーでは常に変換されます。

構文変換 言語バージョン
関数パラメーターリストと呼び出しの末尾のコンマ es2017 foo(a, b, )
数値セパレーター esnext 1_000_000

これらの構文機能は、構成された言語ターゲットに応じて、古いブラウザー用に条件付きで変換されます。

構文変換 --target以下のときに変形
べき乗演算子 es2016 a ** b
スプレッドプロパティ es2018 let x = {...y}
オプションのキャッチバインディング es2019 try {} catch {}
オプションの連鎖 es2020 a?.b
ヌリッシュ合体 es2020 a ?? b
クラスインスタンスフィールド esnext class { x }
静的クラスフィールド esnext class { static x }

これらの構文機能は現在、常に変換されずに渡されます。

構文変換 --target以下の場合サポートされません
非同期機能 es2017 async () => {}
残りのプロパティ es2018 let {...x} = y
非同期反復 es2018 for await (let x of y) {}
BigInt es2020 123n
ハッシュバン文法 esnext #!/usr/bin/env node

これらの構文機能はまだサポートされておらず、現在は解析できません。

構文変換 言語バージョン
プライベートインスタンスメソッド esnext class { #x() {} }
プライベートインスタンスフィールド esnext class { #x }
プライベート静的フィールドとメソッド esnext class { static #x }
論理代入演算子 esnext a ??= b

参照して完成ECMAScriptの提案のリストアクティブなECMAScriptの提案のリストを

免責事項:

  • これは趣味のプロジェクトです。比較的完全で機能的だと思います。ただし、これはまったく新しいコードであり、いくつかのバグに遭遇する可能性があります。
  • This hasn’t yet been used in production by anyone. Use at your own risk.
  • I don’t personally want to run a large community project, so I’m not looking for contributions at this time.
  • The code in this repo isn’t intended to be built upon. I’m may change the internals in a backwards-incompatible way at any time to improve performance or introduce new features.

There is now some documentation about the architecture and about certain subtleties in the code here: docs/architecture.md. I hope it will be helpful for those interested in learning more about how the code works.

Install

A prebuilt binary can be installed using npm:

  • Local install (recommended)

    This installs the esbuild command locally in your project’s package.json file:

    npm install --save-dev esbuild
    

    Invoke it using npx esbuild [arguments]. Note that this uses the npx package runner command, not the npm package manager command.

    This is the recommended project-based workflow because it allows you to have a different version of esbuild for each project and it ensures that everyone working on a given project has the same version of esbuild.

  • Global install

    This adds a global command called esbuild to your path:

    npm install --global esbuild
    

    Invoke it using esbuild [arguments].

    A global install can be handy if you want to run esbuild outside of a project context for one-off file manipulation tasks.

The esbuild package should work on 64-bit macOS, Linux, and Windows systems. It contains an install script that downloads the appropriate package for the current platform. If the install script isn’t working or you need to run esbuild on an unsupported platform, there is a fallback WebAssembly package called esbuild-wasm that should work on all platforms.

For development, the executable can be built by running make (assuming you have the Go language toolchain installed).

Command-line usage

The command-line interface takes a list of entry points and produces one bundle file per entry point. Here are the available options:

Usage:
  esbuild [options] [entry points]

Options:
  --name=...            The name of the module
  --bundle              Bundle all dependencies into the output files
  --outfile=...         The output file (for one entry point)
  --outdir=...          The output directory (for multiple entry points)
  --sourcemap           Emit a source map
  --error-limit=...     Maximum error count or 0 to disable (default 10)
  --target=...          Language target (default esnext)
  --platform=...        Platform target (browser or node, default browser)
  --external:M          Exclude module M from the bundle
  --format=...          Output format (iife or cjs)
  --color=...           Force use of color terminal escapes (true or false)

  --minify              Sets all --minify-* flags
  --minify-whitespace   Remove whitespace
  --minify-identifiers  Shorten identifiers
  --minify-syntax       Use equivalent but shorter syntax

  --define:K=V          Substitute K with V while parsing
  --jsx-factory=...     What to use instead of React.createElement
  --jsx-fragment=...    What to use instead of React.Fragment
  --loader:X=L          Use loader L to load file extension X, where L is
                        one of: js, jsx, ts, tsx, json, text, base64

  --trace=...           Write a CPU trace to this file
  --cpuprofile=...      Write a CPU profile to this file
  --version             Print the current version and exit

Examples:
  # Produces dist/entry_point.js and dist/entry_point.js.map
  esbuild --bundle entry_point.js --outdir=dist --minify --sourcemap

  # Allow JSX syntax in .js files
  esbuild --bundle entry_point.js --outfile=out.js --loader:.js=jsx

  # Substitute the identifier RELEASE for the literal true
  esbuild example.js --outfile=out.js --define:RELEASE=true

JavaScript API usage

The esbuild npm package also exposes a JavaScript API that can be used to invoke the command-line tool from JavaScript.

Running a build

The build() API is the same as invoking the command-line tool. It reads from files on disk and writes back to files on disk. Using this API can be more convenient than managing a lot of command-line flags and also works on all platforms, unlike shell scripts. This is similar to “config files” from other bundlers.

Example build script:

const {
    
     build } = require('esbuild')

const options = {
    
    
  stdio: 'inherit',
  entryPoints: ['./src/main.ts'],
  outfile: './dist/main.js',
  minify: true,
  bundle: true,
}

build(options).catch(() => process.exit(1))

See the TypeScript type definitions for the complete set of options.

Transforming a file

The transform() API transforms a single file in memory. It can be used to minify JavaScript, convert TypeScript/JSX to JavaScript, or convert newer JavaScript to older JavaScript. It’s roughly equivalent to running build() on a single file with bundle: false.

To access this API you need to start a service, which is a long-lived esbuild child process that is then reused. You can use the service to transform many files without the overhead of starting up a new child process each time.

Example usage:

(async () => {
    
    
  const jsx = `
    import * as React from 'react'
    import * as ReactDOM from 'react-dom'

    ReactDOM.render(
      <h1>Hello, world!</h1>,
      document.getElementById('root')
    );
  `

  // Start the esbuild child process once
  const esbuild = require('esbuild')
  const service = await esbuild.startService()

  // This can be called many times without the overhead of starting a service
  const {
    
     js } = await service.transform(jsx, {
    
     loader: 'jsx' })
  console.log(js)

  // The child process can be explicitly killed when it's no longer needed
  service.stop()
})()

See the TypeScript type definitions for the complete set of options.

Using with React

To use esbuild with React:

  • Either put all JSX syntax in .jsx files instead of .js files, or use --loader:.js=jsx to use the JSX loader for .js files.

  • If you’re using TypeScript, pass esbuild your .tsx file as the entry point. There should be no need to convert TypeScript files to JavaScript first because because esbuild parses TypeScript syntax itself.

    Note that esbuild does not do any type checking, so you’ll want to run tsc -noEmit in parallel to check types.

  • If you’re using esbuild to bundle React yourself instead of including it with a <script> tag in your HTML, you’ll need to pass '--define:process.env.NODE_ENV="development"' or '--define:process.env.NODE_ENV="production"' to esbuild on the command line.

    Note that the double quotes around "production" are important because the replacement should be a string, not an identifier. The outer single quotes are for escaping the double quotes in Bash but may not be necessary in other shells.

  • If you’re using Preact instead of React, you’ll also need to pass --jsx-factory=preact.h --jsx-fragment=preact.Fragment to esbuild on the command line.

For example, if you have a file called example.tsx with the following contents:

import * as React from 'react'
import * as ReactDOM from 'react-dom'

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);

Use this for a development build:

esbuild example.tsx --bundle '--define:process.env.NODE_ENV="development"' --outfile=out.js

Use this for a production build:

esbuild example.tsx --bundle '--define:process.env.NODE_ENV="production"' --minify --outfile=out.js

おすすめ

転載: blog.csdn.net/SmallTeddy/article/details/108711937