Esbuild: [Feature] Dynamic import w/ template literals w/ variables

Created on 7 Apr 2020  路  9Comments  路  Source: evanw/esbuild

Dynamic import w/ template literals w/ variables problem. Used [email protected].

// x.js
export const a = 1;

// test.js
const name = 'x';
(async () => {
    await import(`./${name}.js`);
})();

Run: esbuild --bundle test.js --outfile=build.js

Expected output:

Wrote to build.js

Actual output:

test.js:3:17: error: The argument to import() must be a string literal

Most helpful comment

Since it's relevant to this thread: as of version 0.5.3, dynamic imports with template literals with variables are now a warning, not an error. The dynamic import is passed through to the output code unchanged.

At the moment I don't have any plans to emulate a virtual file system like Webpack does. That feature in Webpack seems too complicated and special-cased to be built into esbuild itself in my opinion, especially with all of the Webpack-specific comment directives.

One option is to wait for esbuild to have plugin support and then write a plugin for this (see #111). Another option is to just rewrite the import expression in your source code if it is intended to be statically-determined and included in the bundle. So instead of this:

let translation = await import(`translations/${language}.json`)

You could just do something like this instead:

let translation = await {
  'en-US': () => import(`translations/en-US.json`),
  'zh-CN': () => import(`translations/zh-CN.json`),
  // ...
}[language]()

All 9 comments

Bundling with esbuild is intended to traverse your whole dependency tree and include all of your dependencies in the bundle. However, it can only do this if the dependency tree is statically analyzable. This is why esbuild requires string literals. Dynamically-computed import paths are not statically analyzable.

Can you say more about your use case? What are you using this to try to do?

Is it possible to extract all the dynamic parts of a template literal expression and handle them like a * wildcard?

An example use case would be dynamically loading translation files which is what I use it for at work. e.g. import(`/translations/${language}`)

@traverse do you have a bundle per language or do you import the language at runtime?

@kilianc the translations files are simple JSON files per language but since we're using webpack it creates bundles and a bundle map for us so that they can be loaded dynamically at runtime. You can find some more info here https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import. Like it states in the documentation though the path can't be fully dynamic.

Since it's relevant to this thread: as of version 0.5.3, dynamic imports with template literals with variables are now a warning, not an error. The dynamic import is passed through to the output code unchanged.

At the moment I don't have any plans to emulate a virtual file system like Webpack does. That feature in Webpack seems too complicated and special-cased to be built into esbuild itself in my opinion, especially with all of the Webpack-specific comment directives.

One option is to wait for esbuild to have plugin support and then write a plugin for this (see #111). Another option is to just rewrite the import expression in your source code if it is intended to be statically-determined and included in the bundle. So instead of this:

let translation = await import(`translations/${language}.json`)

You could just do something like this instead:

let translation = await {
  'en-US': () => import(`translations/en-US.json`),
  'zh-CN': () => import(`translations/zh-CN.json`),
  // ...
}[language]()

I'm going to close this issue "won't fix" as explained in my post above.

Unfortunately the suggested work-around doesn't scale for larger (legacy) code bases where a dir substructure must be loaded. Webpack has variable dynamic imports and Parcel has the super useful glob import:

import foo from "/assets/*.png";

(but Parcel has other shortcomings, namely handling externals).

I suppose one could run a glob preprocessor and generate statically linked imports -- but having it in the tool makes it a lot easier. Or as a plugin, when that's available.

@moos I think the plugin API will fit this beautifully. You'll be able to catch the "*" and return an object with anything you like

Was this page helpful?
0 / 5 - 0 ratings

Related issues

a7ul picture a7ul  路  3Comments

evanplaice picture evanplaice  路  3Comments

aelbore picture aelbore  路  4Comments

aelbore picture aelbore  路  3Comments

ayox picture ayox  路  4Comments