Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using ESBuild with Babel: error when target="es5" #1010

Closed
mindplay-dk opened this issue Mar 19, 2021 · 11 comments
Closed

Using ESBuild with Babel: error when target="es5" #1010

mindplay-dk opened this issue Mar 19, 2021 · 11 comments

Comments

@mindplay-dk
Copy link

I'm starting a project that targets IE11.

I'd like to use ESBuild during development with modern browsers - then finalize the build by lowering for IE11 with Babel. (The hope is to get a fast development cycle and only transform for IE11 towards the end before cross-browser testing.)

I've tried with esbuild-plugin-babel, but no luck.

So now I'm attempting to post-process the build by manually integrating Babel, using a build-script along the lines of:

import { build } from "./build.js";
import fs from "fs";
import path from "path";
import babel from "@babel/core";

esbuild.build({
  entryPoints: [
    "src/consent.ts",
    "src/banner.ts",
  ],
  bundle: true,
  minify: true,
  write: false,
  target: "es5",
})
.catch(() => process.exit(1))
.then(({ outputFiles, warnings }) => {

  // Use Babel to transform the source-code for IE11:

  const jsFiles = outputFiles.filter(({ path }) => /.*\.js$/.test(path));

  jsFiles.forEach(file => {
    babel.transformAsync(file.text, {
      presets: [['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }]],
      targets: "ie 11, not ie_mob 11",
    })
      .then(result => {
        console.log(result.code); // for now, just dumping the result to console
      });
  });

});

As soon as it hits a const, it errors with the following message:

error: Transforming const to the configured target environment is not supported yet

The author of esbuild-plugin-babel said in this issue to use target: "es5" to avoid the ES6-style closure around the end result with iife - which, yes, ESBuild emits an old-fashioned function instead, but apparently this has the unfortunate effect of also rejecting the entire build when ES6 features are used.

Is there any way to ignore issues like these and just pass the code through without transforming it?

Or some way to configure the iife wrapper without configuring the language level?

Or is there some other better way to integrate ESBuild and Babel? Maybe by not integrating them at all, and instead just running them as two separate build steps entirely?

Any ideas or examples would be incredibly helpful! Have spent two days now and still not getting to "hello world" 😥

@evanw
Copy link
Owner

evanw commented Mar 19, 2021

If Babel is being used to transform newer syntax to ES5, then why does it matter that the arrow function is there? Shouldn't Babel also transform that to ES5? What happens if you remove target: "es5" in your example?

@mindplay-dk
Copy link
Author

If Babel is being used to transform newer syntax to ES5, then why does it matter that the arrow function is there? Shouldn't Babel also transform that to ES5?

With the example script I posted here, yes.

With the plugin API and esbuild-plugin-babel, the iife wrapper is added after the plugin runs - so that's the case where setting target to es5 doesn't help. The plugin is essentially not much use, as it is. (unless your source-code and all your dependencies are already written in ES5.)

What happens if you remove target: "es5" in your example?

In that case, yes, I get regular functions - but I also get require() statements for core-js modules, which means I would need to run the bundler again...

So maybe the answer is to reverse the process? Run the source-files through Babel first, to a temporary folder - then run ESBuild with target: "es5" for the bundling?

This will give me none of the speed benefits of ESBuild though - since Babel will need to run after every change. Not sure what I'm really getting from ESBuild with that setup.

Either way, something needs to change, to enable esbuild-plugin-babel to have any practical use, right? I wonder if you could make ESBuild add the iife wrapper before passing things on to plugins? Or would that mess up everything?

@evanw
Copy link
Owner

evanw commented Mar 22, 2021

There are two ways to do this, and I'd expect both ways to work:

  1. You can run Babel on input files before esbuild and set esbuild's target to es5. In that case you should never get "Transforming const to the configured target environment is not supported yet" because esbuild should never see a const, since Babel should have removed it.

    You can do this by running the source files through Babel into a temporary folder first and then running esbuild. But you can also do this with an esbuild onLoad plugin by running the source files through Babel into memory without bothering with the temporary folder. This may be a simpler approach. This is what esbuild-plugin-babel does.

    You can also potentially make this much faster for incremental builds since you can memoize the Babel transform so it only runs for changed files when you rebuild. It doesn't look like that plugin does any caching, but details for how to do this are here: https://esbuild.github.io/plugins/#caching-your-plugin.

  2. You can run Babel on the output file after esbuild and keep esbuild's target at esnext. In that case you should never get "Transforming const to the configured target environment is not supported yet" because esbuild should never try to transform the const to an older language version.

@mindplay-dk
Copy link
Author

(1) is what I was trying to do before - so I have target: "es5" and esbuild-plugin-babel configured like this:

  plugins: [
    babel({
      config: {
        presets: [
          ['@babel/preset-env', { targets: "ie 11, not ie_mob 11" }]
        ],
      }
    })
  ]

This should get rid of the short arrow functions.

But (as covered here) now it exits with a different message:

Dynamic import can only be supported when transforming ES modules to AMD, CommonJS or SystemJS. Only the parser plugin will be enabled.

I'm not using dynamic imports. My test-framework might be using them, that's my only clue - but I'm not including the test in my entryPoints right now, just two simple scripts with some "hello world" stuff; no import statements.

Is there any way to enable some extra verbosity or something? So I can find out where it found this dynamic import?

(I also wish there were some way to tell where these messages are coming from - neither Babel nor ESBuild identifies error messages with the name of the tool. Identifying the source of an error currently comes down to searching Github for the error message string...)

@mindplay-dk
Copy link
Author

(2) Running Babel on output files is what I've been trying today, but this results in Babel "unminifying" the code - if I have to run ESBuild again to minify, that's ESBuild -> Babel -> ESBuild, which isn't pretty, or adding a minifier to Babel.

@mindplay-dk
Copy link
Author

Regarding the "dynamic import" error message, I have no idea what triggers this - I just tried removing my entire test-suite from the folder (which I have no idea why it would be picking up in the first place - as said, not listed in my entry-points) and it still emits this error. Literally all that's being compiled now is console.log("hello world") and I'm still getting this error 🤷‍♂️

@mindplay-dk
Copy link
Author

Oh, it's not an error! 😂

The message about dynamic imports is just a warning, I suppose? To let you know that dynamic imports aren't supported when the output format is iife? The script doesn't exit with a status code, it actually emits the files - I totally missed that.

Okay, so maybe use this feedback to improve the console output? 🙂

If every output message started with e.g. [esbuild:warning] or [esbuild:error] that would probably help to clarify things?

Also maybe consider emitting general messages like [esbuild] emitted 2 files, 3.5KB total or something, to let you know that the tool has actually done something. I suppose I can tie in something to do that when the promise resolves - it's just not very obvious what's going on until you've actually set something like that up yourself.

I mean, it sounds like the situation where Babel is needed for IE11 is not temporary, so a lot of people are going to have to try to integrate these tools, at least for the time being. (Our product gets embedded on half a million websites, and while I would love to just tell everyone to drop support for IE11, it's not our call - we don't have that liberty. Maybe in a couple of years.)

Thanks for trying to help!

You can close this issue if there's nothing else actionable you want to take away from this discussion.

@mindplay-dk
Copy link
Author

Side note: I discovered the logLevel setting - even when this is set to error, the "dynamic import" message appears; this message probably should be emitted with the warning-level? (Unless it really is an error? But as mentioned, it exits with status code 0 - this might be considered an error, if any of the code actually uses dynamic imports, but otherwise it's just a warning or possibly even info right? Not an error, at least?)

@evanw
Copy link
Owner

evanw commented Mar 28, 2021

I'm going to close this issue since the problem isn't with esbuild, and there's nothing actionable to do here.

Also maybe consider emitting general messages like [esbuild] emitted 2 files, 3.5KB total or something

A summary message will be printed if you use logLevel: 'info' (well, as long as esbuild isn't configured to write to stdout).

the "dynamic import" message appears; this message probably should be emitted with the warning-level?

This isn't coming from esbuild. This message is from Babel, not esbuild.

@evanw evanw closed this as completed Mar 28, 2021
@januszm
Copy link

januszm commented May 31, 2022

@mindplay-dk Did you manage to somehow solve the issue of "target" es5 support? Less than 1% of the users of my app use E11 but, ... it's not 0 so I'm looking for a solution.

@mindplay-dk
Copy link
Author

@januszm this was a while back, but I think I ended up with a dedicated build-step (using Babel) for the IE11 version. If you're looking for something faster, I hear SWC is pretty good - it's probably the only fast transpiler with IE11 support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants