evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
37.95k stars 1.14k forks source link

[Feature] Code splitting on async import() statements. #16

Open tracker1 opened 4 years ago

tracker1 commented 4 years ago

Support code splitting on dynamic import() statements, and additionally split/join on shared bundles for shared dependency models.

hyrious commented 2 years ago

@pft Dynamic imports:

var { "x-y": x_y } = await import("./b.mjs")
console.log(x_y)
pft commented 2 years ago

@hyrious Wow. And, just for completeness sake, this works too:

import("./a.js").then(({"x-y": x_y}) => console.log(x_y));
hyrious commented 2 years ago

@pablo-mayrgundter You need to use a newer target which supports dynamic import (import()), simply edit that field to esnext or something else.

The-Code-Monkey commented 2 years ago

@evanw i have a different use-case from above, i have this dynamic import

const getIcon = (name) => {
  return lazy(() => import(`./icons/${name}`));
};

problem is that esbuild is bundling all the icons into the index.js file rather than keeping them in a separate file. is it possible to say this should stay as it is.

weilandia commented 2 years ago

Trying to track down a recent update on "Code splitting is still a work in progress. It currently only works with the esm output format. There is also a known ordering issue with import statements across code splitting chunks."

Is a fix for these issues planned?

mattfysh commented 1 year ago

I switched to code splitting on dynamic imports but now I've come across a strange bug, has anyone seen this before?

const { parse } = await import('css-what')
parse(selector)

css-what has both ESM and CJS code internally as well as both main and module package.json entries pointing to the relevant file, but for some reason esbuild is using the CJS code instead of ESM. When I edit the package.json and point main to the same place as module, the code starts working again.

I don't think this is a bug with css-what, but I could be wrong

hyrious commented 1 year ago

@mattfysh If you're using node.js to run this code, then this is because node.js ignores the module field. More details at doc: main-fields. You can import the ESM file directly with:

import {parse} from "css-what/lib/es/index.js"

If you're using esbuild to bundle the code with --platform=node, the reason is the same because esbuild is trying to behave the same as node.js. You can try to add --main-fields=module,main to your build script.

For package authors: If you really want users to use the native ESM way to use your package, at least do this:

"exports": {
    "node": {
        "import": "./dist/index.mjs",
        "require": "./dist/index.js"
    },
    "default": "./dist/index.mjs"
}

More details at doc: how-conditions-work.

mattfysh commented 1 year ago

thanks @hyrious - the fix for my case was to use the --main-fields flag, thanks! One other thing I've noticed is the size of my output directory is much larger, I'm guessing that no tree shaking occurs when using code splitting and dynamic imports?

eamodio commented 1 year ago

Is there any way to control the code-splitting to only look at async/dynamic imports?

evanw commented 1 year ago

If you bundle each entry point separately, then entry points won’t share any code.

eamodio commented 1 year ago

If you bundle each entry point separately, then entry points won’t share any code.

Not sure I fully understand that, but I tried setting up separate entry points for a couple of my dynamic import() calls and the main entry point still fully bundled everything, and then it created separate bundles for those imports (but they weren't used).

And if I try to use splitting then I still get WAY too many spit files

JounQin commented 1 year ago

cjs can also use import(path) inside, so I'm wondering why splitting can only been enabled with esm format, I'm saying that even with cjs format output option, the dynamic chunks can still be esm. Of course, correct extensions (.cjs vs .mjs) should be applied in this case.

@evanw

mxdvl commented 1 year ago

If you bundle each entry point separately, then entry points won’t share any code.

And if I try to use splitting then I still get WAY too many spit files

One important caveat is that if you're importing entry points dynamically, you need to make sure that they are marked as external.

For example, if indicate dynamic imports, and your entry points are A, D & C:

A → B → C
D → C
C

You would need to make sure that your onResolve callbacks marks dynamic imports of A, D & C as external.

Murtatrxx commented 10 months ago

Is this still WIP?

millsp commented 10 months ago

Initially meant to be posted here https://github.com/evanw/esbuild/issues/1341

In my case, I needed to bundle dependencies and chunk them, while keeping the output files separate (not one big bundle). All that works except that only format: 'esm' is currently supported, so I wrote a plugin to transpile to CJS again :upside_down_face:.

Definitely not ideal, I can live with it.

export const esmSplitCodeToCjs: esbuild.Plugin = {
  name: 'esmSplitCodeToCjs',
  setup(build) {
    build.onEnd(async (result) => {
      const outFiles = Object.keys(result.metafile?.outputs ?? {})
      const jsFiles = outFiles.filter((f) => f.endsWith('js'))

      await esbuild.build({
        outdir: build.initialOptions.outdir,
        entryPoints: jsFiles,
        allowOverwrite: true,
        format: 'cjs',
        logLevel: 'error',
      })
    })
  },
}
wiebekaai commented 6 months ago

If you bundle each entry point separately, then entry points won’t share any code.

Not sure I fully understand that, but I tried setting up separate entry points for a couple of my dynamic import() calls and the main entry point still fully bundled everything, and then it created separate bundles for those imports (but they weren't used).

And if I try to use splitting then I still get WAY too many spit files

I have the same issue. It splits imports but would only like to split dynamic import(). Any luck?

snuricp commented 5 months ago

@evanw whats the current status on enabling code splitting with cjs format?

weifeiyue commented 4 months ago

@evanw whats the current status on enabling code splitting with cjs format?

Shinigami92 commented 3 months ago

Use tsup

IMO this issue can be closes as not planned

unilynx commented 3 months ago

IMO this issue can be closes as not planned

Safari added full ES module support with Safari 11, released sep 2017. When the issue was opened in 2020, safari 10.x probably was inside the support matrix for quite a few users (it still was for us. way too many iMac users back then stuck on that version reporting issues)

But shouldn't almost everyone be able to switch to ES Modules in their pipelines by now? I'd think there are much more interesting issues in esbuild to solve than 'backporting' split support to CJS