An extremely fast JavaScript bundler and minifier

Overview

esbuild: An extremely fast JavaScript bundler
Website | Getting started | Documentation | Plugins | FAQ

Why?

Our current build tools for the web are 10-100x slower than they could be:

Bar chart with benchmark results

The main goal of the esbuild bundler project is to bring about a new era of build tool performance, and create an easy-to-use modern bundler along the way.

Major features:

Check out the getting started instructions if you want to give esbuild a try.

Issues
  • When used with `require()` and `node`, does not honor

    When used with `require()` and `node`, does not honor "main" field in package.json if "module" is present

    EDIT: Originally ran into this with node-fetch, but it seems to have surfaced a larger issue which is that esbuild doesn't prioritize the main field in package.json if you're using require() with node. This means that esbuild diverges from how Node.js/CommonJS would require the same package.

    It appears from the comments that this is by design:

    We support ES6 so we always prefer the "module" field over the "main" field.

    https://github.com/evanw/esbuild/blob/99f587a133b34794c235be5fff9919f57b56297e/internal/resolver/resolver.go#L272-L280

    But this means that certain packages just don't work with require(), such as node-fetch:

    To reproduce:

    $ node --version
    v12.18.3
    
    $ npm install node-fetch
    
    $ node -p "typeof require('node-fetch')"
    function
    
    $ node -p "$(echo "typeof require('node-fetch')" | npx esbuild --bundle --platform=node)"
    object
    

    Would be great if esbuild supported the same behavior as Node.js for require'ing modules – if not out-of-the-box, then possibly via a flag?

    opened by mhart 44
  • a few assorted issues

    a few assorted issues

    esbuild-minify is a dumb wrapper script over esbuild --minify - it's not important other than to demonstrate the following minification issues.

    Default argument scope:

    $ cat esb1.js 
    let a="PASS";((x=a)=>{let a="FAIL";console.log(x)})();
    
    $ cat esb1.js | node
    PASS
    
    $ cat esb1.js | esbuild-minify | node
    [stdin]:1
    let c="PASS";((a=b)=>{let b="FAIL";console.log(a)})();
                     ^
    ReferenceError: b is not defined
    

    Switch expression scope:

    $ cat esb2.js 
    var x="FAIL";switch(x){default:let x="PASS";console.log(x)}
    
    $ cat esb2.js | node
    PASS
    
    $ cat esb2.js | esbuild-minify | node
    [stdin]:1
    var b="FAIL";switch(a){default:let a="PASS";console.log(a)}
                        ^
    ReferenceError: a is not defined
    

    Object literal computed property output:

    $ cat esb3.js 
    var x=1;o={[(++x,5)]:x=>++x};console.log(x,o[5](x),--x);
    
    $ cat esb3.js | node
    2 3 1
    
    $ cat esb3.js | esbuild-minify | node
    [stdin]:1
    var a=1;o={[++a,5]:b=>++b},console.log(a,o[5](a),--a);
                   ^
    SyntaxError: Unexpected token ,
    

    Class method computed property output:

    $ cat esb4.js 
    var x=1;o=new class{[(++x,5)](x){return++x}};console.log(x,o[5](x),--x);
    
    $ cat esb4.js | node
    2 3 1
    
    $ cat esb4.js | esbuild-minify | node
    [stdin]:1
    var a=1;o=new class{[++a,5](b){return++b}}(),console.log(a,o[5](a),--a);
                            ^
    SyntaxError: Unexpected token ,
    
    opened by kzc 37
  • [Question] Tree shaking

    [Question] Tree shaking

    @evanw, are you planning to add tree shaking to exclude unused code from bundles?

    opened by amursky 33
  • [MVP] Watch mode

    [MVP] Watch mode

    I want esbuild to demonstrate that it's possible for a normal web development workflow to have high-performance tools. I consider watch mode a part of my initial MVP feature set for esbuild since I believe development builds should be virtually instant. This issue tracks the watch mode part of my MVP.

    Watch mode involves automatically rebuilding when files on disk have been changed. While esbuild is much faster than other bundlers, very large code bases may still take around a second to build. I consider that too long for a development workflow. Watch mode will keep the previous build in memory and only rebuild the changed files between builds.

    This may or may not involve disabling certain cross-file optimizations while in watch mode depending on what the performance looks like. I may also implement a local HTTP server that can be queried for the latest build, which is a nice way to avoid a page reload accidentally picking up a stale build.

    opened by evanw 32
  • Can't esbuild multiple files unless I use unix find

    Can't esbuild multiple files unless I use unix find

    I also don't see index.html file in dist directory.

    The command I used was

    find . -name 'src/*.js' -exec esbuild {} --bundle --outdir=dist --minify --sourcemap --loader:.js=jsx --external:common/Loadable --external:components/MainContent/ThemeChooser --external:components/MainContent/BuildInfoRegion --external:stores/ThemeStylesStore --external:helpers/ThemeManager --external:stores/ThemeStylesStore --external:common/StyledListItem --external:common/StyledSubheader \;
    

    I'm also wondering why I have to add --external since I wrote them.

    It's a ReactJS application.

    opened by c0debreaker 31
  • support deno

    support deno

    Hi guy! Deno's excellence made me give up node, but I need a build tool like esbuild.

    I want to transform npm modules to esm to run in deno, which allows deno to have the entire web ecosystem.

    I know swc, but it is not reliable yet, the worst case is that it will transform to wrong code, so I need esbuild, just like node, but not webAssembly(esbuild-wasm).

    opened by sessionboy 29
  • Wasm build doesn't work on Windows

    Wasm build doesn't work on Windows

    Running a build on Windows doesn't work when using the esbuild-wasm package. It works fine when using esbuild (but we'd prefer to avoid that since it requires a postinstall script).

    Repro:

    git clone [email protected]:yarnpkg/berry && cd berry
    git checkout mael/esbuild-2
    yarn build:cli
    

    Error:

    error: Invalid path: C:\berry\packages\yarnpkg-cli\bundles\yarn.js
        at failureErrorWithLog (C:\berry\.yarn\cache\esbuild-wasm-npm-0.8.33-0589ac914f-8d0542a70c.zip\node_modules\esbuild-wasm\lib\main.js:969:15)
        at buildResponseToResult (C:\berry\.yarn\cache\esbuild-wasm-npm-0.8.33-0589ac914f-8d0542a70c.zip\node_modules\esbuild-wasm\lib\main.js:767:32)
        at C:\berry\.yarn\cache\esbuild-wasm-npm-0.8.33-0589ac914f-8d0542a70c.zip\node_modules\esbuild-wasm\lib\main.js:819:20
        at handleIncomingPacket (C:\berry\.yarn\cache\esbuild-wasm-npm-0.8.33-0589ac914f-8d0542a70c.zip\node_modules\esbuild-wasm\lib\main.js:566:9)
    
    opened by arcanis 25
  • module2.require is not a function when bundling for node

    module2.require is not a function when bundling for node

    Given:

    test.js:

    require('./test2')
    

    test2.js:

    console.log(typeof module.require('crypto'))
    

    And running:

    $ node test.js 
    object
    
    $ esbuild --version
    0.7.15
    
    $ node <(npx esbuild --bundle --platform=node test.js)
    /dev/fd/11:11
      console.log(typeof module2.require("crypto"));
                                 ^
    
    TypeError: module2.require is not a function
    

    When I look at the bundled output, it seems that no arguments are passed in to require_test2:

    var __commonJS = (callback, module2) => () => {
      if (!module2) {
        module2 = {exports: {}};
        callback(module2.exports, module2);
      }
      return module2.exports;
    };
    
    // test2.js
    var require_test2 = __commonJS((exports, module2) => {
      console.log(typeof module2.require("crypto"));
    });
    
    // test.js
    require_test2();
    

    This pattern is used in the real world, for example here:

    https://github.com/apollographql/apollo-server/blob/570f548b88750a06fbf5f67a4abe78fb0f870ccd/packages/apollo-server-core/src/utils/createSHA.ts#L5-L7

    I've also tried passing --define:module.require=require and --define:module2.require=require but neither of them seem to have any effect (should I open a separate bug for that?)

    opened by mhart 25
  • Remove unused pure functions and variables

    Remove unused pure functions and variables

    Esbuild doesn't currently seem to remove unused pure functions or variables. For example this input:

    (function() {
      var x = 2;
    
      function foo() {
        console.log('foo');
      }
    
      function bar() {
        console.log('bar');
      }
    
      foo();
    })();
    

    Currently produces this output:

    (function(){var n=2;function o(){console.log("foo")}function c(){console.log("bar")}o()})();
    

    Whereas terser produces:

    console.log("foo");
    

    That's obviously even more optimized in this case to get rid of the function declaration entirely, but I'd accept at least just removing the unused vars and functions. We're considering esbuild as an alternative minifier in Parcel, but we don't currently have our own DCE pass and rely on terser for this. This means that the output will typically be much larger than it is currently without either Parcel implementing its own DCE or esbuild doing so.

    opened by devongovett 24
  • Different strategy for installing platform-specific binaries

    Different strategy for installing platform-specific binaries

    Hi! πŸ‘‹πŸ»

    We're looking to integrate esbuild with the Netlify CLI and some users are reporting permission-related errors when installing the application, which trace back to https://github.com/evanw/esbuild/issues/369.

    I understand that this is a result of the UID/GID switching that npm does when running scripts, which is problematic when the package is installed as root.

    Rather than manually fetching the appropriate binary as part of the postinstall script, could the esbuild package list the various platform-specific packages as optionalDependencies, and shift to npm itself the responsibility of figuring out which packages to installed based on the cpu and os properties? We're doing this for one of our packages that also includes a Go binary and it works well.

    I'm happy to contribute with a pull request, but I wanted to check whether this is something you'd be interested in, or even if it's something you've already considered and discarded for a particular reason.

    Thanks in advance!

    opened by eduardoboucas 23
  • Barrel export library not tree-shakeable with ESM

    Barrel export library not tree-shakeable with ESM

    Hi!

    I have a simple use case: import everything from a lot of libraries and export it out in a standardized way. This is how it is πŸ‘‡

    Code
    /** COLLECTIONS */
    export { diff as collectionDiff } from 'just-diff';
    export { diffApply as collectionDiffApply } from 'just-diff-apply';
    export { default as collectionCompare } from 'just-compare';
    export { default as collectionClone } from 'just-clone';
    export { default as collectionPluck } from 'just-pluck-it';
    export { default as collectionFlush } from 'just-flush';
    
    /** OBJECTS */
    export { default as objectsExtend } from 'just-extend';
    export { default as objectsMerge } from 'just-merge';
    export { values as objectsValues } from 'just-values';
    export { default as objectsEntries } from 'just-entries';
    export { default as objectsPick } from 'just-pick';
    export { omit as objectsOmit } from 'just-omit';
    export { default as objectsFilterObject } from 'just-filter-object';
    export { default as objectsMapObject } from 'just-map-object';
    export { default as objectsMapValues } from 'just-map-values';
    export { default as objectsMapKeys } from 'just-map-keys';
    export { default as objectsReduceObject } from 'just-reduce-object';
    export { default as objectsIsEmpty } from 'just-is-empty';
    export { default as objectsIsCircular } from 'just-is-circular';
    export { default as objectsIsPrimitive } from 'just-is-primitive';
    export { default as objectsSafeGet } from 'just-safe-get';
    export { default as objectsSafeSet } from 'just-safe-set';
    export { default as objectsTypeof } from 'just-typeof';
    export { default as objectsFlipObject } from 'just-flip-object';
    
    /** ARRAYS */
    export { default as arraysCartesianProduct } from 'just-cartesian-product';
    export { default as arraysUnique } from 'just-unique';
    export { default as arraysFlattenIt } from 'just-flatten-it';
    export { default as arraysIndex } from 'just-index';
    export { default as arraysInsert } from 'just-insert';
    export { default as arraysIntersect } from 'just-intersect';
    export { default as arraysCompact } from 'just-compact';
    export { default as arraysLast } from 'just-last';
    export { default as arraysTail } from 'just-tail';
    export { default as arraysRandom } from 'just-random';
    export { default as arraysShuffle } from 'just-shuffle';
    export { default as arraysSplit } from 'just-split';
    export { default as arraysSplitAt } from 'just-split-at';
    export { default as arraysSortBy } from 'just-sort-by';
    export { default as arraysPartition } from 'just-partition';
    export { default as arraysPermutations } from 'just-permutations';
    export { default as arraysRange } from 'just-range';
    export { default as arraysRemove } from 'just-remove';
    export { default as arraysUnion } from 'just-union';
    export { default as arraysZip } from 'just-zip-it';
    export { default as arraysGroupBy } from 'just-group-by';
    
    /** STATISTICS */
    export { default as statisticsMean } from 'just-mean';
    export { default as statisticsMedian } from 'just-median';
    export { default as statisticsMode } from 'just-mode';
    export { default as statisticsPercentile } from 'just-percentile';
    export { default as statisticsVariance } from 'just-variance';
    export { default as statisticsStdev } from 'just-stdev';
    export { default as statisticsSkewness } from 'just-skewness';
    
    /** STRINGS */
    export { default as stringsTemplate } from 'just-template';
    export { default as stringsTruncate } from 'just-truncate';
    export { default as stringsPrune } from 'just-prune';
    export { default as stringsSquash } from 'just-squash';
    export { default as stringsLeftPad } from 'just-left-pad';
    export { default as stringsRightPad } from 'just-right-pad';
    export { default as stringsCamelCase } from 'just-camel-case';
    export { default as stringsKebabCase } from 'just-kebab-case';
    export { default as stringsSnakeCase } from 'just-snake-case';
    export { default as stringsPascalCase } from 'just-pascal-case';
    export { default as stringsCapitalize } from 'just-capitalize';
    export { default as stringsReplaceAll } from 'just-replace-all';
    
    /** NUMBERS */
    export { default as numbersClamp } from 'just-clamp';
    export { default as numbersPrime } from 'just-is-prime';
    export { default as numbersModulo } from 'just-modulo';
    
    /** FUNCTIONS */
    export { default as functionsCompose } from 'just-compose';
    export { default as functionsCurry } from 'just-curry-it';
    export { default as functionsDemethodize } from 'just-demethodize';
    export { default as functionsFlip } from 'just-flip';
    export { default as functionsPartial } from 'just-partial-it';
    export { default as functionsDebounce } from 'just-debounce-it';
    export { default as functionsMemoize } from 'just-memoize';
    export { default as functionsMemoizeLast } from 'just-memoize-last';
    export { default as functionsRandom } from 'just-random';
    export { default as functionsThrottle } from 'just-throttle';
    export { once as functionsOnce } from 'just-once';
    
    

    Now, I compile it using esbuild to both ESM and JS, and the script is more or less this πŸ‘‡

    const commonConfig: BuildOptions = {
      entryPoints: [
        '../src/index.ts',
        '../src/collection.ts',
        '../src/objects.ts',
        '../src/arrays.ts',
        '../src/statistics.ts',
        '../src/strings.ts',
        '../src/numbers.ts',
        '../src/functions.ts',
      ],
      platform: 'node',
      bundle: true,
      sourcemap: true,
      treeShaking: true,
      target: 'esnext',
      outdir: '../dist/',
      tsconfig: '../tsconfig.json',
    };
    
    await Promise.all([
        build({
          ...commonConfig,
          format: 'esm',
          outExtension: {
            '.js': '.js',
          },
        }),
        build({
          ...commonConfig,
          format: 'cjs',
          outExtension: {
            '.js': '.cjs',
          },
        }),
      ]);
    
    

    The output for the esm can be found here https://gist.github.com/PuruVJ/079b89257f4ba5b60db6cea8db42ef9d

    Problem is, whenever I import individual functions from this code, and compile it(In ViteJS), the whole library is included. Even though it export everything individually.

    Rich harris's https://github.com/Rich-Harris/agadoo tool also says the file isn't tree shakeable

    Please help

    Thanks

    opened by PuruVJ 2
  • Compatability between inject and CJS

    Compatability between inject and CJS

    So, the docs are kind of a weird mishmash of cjs and esm, although they mention CommonJS as a "major feature".

    We've hit a snag with trying to inject some global libraries (like jQuery, React and the like) into our bundle. Let's say we have a file called vendor, which pass to inject, containing the following:

    module.exports = {
    	$: require('jquery'),
    	React: require('react'),
    	ReactDOM: require('react-dom')
    };
    

    ^ The above won't work, and the bundle will complain about a missing React dependency. However, transforming the file to esm -format will work:

    import * as $ from 'jquery';
    import * as React from 'react';
    import * as ReactDOM from 'react-dom';
    
    export { $, React, ReactDOM }
    

    Am I to understand that cjs simply won't work with inject? If so, are there other features that behave similarly? The docs don't really make this clear.

    opened by neutraali 5
  • TypeError: Cannot convert undefined or null to object while trying to analyze metadata

    TypeError: Cannot convert undefined or null to object while trying to analyze metadata

    I was able to convert a small package of mine from Rollup to esbuild, but as soon as I added a call to analyzeMetafile (following the example here), I started getting a crash:

    $ node --trace-warnings esbuild.config.js
    (node:15836) UnhandledPromiseRejectionWarning: TypeError: Cannot convert undefined or null to object
        at Function.keys (<anonymous>)
        at visit (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:81:25)
        at visit (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:86:9)
        at encodePacket (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:93:3)
        at sendRequest (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:637:27)
        at Object.analyzeMetafile2 [as analyzeMetafile] (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1369:5)
        at C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1940:55
        at new Promise (<anonymous>)
        at Object.analyzeMetafile (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1940:14)
        at Object.analyzeMetafile (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1753:71)
        at emitUnhandledRejectionWarning (internal/process/promises.js:168:15)
        at processPromiseRejections (internal/process/promises.js:247:11)
        at processTicksAndRejections (internal/process/task_queues.js:94:32)
    (node:15836) TypeError: Cannot convert undefined or null to object
        at Function.keys (<anonymous>)
        at visit (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:81:25)
        at visit (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:86:9)
        at encodePacket (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:93:3)
        at sendRequest (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:637:27)
        at Object.analyzeMetafile2 [as analyzeMetafile] (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1369:5)
        at C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1940:55
        at new Promise (<anonymous>)
        at Object.analyzeMetafile (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1940:14)
        at Object.analyzeMetafile (C:\Work\web-components\common\temp\node_modules\.pnpm\[email protected]\node_modules\esbuild\lib\main.js:1753:71)
    (node:15836) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
        at emitDeprecationWarning (internal/process/promises.js:180:11)
        at processPromiseRejections (internal/process/promises.js:249:13)
        at processTicksAndRejections (internal/process/task_queues.js:94:32)
    
    opened by kaiyoma 2
  • add  dependent info to metafile

    add dependent info to metafile

    It seems that metafile doesn't contains dependent(A depends on B, then A is B's dependent, B is A's dependency) information about module, which is hard to use for some scenario.(for example I want to track which entryPoints import specific js file) image

    opened by hardfist 2
  • Support `imports` field as internal package import map

    Support `imports` field as internal package import map

    It looks like resolving internal package specifiers (e.g. #internal-dep) doesn't currently work. I think, what would make sense is to apply the same conditions as we do for the exports field.

    I'm working on a plugin that would do just that, let me know if you'd be interested in including this into the core, I'm happy to do the necessary work for that.

    opened by vovacodes 0
  • translation

    translation

    translation

    maybe esbuild could use crowdin so we can contribute translation

    opened by EiosH 1
  • JS sync API is _not_ the fastest?

    JS sync API is _not_ the fastest?

    The docs state (https://esbuild.github.io/api/#js-specific-details):

    First there is the synchronous API. […] It's also optimal performance-wise if all you need to do is run esbuild and then exit.

    All I need to do is run esbuild and then exit – perfect! But I know that the sync API uses a worker_thread behind the scenes, and I recently learned those are not free to spawn performance wise. So I measured if the sync API is really the fastest!

    Here are the three ways of running esbuild I tested:

    // esbuild.transformSync
    const newCode = esbuild.transformSync(code, {
      minify: true,
      target: "es5",
      format: "iife",
    }).code;
    
    // esbuild.transform
    const newCode = (await esbuild.transform(code, {
      minify: true,
      target: "es5",
      format: "iife",
    })).code;
    
    // childProcess.spawnSync
    const newCode = childProcess.spawnSync(
      "esbuild",
      ["--minify", "--target=es5"],
      {
        input: code,
        encoding: "utf8"
      }
    ).stdout;
    

    These are the results:

    | variant | time | second run, for fun | | ---------------------- | -------: | ------------------: | | esbuild.transformSync | 77.133ms | 11.95ms | | esbuild.transform | 29.758ms | 12.852ms | | childProcess.spawnSync | 24.375ms | 23.155ms |

    Benchmarking is hard, but at least in my case it looks like esbuild.transformSync is a clear loser performance-wise when it comes to the β€œrun esbuild once, then exit” use case. Logically, this makes sense to me. The sync API does more work than the others: It also has to spawn a worker thread.

    It’s a tie for the second call between esbuild.transformSync and esbuild.transform. That’s not my use case, but it makes it look like performance is never a reason to choose the sync API. (The valid reasons I can think of for the sync API are: 1) being forced to be synchronous in some context and 2) not wanting to add async/await a bunch in your code).

    Just raw spawning esbuild with childProcess.spawnSync might be a tiny bit faster, but the esbuild.transform() API is much more convenient to use. That experiment also shows how a repeated call would take the same time, while other approaches become faster.


    If this results in the docs being updated, I’d suggest looking at this sentence while at it:

    This is the most convenient option because single-threaded JavaScript code has the cleanest syntax.

    The paragraph above already says β€œBecause JavaScript is single-threaded” (which is true). What do you mean by β€œsingle-threaded JavaScript code has the cleanest syntax”? Since JavaScript is single-threaded, it basically means β€œall JavaScript code has the cleanest syntax”?

    Thanks for making esbuild! ❀️

    opened by lydell 1
  • Running postbuild script using `watch` in the JavaScript API

    Running postbuild script using `watch` in the JavaScript API

    I'm able to get the build behavior I want with the package.json scripts section looking like this:

      "scripts": {
        "build": "esbuild ./briefings/frontend/src/briefings.ts --bundle --target=es2017 --minify --outfile=./briefings/frontend/build/briefings-fe.js --analyze",
        "postbuild": "./postbuild.sh"
      }
    

    When I run yarn run build, esbuild bundles and outputs the file where I want. Yarn also runs the postbuild script, postbuild.sh which is a bash script that moves this file into place and does a few other things.

    I'm trying to make esbuild run my postbuild each time it builds while in watch mode.

    I have a working watch CLI command that looks like this:

    esbuild ./briefings/frontend/src/briefings.ts --bundle --target=es2017 --minify --outfile=./briefings/frontend/build/briefings-fe.js --analyze --watch

    This is basically the build line from above with the --watch flag, and it works well. If I edit any dependency of briefings.ts the watch rebuilds.

    However, if I understand correctly, there's no way to perform any additional actions after each watch-triggered change using the CLI.

    So I am trying to use the javascript API example, with

    build.js

    require('esbuild').build({
      entryPoints: ['./briefings/frontend/src/briefings.ts'],
      outfile: './briefings/frontend/build/briefings-fe.js',
      bundle: true,
      minify: true,
      target: 'es2017',
      watch: {
        onRebuild(error, result) {
          console.log('rebuilding');
          if (error) console.error('watch build failed:', error);
          else console.log('watch build succeeded:', result);
        },
      },
    }).then(result => {
      // Call "stop" on the result when you're done
      console.log('All done!');
      result.stop();
    });
    
    

    When I run this using node build.js it does the initial build but then exits, outputting "All done!"

    I'm expecting this to run persistently, just like the watch in the CLI. Am I not executing build.js correctly? I'm new to node.

    Once I have that, where is the best place to add a call to my postbuild.sh bash script? At the end of onRebuild?

    opened by banagale 2
  • Is there a way to use the async build API to get the locations of all output files?

    Is there a way to use the async build API to get the locations of all output files?

    I'm integrating esbuild into a broader tool, and It would be useful to me to be able to be provided the output files for both the actual JS assets, and the sourcemaps for those assets. Is that possible? need to be implemented? desired?

    atm, I'm doing this, which works for now, but feels clunky:

        let outFile = inFile + '.out';
        // convention from esbuild: https://esbuild.github.io/api/#sourcemap
        let outMapFile = outFile + '.map';
        // NOTE: the awaited return value from the build api only contains lists of errors and/or warnings
        //       (no output file info)
        await esbuild.build({
          entryPoints: [inFile],
          sourcemap: Boolean(terserOpts.sourceMap),
          outfile: outFile,
          // code is alreday as bundled as it needs to be
          bundle: false,
          minify: true,
        });
        
        // then later, I can do this: 
        let [outCode, outMap] = await Promise.all([fs.readFile(outFile, 'utf-8'), fs.readFile(outMapFile, 'utf-8')]);
    
    opened by NullVoxPopuli 2
  • How to import jquery ($) and use it from other files ?

    How to import jquery ($) and use it from other files ?

    I want to include jquery in my app/javascripts/application.js

    import $ from 'jquery';
    import './utility/direct_upload'
    import './utility/custom'
    import './utility/modals'
    import './utility/init'
    

    and use it from other file such as ./utility/custom.js etc..

    ./utility/custom.js

    $("[data-toggle='dropdown']").dropdown();
    $('[data-toggle="popover"]').popover({container: 'body'})
    $('[data-toggle="tooltip"]').tooltip()
    

    How can I do? Instead of re-imported in every file I use the $ var.

    /esbuild.config.js

    const path = require('path')
    const rails = require('esbuild-rails')
    
    require("esbuild").build({
      entryPoints: ["application.js"],
      bundle: true,
      outdir: path.join(process.cwd(), "app/assets/builds"),
      absWorkingDir: path.join(process.cwd(), "app/javascript"),
      watch: process.argv.includes("--watch"),
      plugins: [rails()],
    }).catch(() => process.exit(1))
    

    package.json

      "scripts": {
        "build": "node esbuild.config.js"
      }
    
    opened by leoplct 13
Releases(v0.13.8)
  • v0.13.8(Oct 17, 2021)

    • Fix super inside arrow function inside lowered async function (#1425)

      When an async function is transformed into a regular function for target environments that don't support async such as --target=es6, references to super inside that function must be transformed too since the async-to-regular function transformation moves the function body into a nested function, so the super references are no longer syntactically valid. However, this transform didn't handle an edge case and super references inside of an arrow function were overlooked. This release fixes this bug:

      // Original code
      class Foo extends Bar {
        async foo() {
          return () => super.foo()
        }
      }
      
      // Old output (with --target=es6)
      class Foo extends Bar {
        foo() {
          return __async(this, null, function* () {
            return () => super.foo();
          });
        }
      }
      
      // New output (with --target=es6)
      class Foo extends Bar {
        foo() {
          var __super = (key) => super[key];
          return __async(this, null, function* () {
            return () => __super("foo").call(this);
          });
        }
      }
      
    • Remove the implicit / after [dir] in entry names (#1661)

      The "entry names" feature lets you customize the way output file names are generated. The [dir] and [name] placeholders are filled in with the directory name and file name of the corresponding entry point file, respectively.

      Previously --entry-names=[dir]/[name] and --entry-names=[dir][name] behaved the same because the value used for [dir] always had an implicit trailing slash, since it represents a directory. However, some people want to be able to remove the file name with --entry-names=[dir] and the implicit trailing slash gets in the way.

      With this release, you can now use the [dir] placeholder without an implicit trailing slash getting in the way. For example, the command esbuild foo/bar/index.js --outbase=. --outdir=out --entry-names=[dir] previously generated the file out/foo/bar/.js but will now generate the file out/foo/bar.js.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.7(Oct 15, 2021)

    • Minify CSS alpha values correctly (#1682)

      When esbuild uses the rgba() syntax for a color instead of the 8-character hex code (e.g. when target is set to Chrome 61 or earlier), the 0-to-255 integer alpha value must be printed as a floating-point fraction between 0 and 1. The fraction was only printed to three decimal places since that is the minimal number of decimal places required for all 256 different alpha values to be uniquely determined. However, using three decimal places does not necessarily result in the shortest result. For example, 128 / 255 is 0.5019607843137255 which is printed as ".502" using three decimal places, but ".5" is equivalent because round(0.5 * 255) == 128, so printing ".5" would be better. With this release, esbuild will always use the minimal numeric representation for the alpha value:

      /* Original code */
      a { color: #FF800080 }
      
      /* Old output (with --minify --target=chrome61) */
      a{color:rgba(255,128,0,.502)}
      
      /* New output (with --minify --target=chrome61) */
      a{color:rgba(255,128,0,.5)}
      
    • Match node's behavior for core module detection (#1680)

      Node has a hard-coded list of core modules (e.g. fs) that, when required, short-circuit the module resolution algorithm and instead return the corresponding internal core module object. When you pass --platform=node to esbuild, esbuild also implements this short-circuiting behavior and doesn't try to bundle these import paths. This was implemented in esbuild using the existing external feature (e.g. essentially --external:fs). However, there is an edge case where esbuild's external feature behaved differently than node.

      Modules specified via esbuild's external feature also cause all sub-paths to be excluded as well, so for example --external:foo excludes both foo and foo/bar from the bundle. However, node's core module check is only an exact equality check, so for example fs is a core module and bypasses the module resolution algorithm but fs/foo is not a core module and causes the module resolution algorithm to search the file system.

      This behavior can be used to load a module on the file system with the same name as one of node's core modules. For example, require('fs/') will load the module fs from the file system instead of loading node's core fs module. With this release, esbuild will now match node's behavior in this edge case. This means the external modules that are automatically added by --platform=node now behave subtly differently than --external:, which allows code that relies on this behavior to be bundled correctly.

    • Fix WebAssembly builds on Go 1.17.2+ (#1684)

      Go 1.17.2 introduces a change (specifically a fix for CVE-2021-38297) that causes Go's WebAssembly bootstrap script to throw an error when it's run in situations with many environment variables. One such situation is when the bootstrap script is run inside GitHub Actions. This change was introduced because the bootstrap script writes a copy of the environment variables into WebAssembly memory without any bounds checking, and writing more than 4096 bytes of data ends up writing past the end of the buffer and overwriting who-knows-what. So throwing an error in this situation is an improvement. However, this breaks esbuild which previously (at least seemingly) worked fine.

      With this release, esbuild's WebAssembly bootstrap script that calls out to Go's WebAssembly bootstrap script will now delete all environment variables except for the ones that esbuild checks for, of which there are currently only four: NO_COLOR, NODE_PATH, npm_config_user_agent, and WT_SESSION. This should avoid a crash when esbuild is built using Go 1.17.2+ and should reduce the likelihood of memory corruption when esbuild is built using Go 1.17.1 or earlier. This release also updates the Go version that esbuild ships with to version 1.17.2. Note that this problem only affects the esbuild-wasm package. The esbuild package is not affected.

      See also:

      • https://github.com/golang/go/issues/48797
      • https://github.com/golang/go/issues/49011
    Source code(tar.gz)
    Source code(zip)
  • v0.13.6(Oct 14, 2021)

    • Emit decorators for declare class fields (#1675)

      In version 3.7, TypeScript introduced the declare keyword for class fields that avoids generating any code for that field:

      // TypeScript input
      class Foo {
        a: number
        declare b: number
      }
      
      // JavaScript output
      class Foo {
        a;
      }
      

      However, it turns out that TypeScript still emits decorators for these omitted fields. With this release, esbuild will now do this too:

      // TypeScript input
      class Foo {
        @decorator a: number;
        @decorator declare b: number;
      }
      
      // Old JavaScript output
      class Foo {
        a;
      }
      __decorateClass([
        decorator
      ], Foo.prototype, "a", 2);
      
      // New JavaScript output
      class Foo {
        a;
      }
      __decorateClass([
        decorator
      ], Foo.prototype, "a", 2);
      __decorateClass([
        decorator
      ], Foo.prototype, "b", 2);
      
    • Experimental support for esbuild on NetBSD (#1624)

      With this release, esbuild now has a published binary executable for NetBSD in the esbuild-netbsd-64 npm package, and esbuild's installer has been modified to attempt to use it when on NetBSD. Hopefully this makes installing esbuild via npm work on NetBSD. This change was contributed by @gdt.

      ⚠️ Note: NetBSD is not one of Node's supported platforms, so installing esbuild may or may not work on NetBSD depending on how Node has been patched. This is not a problem with esbuild. ⚠️

    • Disable the "esbuild was bundled" warning if ESBUILD_BINARY_PATH is provided (#1678)

      The ESBUILD_BINARY_PATH environment variable allows you to substitute an alternate binary executable for esbuild's JavaScript API. This is useful in certain cases such as when debugging esbuild. The JavaScript API has some code that throws an error if it detects that it was bundled before being run, since bundling prevents esbuild from being able to find the path to its binary executable. However, that error is unnecessary if ESBUILD_BINARY_PATH is present because an alternate path has been provided. This release disables the warning when ESBUILD_BINARY_PATH is present so that esbuild can be used when bundled as long as you also manually specify ESBUILD_BINARY_PATH.

      This change was contributed by @heypiotr.

    • Remove unused catch bindings when minifying (#1660)

      With this release, esbuild will now remove unused catch bindings when minifying:

      // Original code
      try {
        throw 0;
      } catch (e) {
      }
      
      // Old output (with --minify)
      try{throw 0}catch(t){}
      
      // New output (with --minify)
      try{throw 0}catch{}
      

      This takes advantage of the new optional catch binding syntax feature that was introduced in ES2019. This minification rule is only enabled when optional catch bindings are supported by the target environment. Specifically, it's not enabled when using --target=es2018 or older. Make sure to set esbuild's target setting correctly when minifying if the code will be running in an older JavaScript environment.

      This change was contributed by @sapphi-red.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.5(Oct 13, 2021)

    • Improve watch mode accuracy (#1113)

      Watch mode is enabled by --watch and causes esbuild to become a long-running process that automatically rebuilds output files when input files are changed. It's implemented by recording all calls to esbuild's internal file system interface and then invalidating the build whenever these calls would return different values. For example, a call to esbuild's internal ReadFile() function is considered to be different if either the presence of the file has changed (e.g. the file didn't exist before but now exists) or the presence of the file stayed the same but the content of the file has changed.

      Previously esbuild's watch mode operated at the ReadFile() and ReadDirectory() level. When esbuild checked whether a directory entry existed or not (e.g. whether a directory contains a node_modules subdirectory or a package.json file), it called ReadDirectory() which then caused the build to depend on that directory's set of entries. This meant the build would be invalidated even if a new unrelated entry was added or removed, since that still changes the set of entries. This is problematic when using esbuild in environments that constantly create and destroy temporary directory entries in your project directory. In that case, esbuild's watch mode would constantly rebuild as the directory was constantly considered to be dirty.

      With this release, watch mode now operates at the ReadFile() and ReadDirectory().Get() level. So when esbuild checks whether a directory entry exists or not, the build should now only depend on the presence status for that one directory entry. This should avoid unnecessary rebuilds due to unrelated directory entries being added or removed. The log messages generated using --watch will now also mention the specific directory entry whose presence status was changed if a build is invalidated for this reason.

      Note that this optimization does not apply to plugins using the watchDirs return value because those paths are only specified at the directory level and do not describe individual directory entries. You can use watchFiles or watchDirs on the individual entries inside the directory to get a similar effect instead.

    • Disallow certain uses of < in .mts and .cts files

      The upcoming version 4.5 of TypeScript is introducing the .mts and .cts extensions that turn into the .mjs and .cjs extensions when compiled. However, unlike the existing .ts and .tsx extensions, expressions that start with < are disallowed when they would be ambiguous depending on whether they are parsed in .ts or .tsx mode. The ambiguity is caused by the overlap between the syntax for JSX elements and the old deprecated syntax for type casts:

      | Syntax | .ts | .tsx | .mts/.cts | |-------------------------------|----------------------|------------------|----------------------| | <x>y | βœ… Type cast | 🚫 Syntax error | 🚫 Syntax error | | <T>() => {} | βœ… Arrow function | 🚫 Syntax error | 🚫 Syntax error | | <x>y</x> | 🚫 Syntax error | βœ… JSX element | 🚫 Syntax error | | <T>() => {}</T> | 🚫 Syntax error | βœ… JSX element | 🚫 Syntax error | | <T extends>() => {}</T> | 🚫 Syntax error | βœ… JSX element | 🚫 Syntax error | | <T extends={0}>() => {}</T> | 🚫 Syntax error | βœ… JSX element | 🚫 Syntax error | | <T,>() => {} | βœ… Arrow function | βœ… Arrow function | βœ… Arrow function | | <T extends X>() => {} | βœ… Arrow function | βœ… Arrow function | βœ… Arrow function |

      This release of esbuild introduces a syntax error for these ambiguous syntax constructs in .mts and .cts files to match the new behavior of the TypeScript compiler.

    • Do not remove empty @keyframes rules (#1665)

      CSS minification in esbuild automatically removes empty CSS rules, since they have no effect. However, empty @keyframes rules still trigger JavaScript animation events so it's incorrect to remove them. To demonstrate that empty @keyframes rules still have an effect, here is a bug report for Firefox where it was incorrectly not triggering JavaScript animation events for empty @keyframes rules: https://bugzilla.mozilla.org/show_bug.cgi?id=1004377.

      With this release, empty @keyframes rules are now preserved during minification:

      /* Original CSS */
      @keyframes foo {
        from {}
        to {}
      }
      
      /* Old output (with --minify) */
      
      /* New output (with --minify) */
      @keyframes foo{}
      

      This fix was contributed by @eelco.

    • Fix an incorrect duplicate label error (#1671)

      When labeling a statement in JavaScript, the label must be unique within the enclosing statements since the label determines the jump target of any labeled break or continue statement:

      // This code is valid
      x: y: z: break x;
      
      // This code is invalid
      x: y: x: break x;
      

      However, an enclosing label with the same name is allowed as long as it's located in a different function body. Since break and continue statements can't jump across function boundaries, the label is not ambiguous. This release fixes a bug where esbuild incorrectly treated this valid code as a syntax error:

      // This code is valid, but was incorrectly considered a syntax error
      x: (() => {
        x: break x;
      })();
      

      This fix was contributed by @nevkontakte.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.4(Oct 5, 2021)

    • Fix permission issues with the install script (#1642)

      The esbuild package contains a small JavaScript stub file that implements the CLI (command-line interface). Its only purpose is to spawn the binary esbuild executable as a child process and forward the command-line arguments to it.

      The install script contains an optimization that replaces this small JavaScript stub with the actual binary executable at install time to avoid the overhead of unnecessarily creating a new node process. This optimization can't be done at package publish time because there is only one esbuild package but there are many supported platforms, so the binary executable for the current platform must live outside of the esbuild package.

      However, the optimization was implemented with an unlink operation followed by a link operation. This means that if the first step fails, the package is left in a broken state since the JavaScript stub file is deleted but not yet replaced.

      With this release, the optimization is now implemented with a link operation followed by a rename operation. This should always leave the package in a working state even if either step fails.

    • Add a fallback for npm install esbuild --no-optional (#1647)

      The installation method for esbuild's platform-specific binary executable was recently changed in version 0.13.0. Before that version esbuild downloaded it in an install script, and after that version esbuild lets the package manager download it using the optionalDependencies feature in package.json. This change was made because downloading the binary executable in an install script never really fully worked. The reasons are complex but basically there are a variety of edge cases where people people want to install esbuild in environments that they have customized such that downloading esbuild isn't possible. Using optionalDependencies instead lets the package manager deal with it instead, which should work fine in all cases (either that or your package manager has a bug, but that's not esbuild's problem).

      There is one case where this new installation method doesn't work: if you pass the --no-optional flag to npm to disable the optionalDependencies feature. If you do this, you prevent esbuild from being installed. This is not a problem with esbuild because you are manually enabling a flag to change npm's behavior such that esbuild doesn't install correctly. However, people still want to do this.

      With this release, esbuild will now fall back to the old installation method if the new installation method fails. THIS MAY NOT WORK. The new optionalDependencies installation method is the only supported way to install esbuild with npm. The old downloading installation method was removed because it doesn't always work. The downloading method is only being provided to try to be helpful but it's not the supported installation method. If you pass --no-optional and the download fails due to some environment customization you did, the recommended fix is to just remove the --no-optional flag.

    • Support the new .mts and .cts TypeScript file extensions

      The upcoming version 4.5 of TypeScript has two new file extensions: .mts and .cts. Files with these extensions can be imported using the .mjs and .cjs, respectively. So the statement import "./foo.mjs" in TypeScript can actually succeed even if the file ./foo.mjs doesn't exist on the file system as long as the file ./foo.mts does exist. The import path with the .mjs extension is automatically re-routed to the corresponding file with the .mts extension at type-checking time by the TypeScript compiler. See the TypeScript 4.5 beta announcement for details.

      With this release, esbuild will also automatically rewrite .mjs to .mts and .cjs to .cts when resolving import paths to files on the file system. This should make it possible to bundle code written in this new style. In addition, the extensions .mts and .cts are now also considered valid TypeScript file extensions by default along with the .ts extension.

    • Fix invalid CSS minification of margin and padding (#1657)

      CSS minification does collapsing of margin and padding related properties. For example:

      /* Original CSS */
      div {
        margin: auto;
        margin-top: 5px;
        margin-left: 5px;
      }
      
      /* Minified CSS */
      div{margin:5px auto auto 5px}
      

      However, while this works for the auto keyword, it doesn't work for other keywords. For example:

      /* Original CSS */
      div {
        margin: inherit;
        margin-top: 5px;
        margin-left: 5px;
      }
      
      /* Minified CSS */
      div{margin:inherit;margin-top:5px;margin-left:5px}
      

      Transforming this to div{margin:5px inherit inherit 5px}, as was done in previous releases of esbuild, is an invalid transformation and results in incorrect CSS. This release of esbuild fixes this CSS transformation bug.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.3(Sep 28, 2021)

    • Support TypeScript type-only import/export specifiers (#1637)

      This release adds support for a new TypeScript syntax feature in the upcoming version 4.5 of TypeScript. This feature lets you prefix individual imports and exports with the type keyword to indicate that they are types instead of values. This helps tools such as esbuild omit them from your source code, and is necessary because esbuild compiles files one-at-a-time and doesn't know at parse time which imports/exports are types and which are values. The new syntax looks like this:

      // Input TypeScript code
      import { type Foo } from 'foo'
      export { type Bar }
      
      // Output JavaScript code (requires "importsNotUsedAsValues": "preserve" in "tsconfig.json")
      import {} from "foo";
      export {};
      

      See microsoft/TypeScript#45998 for full details. From what I understand this is a purely ergonomic improvement since this was already previously possible using a type-only import/export statements like this:

      // Input TypeScript code
      import type { Foo } from 'foo'
      export type { Bar }
      import 'foo'
      export {}
      
      // Output JavaScript code (requires "importsNotUsedAsValues": "preserve" in "tsconfig.json")
      import "foo";
      export {};
      

      This feature was contributed by @g-plane.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.2(Sep 23, 2021)

    • Fix export {} statements with --tree-shaking=true (#1628)

      The new --tree-shaking=true option allows you to force-enable tree shaking in cases where it wasn't previously possible. One such case is when bundling is disabled and there is no output format configured, in which case esbuild just preserves the format of whatever format the input code is in. Enabling tree shaking in this context caused a bug where export {} statements were stripped. This release fixes the bug so export {} statements should now be preserved when you pass --tree-shaking=true. This bug only affected this new functionality and didn't affect existing scenarios.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.1(Sep 23, 2021)

    • Fix the esbuild package in yarn 2+

      The yarn package manager version 2 and above has a mode called PnP that installs packages inside zip files instead of using individual files on disk, and then hijacks node's fs module to pretend that paths to files inside the zip file are actually individual files on disk so that code that wasn't written specifically for yarn still works. Unfortunately that hijacking is incomplete and it still causes certain things to break such as using these zip file paths to create a JavaScript worker thread or to create a child process.

      This was an issue for the new optionalDependencies package installation strategy that was just released in version 0.13.0 since the binary executable is now inside of an installed package instead of being downloaded using an install script. When it's installed with yarn 2+ in PnP mode the binary executable is inside a zip file and can't be run. To work around this, esbuild detects yarn's PnP mode and copies the binary executable to a real file outside of the zip file.

      Unfortunately the code to do this didn't create the parent directory before writing to the file path. That caused esbuild's API to crash when it was run for the first time. This didn't come up during testing because the parent directory already existed when the tests were run. This release changes the location of the binary executable from a shared cache directory to inside the esbuild package itself, which should fix this crash. This problem only affected esbuild's JS API when it was run through yarn 2+ with PnP mode active.

    Source code(tar.gz)
    Source code(zip)
  • v0.13.0(Sep 22, 2021)

    This release contains backwards-incompatible changes. Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as recommended by npm). You should either be pinning the exact version of esbuild in your package.json file or be using a version range syntax that only accepts patch upgrades such as ~0.12.0. See the documentation about semver for more information.

    • Allow tree shaking to be force-enabled and force-disabled (#1518, #1610, #1611, #1617)

      This release introduces a breaking change that gives you more control over when tree shaking happens ("tree shaking" here refers to declaration-level dead code removal). Previously esbuild's tree shaking was automatically enabled or disabled for you depending on the situation and there was no manual override to change this. Specifically, tree shaking was only enabled either when bundling was enabled or when the output format was set to iife (i.e. wrapped in an immediately-invoked function expression). This was done to avoid issues with people appending code to output files in the cjs and esm formats and expecting that code to be able to reference code in the output file that isn't otherwise referenced.

      You now have the ability to explicitly force-enable or force-disable tree shaking to bypass this default behavior. This is a breaking change because there is already a setting for tree shaking that does something else, and it has been moved to a separate setting instead. The previous setting allowed you to control whether or not to ignore manual side-effect annotations, which is related to tree shaking since only side-effect free code can be removed as dead code. Specifically you can annotate function calls with /* @__PURE__ */ to indicate that they can be removed if they are not used, and you can annotate packages with "sideEffects": false to indicate that imports of that package can be removed if they are not used. Being able to ignore these annotations is necessary because they are sometimes incorrect. This previous setting has been moved to a separate setting because it actually impacts dead-code removal within expressions, which also applies when minifying with tree-shaking disabled.

      Old behavior

      • CLI
        • Ignore side-effect annotations: --tree-shaking=ignore-annotations
      • JS
        • Ignore side-effect annotations: treeShaking: 'ignore-annotations'
      • Go
        • Ignore side-effect annotations: TreeShaking: api.TreeShakingIgnoreAnnotations

      New behavior

      • CLI
        • Ignore side-effect annotations: --ignore-annotations
        • Force-disable tree shaking: --tree-shaking=false
        • Force-enable tree shaking: --tree-shaking=true
      • JS
        • Ignore side-effect annotations: ignoreAnnotations: true
        • Force-disable tree shaking: treeShaking: false
        • Force-enable tree shaking: treeShaking: true
      • Go
        • Ignore side-effect annotations: IgnoreAnnotations: true
        • Force-disable tree shaking: TreeShaking: api.TreeShakingFalse
        • Force-enable tree shaking: TreeShaking: api.TreeShakingTrue
    • The npm package now uses optionalDependencies to install the platform-specific binary executable (#286, #291, #319, #347, #369, #547, #565, #789, #921, #1193, #1270, #1382, #1422, #1450, #1485, #1546, #1547, #1574, #1609)

      This release changes esbuild's installation strategy in an attempt to improve compatibility with edge cases such as custom registries, custom proxies, offline installations, read-only file systems, or when post-install scripts are disabled. It's being treated as a breaking change out of caution because it's a significant change to how esbuild works with JS package managers, and hasn't been widely tested yet.

      The old installation strategy manually downloaded the correct binary executable in a post-install script. The binary executable is hosted in a separate platform-specific npm package such as esbuild-darwin-64. The install script first attempted to download the package via the npm command in case npm had custom network settings configured. If that didn't work, the install script attempted to download the package from https://registry.npmjs.org/ before giving up. This was problematic for many reasons including:

      • Not all of npm's settings can be forwarded due to npm bugs such as https://github.com/npm/cli/issues/2284, and npm has said these bugs will never be fixed.
      • Some people have configured their network environments such that downloading from https://registry.npmjs.org/ will hang instead of either succeeding or failing.
      • The installed package was broken if you used npm --ignore-scripts because then the post-install script wasn't run. Some people enable this option so that malicious packages must be run first before being able to do malicious stuff.

      The new installation strategy automatically downloads the correct binary executable using npm's optionalDependencies feature to depend on all esbuild packages for all platforms but only have the one for the current platform be installed. This is a built-in part of the package manager so my assumption is that it should work correctly in all of these edge cases that currently don't work. And if there's an issue with this, then the problem is with the package manager instead of with esbuild so this should hopefully reduce the maintenance burden on esbuild itself. Changing to this installation strategy has these drawbacks:

      • Old versions of certain package managers (specifically npm and yarn) print lots of useless log messages during the installation, at least one for each platform other than the current one. These messages are harmless and can be ignored. However, they are annoying. There is nothing I can do about this. If you have this problem, one solution is to upgrade your package manager to a newer version.

      • Installation will be significantly slower in old versions of npm, old versions of pnpm, and all versions of yarn. These package managers download all packages for all platforms even though they aren't needed and actually cannot be used. This problem has been fixed in npm and pnpm and the problem has been communicated to yarn: https://github.com/yarnpkg/berry/issues/3317. If you have this problem, one solution is to use a newer version of npm or pnpm as your package manager.

      • This installation strategy does not work if you use npm --no-optional since then the package with the binary executable is not installed. If you have this problem, the solution is to not pass the --no-optional flag when installing packages.

      • There is still a small post-install script but it's now optional in that the esbuild package should still function correctly if post-install scripts are disabled (such as with npm --ignore-scripts). This post-install script optimizes the installed package by replacing the esbuild JavaScript command shim with the actual binary executable at install time. This avoids the overhead of launching another node process when using the esbuild command. So keep in mind that installing with --ignore-scripts will result in a slower esbuild command.

      Despite the drawbacks of the new installation strategy, I believe this change is overall a good thing to move forward with. It should fix edge case scenarios where installing esbuild currently doesn't work at all, and this only comes at the expense of the install script working in a less-optimal way (but still working) if you are using an old version of npm. So I'm going to switch installation strategies and see how it goes.

      The platform-specific binary executables are still hosted on npm in the same way, so anyone who wrote code that downloads builds from npm using the instructions here should not have to change their code: https://esbuild.github.io/getting-started/#download-a-build. However, note that these platform-specific packages no longer specify the bin field in package.json so the esbuild command will no longer be automatically put on your path. The bin field had to be removed because of a collision with the bin field of the esbuild package (now that the esbuild package depends on all of these platform-specific packages as optional dependencies).

    In addition to the breaking changes above, the following features are also included in this release:

    • Treat x guarded by typeof x !== 'undefined' as side-effect free

      This is a small tree-shaking (i.e. dead code removal) improvement. Global identifier references are considered to potentially have side effects since they will throw a reference error if the global identifier isn't defined, and code with side effects cannot be removed as dead code. However, there's a somewhat-common case where the identifier reference is guarded by a typeof check to check that it's defined before accessing it. With this release, code that does this will now be considered to have no side effects which allows it to be tree-shaken:

      // Original code
      var __foo = typeof foo !== 'undefined' && foo;
      var __bar = typeof bar !== 'undefined' && bar;
      console.log(__bar);
      
      // Old output (with --bundle, which enables tree-shaking)
      var __foo = typeof foo !== 'undefined' && foo;
      var __bar = typeof bar !== 'undefined' && bar;
      console.log(__bar);
      
      // New output (with --bundle, which enables tree-shaking)
      var __bar = typeof bar !== 'undefined' && bar;
      console.log(__bar);
      
    Source code(tar.gz)
    Source code(zip)
  • v0.12.29(Sep 22, 2021)

    • Fix compilation of abstract class fields in TypeScript (#1623)

      This release fixes a bug where esbuild could incorrectly include a TypeScript abstract class field in the compiled JavaScript output. This is incorrect because the official TypeScript compiler never does this. Note that this only happened in scenarios where TypeScript's useDefineForClassFields setting was set to true (or equivalently where TypeScript's target setting was set to ESNext). Here is the difference:

      // Original code
      abstract class Foo {
        abstract foo: any;
      }
      
      // Old output
      class Foo {
        foo;
      }
      
      // New output
      class Foo {
      }
      
    • Proxy from the __require shim to require (#1614)

      Some background: esbuild's bundler emulates a CommonJS environment. The bundling process replaces the literal syntax require(<string>) with the referenced module at compile-time. However, other uses of require such as require(someFunction()) are not bundled since the value of someFunction() depends on code evaluation, and esbuild does not evaluate code at compile-time. So it's possible for some references to require to remain after bundling.

      This was causing problems for some CommonJS code that was run in the browser and that expected typeof require === 'function' to be true (see #1202), since the browser does not provide a global called require. Thus esbuild introduced a shim require function called __require (shown below) and replaced all references to require in the bundled code with __require:

      var __require = x => {
        if (typeof require !== 'undefined') return require(x);
        throw new Error('Dynamic require of "' + x + '" is not supported');
      };
      

      However, this broke code that referenced require.resolve inside the bundle, which could hypothetically actually work since you could assign your own implementation to window.require.resolve (see #1579). So the implementation of __require was changed to this:

      var __require = typeof require !== 'undefined' ? require : x => {
        throw new Error('Dynamic require of "' + x + '" is not supported');
      };
      

      However, that broke code that assigned to window.require later on after the bundle was loaded (#1614). So with this release, the code for __require now handles all of these edge cases:

      • typeof require is still function even if window.require is undefined
      • window.require can be assigned to either before or after the bundle is loaded
      • require.resolve and arbitrary other properties can still be accessed
      • require will now forward any number of arguments, not just the first one

      Handling all of these edge cases is only possible with the Proxy API. So the implementation of __require now looks like this:

      var __require = (x =>
        typeof require !== 'undefined' ? require :
        typeof Proxy !== 'undefined' ? new Proxy(x, {
          get: (a, b) => (typeof require !== 'undefined' ? require : a)[b]
        }) : x
      )(function(x) {
        if (typeof require !== 'undefined') return require.apply(this, arguments);
        throw new Error('Dynamic require of "' + x + '" is not supported');
      });
      
    • Consider typeof x to have no side effects

      The typeof operator does not itself trigger any code evaluation so it can safely be removed if evaluating the operand does not cause any side effects. However, there is a special case of the typeof operator when the operand is an identifier expression. In that case no reference error is thrown if the referenced symbol does not exist (e.g. typeof x does not throw an error if there is no symbol named x). With this release, esbuild will now consider typeof x to have no side effects even if evaluating x would have side effects (i.e. would throw a reference error):

      // Original code
      var unused = typeof React !== 'undefined';
      
      // Old output
      var unused = typeof React !== 'undefined';
      
      // New output
      

      Note that there is actually an edge case where typeof x can throw an error: when x is being referenced inside of its TDZ, or temporal dead zone (i.e. before it's declared). This applies to let, const, and class symbols. However, esbuild doesn't currently handle TDZ rules so the possibility of errors thrown due to TDZ rules is not currently considered. This typically doesn't matter in real-world code so this hasn't been a priority to fix (and is actually tricky to fix with esbuild's current bundling approach). So esbuild may incorrectly remove a typeof expression that actually has side effects. However, esbuild already incorrectly did this in previous releases so its behavior regarding typeof and TDZ rules hasn't changed in this release.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.28(Sep 14, 2021)

    • Fix U+30FB and U+FF65 in identifier names in ES5 vs. ES6+ (#1599)

      The ES6 specification caused two code points that were previously valid in identifier names in ES5 to no longer be valid in identifier names in ES6+. The two code points are:

      • U+30FB i.e. KATAKANA MIDDLE DOT i.e. ・
      • U+FF65 i.e. HALFWIDTH KATAKANA MIDDLE DOT i.e. ο½₯

      This means that using ES6+ parsing rules will fail to parse some valid ES5 code, and generating valid ES5 code may fail to be parsed using ES6+ parsing rules. For example, esbuild would previously fail to parse x.yο½₯ even though it's valid ES5 code (since it's not valid ES6+ code) and esbuild could generate {yο½₯:x} when minifying even though it's not valid ES6+ code (since it's valid ES5 code). This problem is the result of my incorrect assumption that ES6 is a superset of ES5.

      As of this release, esbuild will now parse a superset of ES5 and ES6+ and will now quote identifier names when possible if it's not considered to be a valid identifier name in either ES5 or ES6+. In other words, a union of ES5 and ES6 rules is used for parsing and the intersection of ES5 and ES6 rules is used for printing.

    • Fix ++ and -- on class private fields when used with big integers (#1600)

      Previously when esbuild lowered class private fields (e.g. #foo) to older JavaScript syntax, the transform of the ++ and -- was not correct if the value is a big integer such as 123n. The transform in esbuild is similar to Babel's transform which has the same problem. Specifically, the code was transformed into code that either adds or subtracts the number 1 and 123n + 1 throws an exception in JavaScript. This problem has been fixed so this should now work fine starting with this release.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.27(Sep 13, 2021)

    • Update JavaScript syntax feature compatibility tables (#1594)

      Most JavaScript syntax feature compatibility data is able to be obtained automatically via https://kangax.github.io/compat-table/. However, they are missing data for quite a few new JavaScript features (see (kangax/compat-table#1034)) so data on these new features has to be added manually. This release manually adds a few new entries:

      • Top-level await

        This feature lets you use await at the top level of a module, outside of an async function. Doing this holds up the entire module instantiation operation until the awaited expression is resolved or rejected. This release marks this feature as supported in Edge 89, Firefox 89, and Safari 15 (it was already marked as supported in Chrome 89 and Node 14.8). The data source for this is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await.

      • Arbitrary module namespace identifier names

        This lets you use arbitrary strings as module namespace identifier names as long as they are valid UTF-16 strings. An example is export { x as "πŸ•" } which can then be imported as import { "πŸ•" as y } from "./example.js". This release marks this feature as supported in Firefox 87 (it was already marked as supported in Chrome 90 and Node 16). The data source for this is https://bugzilla.mozilla.org/show_bug.cgi?id=1670044.

      I would also like to add data for Safari. They have recently added support for arbitrary module namespace identifier names (https://bugs.webkit.org/show_bug.cgi?id=217576) and export * as (https://bugs.webkit.org/show_bug.cgi?id=214379). However, I have no idea how to determine which Safari release these bugs correspond to so this compatibility data for Safari has been omitted.

    • Avoid unnecessary additional log messages after the server is stopped (#1589)

      There is a development server built in to esbuild which is accessible via the serve() API call. This returns a promise that resolves to an object with a stop() method that immediately terminates the development server. Previously calling this could cause esbuild to print stray log messages since stop() could cause plugins to be unregistered while a build is still in progress. With this release, calling stop() no longer terminates the development server immediately. It now waits for any active builds to finish first so the builds are not interrupted and left in a confusing state.

    • Fix an accidental dependency on Go β‰₯1.17.0 (#1585)

      The source code of this release no longer uses the math.MaxInt constant that was introduced in Go version 1.17.0. This constant was preventing esbuild from being compiled on Go version <1.17.0. This fix was contributed by @davezuko.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.26(Sep 9, 2021)

    • Add --analyze to print information about the bundle (#1568)

      The --metafile= flag tells esbuild to write information about the bundle into the provided metadata file in JSON format. It contains information about the input files and which other files each one imports, as well as the output files and which input files they include. This information is sufficient to answer many questions such as:

      • Which files are in my bundle?
      • What's are the biggest files in my bundle?
      • Why is this file included in my bundle?

      Previously you had to either write your own code to answer these questions, or use another tool such as https://bundle-buddy.com/esbuild to visualize the data. Starting with this release you can now also use --analyze to enable esbuild's built-in visualizer. It looks like this:

      $ esbuild --bundle example.jsx --outfile=out.js --minify --analyze
      
        out.js  27.6kb
      
      ⚑ Done in 6ms
      
        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                                     965b     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%
      

      This tells you what input files were bundled into each output file as well as the final minified size contribution of each input file as well as the percentage of the output file it takes up. You can also enable verbose analysis with --analyze=verbose to see why each input file was included (i.e. which files imported it from the entry point file):

      $ esbuild --bundle example.jsx --outfile=out.js --minify --analyze=verbose
      
        out.js  27.6kb
      
      ⚑ Done in 6ms
      
        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 ──────────────────────────────────── 965b ──── 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
      

      There is also a JS API for this:

      const result = await esbuild.build({
        metafile: true,
        ...
      })
      console.log(await esbuild.analyzeMetafile(result.metafile, {
        verbose: true,
      }))
      

      and a Go API:

      result := api.Build(api.BuildOptions{
        Metafile: true,
        ...
      })
      fmt.Println(api.AnalyzeMetafile(result.Metafile, api.AnalyzeMetafileOptions{
        Verbose: true,
      }))
      

      Note that this is not the only way to visualize this data. If you want a visualization that's different than the information displayed here, you can easily build it yourself using the information in the metafile that is generated with the --metafile= flag.

      Also note that this data is intended for humans, not machines. The specific format of this data may change over time which will likely break any tools that try to parse it. You should not write a tool to parse this data. You should be using the information in the JSON metadata file instead. Everything in this visualization is derived from the JSON metadata so you are not losing out on any information by not using esbuild's output.

    • Allow require.resolve in non-node builds (#1579)

      With this release, you can now use require.resolve in builds when the target platform is set to browser instead of node as long as the function window.require.resolve exists somehow. This was already possible when the platform is node but when the platform is browser, esbuild generates a no-op shim require function for compatibility reasons (e.g. because some code expects typeof require must be "function" even in the browser). The shim previously had a fallback to window.require if it exists, but additional properties of the require function such as require.resolve were not copied over to the shim. Now the shim function is only used if window.require is undefined so additional properties such as require.resolve should now work.

      This change was contributed by @screetBloom.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.25(Sep 2, 2021)

    • Fix a TypeScript parsing edge case with the postfix ! operator (#1560)

      This release fixes a bug with esbuild's TypeScript parser where the postfix ! operator incorrectly terminated a member expression after the new operator:

      // Original input
      new Foo!.Bar();
      
      // Old output
      new Foo().Bar();
      
      // New output
      new Foo.Bar();
      

      The problem was that ! was considered a postfix operator instead of part of a member expression. It is now considered to be part of a member expression instead, which fixes this edge case.

    • Fix a parsing crash with nested private brand checks

      This release fixes a bug in the parser where code of the form #a in #b in c caused a crash. This code now causes a syntax error instead. Private identifiers are allowed when followed by in, but only if the operator precedence level is such that the in operator is allowed. The parser was missing the operator precedence check.

    • Publish x86-64 binary executables for illumos (#1562)

      This release adds support for the illumos operating system, which is related to Solaris and SunOS. Support for this platform was contributed by @hadfl.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.24(Aug 27, 2021)

    • Fix an edge case with direct eval and variable renaming

      Use of the direct eval construct causes all variable names in the scope containing the direct eval and all of its parent scopes to become "pinned" and unable to be renamed. This is because the dynamically-evaluated code is allowed to reference any of those variables by name. When this happens esbuild avoids renaming any of these variables, which effectively disables minification for most of the file, and avoids renaming any non-pinned variables to the name of a pinned variable.

      However, there was previously a bug where the pinned variable name avoidance only worked for pinned variables in the top-level scope but not in nested scopes. This could result in a non-pinned variable being incorrectly renamed to the name of a pinned variable in certain cases. For example:

      // Input to esbuild
      return function($) {
        function foo(arg) {
          return arg + $;
        }
        // Direct "eval" here prevents "$" from being renamed
        // Repeated "$" puts "$" at the top of the character frequency histogram
        return eval(foo($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$))
      }(2);
      

      When this code is minified with --minify-identifiers, the non-pinned variable arg is incorrectly transformed into $ resulting in a name collision with the nested pinned variable $:

      // Old output from esbuild (incorrect)
      return function($) {
        function foo($) {
          return $ + $;
        }
        return eval(foo($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$));
      }(2);
      

      This is because the non-pinned variable arg is renamed to the top character in the character frequency histogram $ (esbuild uses a character frequency histogram for smaller gzipped output sizes) and the pinned variable $ was incorrectly not present in the list of variable names to avoid. With this release, the output is now correct:

      // New output from esbuild (correct)
      return function($) {
        function foo(n) {
          return n + $;
        }
        return eval(foo($$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$));
      }(2);
      

      Note that even when esbuild handles direct eval correctly, using direct eval is not recommended because it disables minification for the file and likely won't work correctly in the presence of scope hoisting optimizations. See https://esbuild.github.io/link/direct-eval for more details.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.23(Aug 26, 2021)

    • Parsing of rest arguments in certain TypeScript types (#1553)

      This release implements parsing of rest arguments inside object destructuring inside arrow functions inside TypeScript type declarations. Support for rest arguments in this specific syntax was not previously implemented. The following code was incorrectly considered a syntax error before this release, but is no longer considered a syntax error:

      type F = ({ ...rest }) => void;
      
    • Fix error message for watch: true and buildSync (#1552)

      Watch mode currently only works with the build API. Previously using watch mode with the buildSync API caused a confusing error message. This release explicitly disallows doing this, so the error message is now more clear.

    • Fix an minification bug with the --keep-names option (#1552)

      This release fixes a subtle bug that happens with --keep-names --minify and nested function declarations in strict mode code. It can be triggered by the following code, which was being compiled incorrectly under those flags:

      export function outer() {
        {
          function inner() {
            return Math.random();
          }
          const x = inner();
          console.log(x);
        }
      }
      outer();
      

      The bug was caused by an unfortunate interaction between a few of esbuild's behaviors:

      1. Function declarations inside of nested scopes behave differently in different situations, so esbuild rewrites this function declaration to a local variable initialized to a function expression instead so that it behaves the same in all situations.

        More specifically, the interpretation of such function declarations depends on whether or not it currently exists in a strict mode context:

        > (function(){ { function x(){} } return x })()
        function x() {}
        
        > (function(){ 'use strict'; { function x(){} } return x })()
        ❌ Uncaught ReferenceError: x is not defined
        

        The bundling process sometimes erases strict mode context. For example, different files may have different strict mode status but may be merged into a single file which all shares the same strict mode status. Also, files in ESM format are automatically in strict mode but a bundle output file in IIFE format may not be executed in strict mode. Transforming the nested function to a let in strict mode and a var in non-strict mode means esbuild's output will behave reliably in different environments.

      2. The "keep names" feature adds automatic calls to the built-in __name helper function to assign the original name to the .name property of the minified function object at run-time. That transforms the code into this:

        let inner = function() {
          return Math.random();
        };
        __name(inner, "inner");
        const x = inner();
        console.log(x);
        

        This injected helper call does not count as a use of the associated function object so that dead-code elimination will still remove the function object as dead code if nothing else uses it. Otherwise dead-code elimination would stop working when the "keep names" feature is enabled.

      3. Minification enables an optimization where an initialized variable with a single use immediately following that variable is transformed by inlining the initializer into the use. So for example var a = 1; return a is transformed into return 1. This code matches this pattern (initialized single-use variable + use immediately following that variable) so the optimization does the inlining, which transforms the code into this:

        __name(function() {
          return Math.random();
        }, "inner");
        const x = inner();
        console.log(x);
        

        The code is now incorrect because inner actually has two uses, although only one was actually counted.

      This inlining optimization will now be avoided in this specific case, which fixes the bug without regressing dead-code elimination or initialized variable inlining in any other cases.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.22(Aug 21, 2021)

    • Make HTTP range requests more efficient (#1536)

      The local HTTP server built in to esbuild supports range requests, which are necessary for video playback in Safari. This means you can now use <video> tags in your HTML pages with esbuild's local HTTP server.

      Previously this was implemented inefficiently for files that aren't part of the build, but that are read from the underlying fallback directory. In that case the entire file was being read even though only part of the file was needed. In this release, only the part of the file that is needed is read so using HTTP range requests with esbuild in this case will now use less memory.

    • Fix CSS minification bug with box-shadow and var() (#1538)

      The box-shadow property can be specified using 2, 3, or 4 numbers. The 3rd and 4th numbers are the blur radius and spread radius, and can be omitted if zero. When minifying, esbuild has an optimization that removes trailing zeros from runs of numbers within the box-shadow property. However, that optimization is not correct in the presence of tokens that are neither a number, a color, nor the token insert. These edge cases include var() or calc() tokens. With this release, esbuild will now do stronger validation and will only remove trailing zeros if the contents of the box-shadow property matches the underlying CSS grammar exactly.

      /* Original code */
      button {
        box-shadow: 0 0 0 var(--spread) red;
      }
      
      /* Old minified output */
      button{box-shadow:0 0 var(--spread) red}
      
      /* New minified output */
      button{box-shadow:0 0 0 var(--spread) red}
      
    Source code(tar.gz)
    Source code(zip)
  • v0.12.21(Aug 18, 2021)

  • v0.12.20(Aug 12, 2021)

    • Avoid the sequence </style in CSS output (#1509)

      The CSS code generator now avoids generating the character sequence </style in case you want to embed the CSS output in a <style>...</style> tag inside HTML:

      /* Original code */
      a:after {
        content: "</style>";
      }
      
      /* Old output */
      a:after {
        content: "</style>";
      }
      
      /* New output */
      a:after {
        content: "<\/style>";
      }
      

      This mirrors how the JS code generator similarly avoids the character sequence </script.

      In addition, the check that escapes </style and </script is now case-insensitive to match how the browser's HTML parser behaves. So </STYLE and </SCRIPT are now escaped as well.

    • Fix a TypeScript parsing edge case with ASI (Automatic Semicolon Insertion) (#1512)

      This fixes a parsing bug where TypeScript types consisting of multiple identifiers joined together with a . could incorrectly extend onto the next line if the next line started with <. This problem was due to ASI; esbuild should be automatically inserting a semicolon at the end of the line:

      let x: {
        <A extends B>(): c.d /* A semicolon should be automatically inserted here */
        <E extends F>(): g.h
      }
      

      Previously the above code was incorrectly considered a syntax error since esbuild attempted to parse the parameterized type c.d<E extends F ? ...>. With this release, this code is now parsed correctly.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.19(Aug 7, 2021)

    • Add support for CSS source maps (#519)

      With this release, esbuild will now generate source maps for CSS output files when --sourcemap is enabled. This supports all of the same options as JS source maps including --sourcemap=inline and --sourcemap=external. In addition, CSS input files with embedded /*# sourceMappingURL=... */ comments will cause the CSS output file source map to map all the way back to the original inputs. CSS source maps are used by the browser's style inspector to link back to the original source code instead of linking to the bundled source code.

    • Fix computed class fields in TypeScript edge case (#1507)

      If TypeScript code contains computed class fields, the target environment supports class fields so syntax lowering is not necessary, and TypeScript's useDefineForClassFields setting is set to true, then esbuild had a bug where the computed property names were computed after the class definition and were undefined. Note that TypeScript's useDefineForClassFields setting defaults to true if tsconfig.json contains "target": "ESNext".

      // Original code
      class Foo {
        [foo] = 1;
        @bar [baz] = 2;
      }
      
      // Old output
      var _a, _b;
      var Foo = class {
        [_a] = 1;
        [_b] = 2;
      };
      _a = foo, _b = baz;
      __decorateClass([
        bar
      ], Foo.prototype, _b, 2);
      
      // New output
      var _a;
      var Foo = class {
        [foo] = 1;
        [_a = baz] = 2;
      };
      __decorateClass([
        bar
      ], Foo.prototype, _a, 2);
      

      The problem in this case is that normally TypeScript moves class field initializers into the special constructor method (automatically generating one if one doesn't already exist) so the side effects for class field property names must happen after the class body. But if class fields are supported by the target environment then the side effects must happen inline instead.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.18(Aug 5, 2021)

    • Allow implicit ./ in CSS @import paths (#1494)

      In the browser, the paths inside CSS @import rules are implicitly relative to the path of the current CSS style sheet. Previously esbuild used node's JS path resolution rules in CSS as well, which required a ./ or ../ prefix for a path to be considered a relative path. Paths without that prefix are considered package paths and are searched for inside node_modules instead.

      With this release, esbuild will now first try to interpret the path as a relative path and then fall back to interpreting it as a package path if nothing exists at that relative path. This feature was originally added in version 0.7.18 but only worked for CSS url() tokens. In this release it now also works for @import rules.

      This feature was contributed by @pd4d10.

    • Fix lowering of nullish coalescing assignment edge case (#1493)

      This release fixes a bug where lowering of the ??= nullish coalescing assignment operator failed when the target environment supported nullish coalescing and private class fields but not nullish coalescing assignment. An example target environment with this specific feature support matrix combination is node 14.8. This edge case is now lowered correctly:

      // Original code
      class A {
        #a;
        f() {
          this.#a ??= 1;
        }
      }
      
      // Old output (with --target=node14.8)
      panic: Unexpected expression of type *js_ast.EPrivateIdentifier
      
      // New output (with --target=node14.8)
      class A {
        #a;
        f() {
          this.#a ?? (this.#a = 1);
        }
      }
      
    • Fix public fields being inserted before super() call (#1497)

      The helper function that esbuild uses to emulate the new public class field syntax can potentially be inserted into the class constructor before the super() call. That is problematic because the helper function makes use of this, and this must only be used after the super() call. This release fixes a case where this happens when minification is enabled:

      // Original code
      class A extends B {
        x;
        constructor() {
          f();
          super();
        }
      }
      
      // Old output (with --minify-syntax --target=es6)
      class A extends B {
        constructor() {
          __publicField(this, "x");
          f(), super();
        }
      }
      
      // New output (with --minify-syntax --target=es6)
      class A extends B {
        constructor() {
          f();
          super();
          __publicField(this, "x");
        }
      }
      
    • Fix lowering of static private methods in class expressions (#1498)

      Previously static private methods were lowered incorrectly when present in class expressions. The class expression itself was missing in the output due to an oversight (variable shadowing). This issue has been fixed:

      // Original code
      (class {
        static #x() {}
      });
      
      // Old output (with --target=es6)
      var _x, _a, x_fn;
      __privateAdd(_a, _x), _x = new WeakSet(), x_fn = function() {
      }, __privateAdd(_a, _x), _a;
      
      // New output (with --target=es6)
      var _x, _a, x_fn;
      _a = class {
      }, _x = new WeakSet(), x_fn = function() {
      }, __privateAdd(_a, _x), _a;
      
    Source code(tar.gz)
    Source code(zip)
  • v0.12.17(Jul 29, 2021)

    • Fix a bug with private fields and logical assignment operators (#1418)

      This release fixes a bug where code using private fields in combination with logical assignment operators was transformed incorrectly if the target environment supported logical assignment operators but not private fields. Since logical assignment operators are assignment operators, the entire operator must be transformed even if the operator is supported. This should now work correctly:

      // Original code
      class Foo {
        #x
        foo() {
          this.#x &&= 2
          this.#x ||= 2
          this.#x ??= 2
        }
      }
      
      // Old output
      var _x;
      class Foo {
        constructor() {
          __privateAdd(this, _x, void 0);
        }
        foo() {
          this._x &&= 2;
          this._x ||= 2;
          this._x ??= 2;
        }
      }
      _x = new WeakMap();
      
      // New output
      var _x, _a;
      class Foo {
        constructor() {
          __privateAdd(this, _x, void 0);
        }
        foo() {
          __privateGet(this, _x) && __privateSet(this, _x, 2);
          __privateGet(this, _x) || __privateSet(this, _x, 2);
          __privateGet(this, _x) ?? __privateSet(this, _x, 2);
        }
      }
      _x = new WeakMap();
      
    • Fix a hoisting bug in the bundler (#1455)

      This release fixes a bug where variables declared using var inside of top-level for loop initializers were not hoisted inside lazily-initialized ES modules (such as those that are generated when bundling code that loads an ES module using require). This meant that hoisted function declarations incorrectly didn't have access to these loop variables:

      // entry.js
      console.log(require('./esm-file').test())
      
      // esm-file.js
      for (var i = 0; i < 10; i++) ;
      export function test() { return i }
      

      Old output (incorrect):

      // esm-file.js
      var esm_file_exports = {};
      __export(esm_file_exports, {
        test: () => test
      });
      function test() {
        return i;
      }
      var init_esm_file = __esm({
        "esm-file.js"() {
          for (var i = 0; i < 10; i++)
            ;
        }
      });
      
      // entry.js
      console.log((init_esm_file(), esm_file_exports).test());
      

      New output (correct):

      // esm-file.js
      var esm_file_exports = {};
      __export(esm_file_exports, {
        test: () => test
      });
      function test() {
        return i;
      }
      var i;
      var init_esm_file = __esm({
        "esm-file.js"() {
          for (i = 0; i < 10; i++)
            ;
        }
      });
      
      // entry.js
      console.log((init_esm_file(), esm_file_exports).test());
      
    • Fix a code generation bug for private methods (#1424)

      This release fixes a bug where when private methods are transformed and the target environment is one that supports private methods (such as esnext), the member function name was uninitialized and took on the zero value by default. This resulted in the member function name becoming __create instead of the correct name since that's the name of the symbol at index 0. Now esbuild always generates a private method symbol even when private methods are supported, so this is no longer an issue:

      // Original code
      class Foo {
        #a() { return 'a' }
        #b() { return 'b' }
        static c
      }
      
      // Old output
      var _a, __create, _b, __create;
      var Foo = class {
        constructor() {
          __privateAdd(this, _a);
          __privateAdd(this, _b);
        }
      };
      _a = new WeakSet();
      __create = function() {
        return "a";
      };
      _b = new WeakSet();
      __create = function() {
        return "b";
      };
      __publicField(Foo, "c");
      
      // New output
      var _a, a_fn, _b, b_fn;
      var Foo = class {
        constructor() {
          __privateAdd(this, _a);
          __privateAdd(this, _b);
        }
      };
      _a = new WeakSet();
      a_fn = function() {
        return "a";
      };
      _b = new WeakSet();
      b_fn = function() {
        return "b";
      };
      __publicField(Foo, "c");
      
    • The CLI now stops watch and serve mode when stdin is closed (#1449)

      To facilitate esbuild being called from the Erlang VM, esbuild's command-line interface will now exit when in --watch or --serve mode if stdin is closed. This change is necessary because the Erlang VM doesn't have an API for terminating a child process, so it instead closes stdin to indicate that the process is no longer needed.

      Note that this only happens when stdin is not a TTY (i.e. only when the CLI is being used non-interactively) to avoid disrupting the use case of manually moving esbuild to a background job using a Unix terminal.

      This change was contributed by @josevalim.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.16(Jul 26, 2021)

    • Remove warning about bad CSS @-rules (#1426)

      The CSS bundler built in to esbuild is only designed with real CSS in mind. Running other languages that compile down to CSS through esbuild without compiling them down to CSS first can be a bad idea since esbuild applies browser-style error recovery to invalid syntax and uses browser-style import order that other languages might not be expecting. This is why esbuild previously generated warnings when it encountered unknown CSS @-rules.

      However, some people want to run other non-CSS languages through esbuild's CSS bundler anyway. So with this release, esbuild will no longer generate any warnings if you do this. But keep in mind that doing this is still potentially unsafe. Depending on the input language, using esbuild's CSS bundler to bundle non-CSS code can still potentially alter the semantics of your code.

    • Allow ES2021 in tsconfig.json (#1470)

      TypeScript recently added support for ES2021 in tsconfig.json so esbuild now supports this too. This has the same effect as if you passed --target=es2021 to esbuild. Keep in mind that the value of target in tsconfig.json is only respected if you did not pass a --target= value to esbuild.

    • Avoid using the worker_threads optimization in certain old node versions (#1462)

      The worker_threads optimization makes esbuild's synchronous API calls go much faster than they would otherwise. However, it turns out this optimization cannot be used in certain node versions older than v12.17.0, where node throws an error when trying to create the worker. This optimization is now disabled in these scenarios.

      Note that these old node versions are currently in maintenance. I recommend upgrading to a modern version of node if run-time performance is important to you.

    • Paths starting with node: are implicitly external when bundling for node (#1466)

      This replicates a new node feature where you can prefix an import path with node: to load a native node module by that name (such as import fs from "node:fs/promises"). These paths also have special behavior:

      Core modules can also be identified using the node: prefix, in which case it bypasses the require cache. For instance, require('node:http') will always return the built in HTTP module, even if there is require.cache entry by that name.

      With this release, esbuild's built-in resolver will now automatically consider all import paths starting with node: as external. This new behavior is only active when the current platform is set to node such as with --platform=node. If you need to customize this behavior, you can write a plugin to intercept these paths and treat them differently.

    • Consider \ and / to be the same in file paths (#1459)

      On Windows, there are many different file paths that can refer to the same underlying file. Windows uses a case-insensitive file system so for example foo.js and Foo.js are the same file. When bundling, esbuild needs to treat both of these paths as the same to avoid incorrectly bundling the file twice. This is case is already handled by identifying files by their lower-case file path.

      The case that wasn't being handled is the fact that Windows supports two different path separators, / and \, both of which mean the same thing. For example foo/bar.js and foo\bar.js are the same file. With this release, this case is also handled by esbuild. Files that are imported in multiple places with inconsistent path separators will now be considered the same file instead of bundling the file multiple times.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.15(Jul 6, 2021)

    • Fix a bug with var() in CSS color lowering (#1421)

      This release fixes a bug with esbuild's handling of the rgb and hsl color functions when they contain var(). Each var() token sequence can be substituted for any number of tokens including zero or more than one, but previously esbuild's output was only correct if each var() inside of rgb or hsl contained exactly one token. With this release, esbuild will now not attempt to transform newer CSS color syntax to older CSS color syntax if it contains var():

      /* Original code */
      a {
        color: hsl(var(--hs), var(--l));
      }
      
      /* Old output */
      a {
        color: hsl(var(--hs), ,, var(--l));
      }
      
      /* New output */
      a {
        color: hsl(var(--hs), var(--l));
      }
      

      The bug with the old output above happened because esbuild considered the arguments to hsl as matching the pattern hsl(h s l) which is the new space-separated form allowed by CSS Color Module Level 4. Then esbuild tried to convert this to the form hsl(h, s, l) which is more widely supported by older browsers. But this substitution doesn't work in the presence of var(), so it has now been disabled in that case.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.14(Jul 1, 2021)

    • Fix the file loader with custom namespaces (#1404)

      This fixes a regression from version 0.12.12 where using a plugin to load an input file with the file loader in a custom namespace caused esbuild to write the contents of that input file to the path associated with that namespace instead of to a path inside of the output directory. With this release, the file loader should now always copy the file somewhere inside of the output directory.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.13(Jul 1, 2021)

    • Fix using JS synchronous API from from non-main threads (#1406)

      This release fixes an issue with the new implementation of the synchronous JS API calls (transformSync and buildSync) when they are used from a thread other than the main thread. The problem happened because esbuild's new implementation uses node's worker_threads library internally and non-main threads were incorrectly assumed to be esbuild's internal thread instead of potentially another unrelated thread. Now esbuild's synchronous JS APIs should work correctly when called from non-main threads.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.12(Jun 28, 2021)

    • Fix file loader import paths when subdirectories are present (#1044)

      Using the file loader for a file type causes importing affected files to copy the file into the output directory and to embed the path to the copied file into the code that imported it. However, esbuild previously always embedded the path relative to the output directory itself. This is problematic when the importing code is generated within a subdirectory inside the output directory, since then the relative path is wrong. For example:

      $ cat src/example/entry.css
      div {
        background: url(../images/image.png);
      }
      
      $ esbuild --bundle src/example/entry.css --outdir=out --outbase=src --loader:.png=file
      
      $ find out -type f
      out/example/entry.css
      out/image-55DNWN2R.png
      
      $ cat out/example/entry.css
      /* src/example/entry.css */
      div {
        background: url(./image-55DNWN2R.png);
      }
      

      This is output from the previous version of esbuild. The above asset reference in out/example/entry.css is wrong. The path should start with ../ because the two files are in different directories.

      With this release, the asset references present in output files will now be the full relative path from the output file to the asset, so imports should now work correctly when the entry point is in a subdirectory within the output directory. This change affects asset reference paths in both CSS and JS output files.

      Note that if you want asset reference paths to be independent of the subdirectory in which they reside, you can use the --public-path setting to provide the common path that all asset reference paths should be constructed relative to. Specifically --public-path=. should bring back the old problematic behavior in case you need it.

    • Add support for [dir] in --asset-names (#1196)

      You can now use path templates such as --asset-names=[dir]/[name]-[hash] to copy the input directory structure of your asset files (i.e. input files loaded with the file loader) to the output directory. Here's an example:

      $ cat entry.css
      header {
        background: url(images/common/header.png);
      }
      main {
        background: url(images/home/hero.png);
      }
      
      $ esbuild --bundle entry.css --outdir=out --asset-names=[dir]/[name]-[hash] --loader:.png=file
      
      $ find out -type f
      out/images/home/hero-55DNWN2R.png
      out/images/common/header-55DNWN2R.png
      out/entry.css
      
      $ cat out/entry.css
      /* entry.css */
      header {
        background: url(./images/common/header-55DNWN2R.png);
      }
      main {
        background: url(./images/home/hero-55DNWN2R.png);
      }
      
    Source code(tar.gz)
    Source code(zip)
  • v0.12.11(Jun 28, 2021)

    • Enable faster synchronous transforms with the JS API by default (#1000)

      Currently the synchronous JavaScript API calls transformSync and buildSync spawn a new child process on every call. This is due to limitations with node's child_process API. Doing this means transformSync and buildSync are much slower than transform and build, which share the same child process across calls.

      This release improves the performance of transformSync and buildSync by up to 20x. It enables a hack where node's worker_threads API and atomics are used to block the main thread while asynchronous communication with a single long-lived child process happens in a worker. Previously this was only enabled when the ESBUILD_WORKER_THREADS environment variable was set to 1. But this experiment has been available for a while (since version 0.9.6) without any reported issues. Now this hack will be enabled by default. It can be disabled by setting ESBUILD_WORKER_THREADS to 0 before running node.

    • Fix nested output directories with WebAssembly on Windows (#1399)

      Many functions in Go's standard library have a bug where they do not work on Windows when using Go with WebAssembly. This is a long-standing bug and is a fault with the design of the standard library, so it's unlikely to be fixed. Basically Go's standard library is designed to bake "Windows or not" decision into the compiled executable, but WebAssembly is platform-independent which makes "Windows or not" is a run-time decision instead of a compile-time decision. Oops.

      I have been working around this by trying to avoid using path-related functions in the Go standard library and doing all path manipulation by myself instead. This involved completely replacing Go's path/filepath library. However, I missed the os.MkdirAll function which is also does path manipulation but is outside of the path/filepath package. This meant that nested output directories failed to be created on Windows, which caused a build error. This problem only affected the esbuild-wasm package.

      This release manually reimplements nested output directory creation to work around this bug in the Go standard library. So nested output directories should now work on Windows with the esbuild-wasm package.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.10(Jun 27, 2021)

    • Add a target for ES2021

      It's now possible to use --target=es2021 to target the newly-released JavaScript version ES2021. The only difference between that and --target=es2020 is that logical assignment operators such as a ||= b are not converted to regular assignment operators such as a || (a = b).

    • Minify the syntax Infinity to 1 / 0 (#1385)

      The --minify-syntax flag (automatically enabled by --minify) will now minify the expression Infinity to 1 / 0, which uses fewer bytes:

      	// Original code
      	const a = Infinity;
      
      	// Output with "--minify-syntax"
      	const a = 1 / 0;
      

      This change was contributed by @Gusted.

    • Minify syntax in the CSS transform property (#1390)

      This release includes various size reductions for CSS transform matrix syntax when minification is enabled:

      /* Original code */
      div {
        transform: translate3d(0, 0, 10px) scale3d(200%, 200%, 1) rotate3d(0, 0, 1, 45deg);
      }
      
      /* Output with "--minify-syntax" */
      div {
        transform: translateZ(10px) scale(2) rotate(45deg);
      }
      

      The translate3d to translateZ conversion was contributed by @steambap.

    • Support for the case-sensitive flag in CSS attribute selectors (#1397)

      You can now use the case-sensitive CSS attribute selector flag s such as in [type="a" s] { list-style: lower-alpha; }. Previously doing this caused a warning about unrecognized syntax.

    Source code(tar.gz)
    Source code(zip)
  • v0.12.9(Jun 16, 2021)

    • Allow this with --define (#1361)

      You can now override the default value of top-level this with the --define feature. Top-level this defaults to being undefined in ECMAScript modules and exports in CommonJS modules. For example:

      // Original code
      ((obj) => {
        ...
      })(this);
      
      // Output with "--define:this=window"
      ((obj) => {
        ...
      })(window);
      

      Note that overriding what top-level this is will likely break code that uses it correctly. So this new feature is only useful in certain cases.

    • Fix CSS minification issue with !important and duplicate declarations (#1372)

      Previously CSS with duplicate declarations for the same property where the first one was marked with !important was sometimes minified incorrectly. For example:

      .selector {
        padding: 10px !important;
        padding: 0;
      }
      

      This was incorrectly minified as .selector{padding:0}. The bug affected three properties: padding, margin, and border-radius. With this release, this code will now be minified as .selector{padding:10px!important;padding:0} instead which means there is no longer a difference between minified and non-minified code in this case.

    Source code(tar.gz)
    Source code(zip)
Tool to check for dependency confusion vulnerabilities in multiple package management systems

Confused A tool for checking for lingering free namespaces for private package names referenced in dependency configuration for Python (pypi) requirem

Visma Product Security 374 Oct 18, 2021
A zero dependency asset embedder for Go

Mewn A zero dependency asset embedder for Go. About Mewn is perhaps the easiest way to embed assets in a Go program. Here is an example: package main

Lea Anthony 85 Aug 9, 2021
Please is a cross-language high-performance extensible build system for reproducible multi-language builds.

Please is a cross-language build system with an emphasis on high performance, extensibility and reproducibility. It supports a number of popular languages and can automate nearly any aspect of your build process.

Thought Machine 1.8k Oct 17, 2021
packM 🧬 is fivem resource compiler for golang with the power of golang+typescript+webpack

packM ?? packM ?? is fivem resource compiler for golang ,typescript with the power of golang+typescript compiler+webpack

normalM 1 Oct 13, 2021
Go Version Manager

gvm By Josh Bussdieker (jbuss, jaja, jbussdieker) while working at Moovweb Currently lovingly maintained by Benjamin Knigge Pull requests and other an

Moovweb 6.7k Oct 14, 2021
GoReleaser builds Go binaries as fast and easily as possible

GoReleaser builds Go binaries for several platforms, creates a GitHub release and then pushes a Homebrew formula to a tap repository. All that wrapped in your favorite CI.

GoReleaser 8.9k Oct 17, 2021
a build tool for Go, with a focus on cross-compiling, packaging and deployment

goxc NOTE: goxc has long been in maintenance mode. Ever since Go1.5 supported simple cross-compilation, this tool lost much of its value. There are st

Am Laher 1.7k Oct 19, 2021
Moldy CLI the best project starter and manager of the world

Moldy The best project starter of the world ?? What is Moldy ? Hey I present Moldy this beautiful tool that will solve your life in creating, managing

Moldy Community 19 Sep 25, 2021
🌍 Earthly is a build automation tool for the container era

?? Earthly is a build automation tool for the container era. It allows you to execute all your builds in containers. This makes them self-contained, repeatable, portable and parallel. You can use Earthly to create Docker images and artifacts (eg binaries, packages, arbitrary files).

Earthly 5.2k Oct 24, 2021
Create build pipelines in Go

taskflow Create build pipelines in Go This package aims to simplify the creation of build pipelines in Go instead of using scripts or Make. taskflow A

Robert PajΔ…k 257 Oct 12, 2021
Frictionless way of managing project-specific commands

1build is an automation tool used for research and development projects that arms you with the convenience to configure project-local command line ali

Gopinath Langote 106 Oct 17, 2021
Realize is the #1 Golang Task Runner which enhance your workflow by automating the most common tasks and using the best performing Golang live reloading.

#1 Golang live reload and task runner Content - ⭐️ Top Features - ???? Get started - ?? Config sample - ?? Commands List - ?? Support and Suggestions

Oxequa 4.1k Oct 17, 2021
Concurrent task runner, developer's routine tasks automation toolkit. Simple modern alternative to GNU Make 🧰

taskctl - concurrent task runner, developer's routine tasks automation toolkit Simple modern alternative to GNU Make. taskctl is concurrent task runne

null 136 Oct 8, 2021
Various tools for usage with Golang like installer, github tool and cloud features.

Gopei2 (Go Programming Environment Installer) Gopei shell install Go compiler, LiteIDE and configure for you the entire environment, variables, paths,

George Calianu 100 May 18, 2021
Eget is the best way to easily get pre-built binaries for your favorite tools.

Eget: easy pre-built binary installation Eget is the best way to easily get pre-built binaries for your favorite tools. It downloads and extracts pre-

Zachary Yedidia 76 Oct 11, 2021
go language generics system

Gotgo This document describes the third iteration of my attempt at a reasonable implementation of generics for go based on the idea of template packag

David Roundy 120 Apr 29, 2021
Build system and task runner for Go projects

Gilbert is task runner that aims to provide declarative way to define and run tasks like in other projects like Gradle, Maven and etc.

Gilbert 93 Aug 14, 2021
Builds and restarts a Go project when it crashes or some watched file changes

gaper Used to build and restart a Go project when it crashes or some watched file changes Aimed to be used in development only. Changelog See Releases

Max Claus Nunes 48 Aug 2, 2021
a Make/rake-like dev tool using Go

About Mage is a make-like build tool using Go. You write plain-old go functions, and Mage automatically uses them as Makefile-like runnable targets. I

Mage 2.7k Oct 13, 2021