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

Dynamic imports on runtime are ignored by Rollup #2463

Closed
pieterbeulque opened this issue Sep 18, 2018 · 22 comments
Closed

Dynamic imports on runtime are ignored by Rollup #2463

pieterbeulque opened this issue Sep 18, 2018 · 22 comments

Comments

@pieterbeulque
Copy link

@pieterbeulque pieterbeulque commented Sep 18, 2018

  • Rollup Version: v0.66.0
  • Operating System (or Browser): Mac OS X
  • Node Version: v10.4.1

How Do We Reproduce?

REPL

// main.js
const imports = ['./foo.js'];

imports.forEach((file) => import(file));
// rollup.config.js
export default {
	input: 'main.js',
	output: {
		dir: 'public/js',
		format: 'esm',
	},
	experimentalCodeSplitting: true,
}

Expected Behavior

To have foo.js be considered by Rollup as part of the application and copied over to public/js.

Actual Behavior

foo.js is not copied over or considered for code splitting.

I'm not sure if this is hard to do or possible at all.

@alundiak
Copy link

@alundiak alundiak commented Oct 12, 2018

I use the same rollup.config.js and main.js and I have this foo.js:

export default [
  './a.js',
  './b.js'
]

And I can confirm, that after rollup -c rollup.config.js public/js contains ONLY main.js. With the same content, and no foo.js, no a.js, no b.js.

rollup v0.66.6, node v10.6.0, npm v6.1.0, Windows 7.

@lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Oct 12, 2018

To answer the initial question: This is indeed a harder problem. We might add special code for very specific ways of writing this. Nevertheless it will break down once your arrays contains non-trivial code that can only be evaluated at runtime.

@guybedford
Copy link
Contributor

@guybedford guybedford commented Oct 15, 2018

So far as possible I believe quite strongly we should trace and handle these builds, while retaining the hooks for custom behaviours.

@hronro
Copy link

@hronro hronro commented Jan 17, 2019

My case is to dynamic import a module in a specific folder:

Here is my files structure:

├── i18n
│   ├── index.js
│   └── locales
│       ├── en.js
│       └── zh.js

In file i18n/index.js, I have a function like this:

function getTranslation (lang) {
	return import(`./locales/${lang}`)
}

Is there any possible solutions for rollup to handle this kind of situation?

By the way, Webpack solve this problem by wrap every file inside i18n/locales into a chunk, so I'm wondering if there can be something like a magic comment to tell Rollup to wrap everything inside i18n/locales into a chunk.

@pieterbeulque
Copy link
Author

@pieterbeulque pieterbeulque commented Jan 17, 2019

I think this could work, because you remove the string interpolation.

function getTranslation (lang) {
  if (lang === 'en') {
    return import('./locales/en.js');
  }

  if (lang === 'zh') {
    return import('./locales/zh.js');
  }
}
@laurentpayot
Copy link

@laurentpayot laurentpayot commented Jan 17, 2019

@pieterbeulque is right, unfortunately Rollup dynamic imports do not support string interpolation (yet?).

@lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Jan 21, 2019

The problem with string interpolation is that rollup needs to make some assumptions about the host system it runs on (i.e. it is not run in the browser) as well as inject a custom resolver runtime to handle the dynamic import. The custom resolver then needs to properly translate relative paths into absolute paths which leads to either paths from your host system sneaking into the bundle or at the very least needlessly complicated code.

(Simple example: You write import('../a/' + somethingCustom);. Then Rollup will need to take care of stripping the ../a/ while at the same time be prepared to resolve any sub-directories found in somethingCustom while you are probably just looking for a way to access some file in the a folder that you only know at runtime).

If you want to directly take the contents of a directory as a base for dynamic imports, I would suggest a better solution that solely has the caveat of not providing the same level of syntactic sugar.

The following config will create a virtual module dynamic-targets that exports an object containing all file names in the directory /dynamic (relative to the rollup config) as keys and functions that import those files as values:

// rollup.config.js
import path from 'path';
import fs from 'fs';

export default {
  input: 'src/main.js',
  plugins: [{
    // this is necessary to tell rollup that it should not try to resolve "dynamic-targets"
    // via other means
    resolveId(id) {
      if (id === 'dynamic-targets') {
        return id;
      }
      return null;
    },

    // create a module that exports an object containing file names as keys and
    // functions that import those files as values
    load(id) {
      if (id === 'dynamic-targets') {
        const targetDir = path.join(__dirname, 'dynamic');
        const files = fs.readdirSync(targetDir);
        const objectEntries = files
          .map(file => `  '${file}': () => import('${path.join(targetDir, file)}')`);
        return `export default {\n${objectEntries.join(',\n')}\n};`;
      }
      return null;
    }
  }],
  output: {
    dir: 'dist',
    format: 'cjs'
  }
}

You can now use this virtual module in your code like this:

import dynamicTargets from 'dynamic-targets';

// my example files are "test0.js", ..., "test9.js"
const fileName = `test${Math.floor(Math.random() * 10)}.js`;

// import and run the file, then log a message
dynamicTargets[fileName]().then(() => console.log(`imported ${fileName}`));

If you feel inspired, it should not be too difficult to generalize this idea into a "real" plugin for everyone to use.

@mafar
Copy link

@mafar mafar commented Mar 16, 2019

@lukastaegert
I have same problem. one of my requirements is to use dynamic imports , which is covered by https://rollupjs.org, but in my case my routes are sent by Backend so I have make dynamic imports by variables names .Here is my simplified logic

// for router
const route = {
  "path":"data/dispaly/{id}",
  "js":"pages/data_dispaly.js",
}
import renderer from route.js;
new renderer();// display page

now each renderer page may have its own imports and such.
How can I bundle such js files with my app ? so that I can have chunks and code splitting while routes are fetched on runtime and unknown to rollup.

Can anyone please propose a solution ?

@lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Mar 16, 2019

I interpret the problem as follows:

  • There are different routes which could be gathered from the file system but which you do not want to hard-code
  • You want to import from different routes in a way that is determined at runtime

Vague suggestion:

  • Create a simple plugin that creates a virtual module (the simple example in the docs shows you how to do that: https://rollupjs.org/guide/en#a-simple-example)
  • This module is populated dynamically while bundling by checking the file system and creating e.g. a switch statement containing dynamic imports to all possible routes
  • This module exposes a function that receives a parameter and imports the correct file
@mafar
Copy link

@mafar mafar commented Mar 16, 2019

@lukastaegert yes . You understood the issue correctly. My App brings router info from server so i cant hardcode imports for my routes import * from './some.js'

But those js files do exit in src/pages folder and they will be fetched on demand when for example a user clicks a button, based on its route information, relevant js will be rendered.

@mafar
Copy link

@mafar mafar commented Mar 16, 2019

@lukastaegert
Say my app.js has bootstrap and jquery as imports and a button with event click that fetches its route info from api and then has to load page1.js

now page1.js has again jquery as import but another chart.js as well.
QUESTION
How can rollup be aware that page1.js has a common chunk with app.js ? while code splitting as in app.js , path to page1.js was a variable and unknown during bootup

see proposal here https://github.com/tc39/proposal-dynamic-import#example

@lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Mar 16, 2019

Rollup will create shared chunks automatically if all entry points are part of the same build. These can be either manual, static entry points by adding them to the input option or dynamic ones via dynamic imports. The latter only creates separate chunks if they can be statically resolved. If you create a virtual module as outlined above, this would work out of the box. If they just do not exist at build time, creating shared chunk needs more manual work.

@shellscape
Copy link
Contributor

@shellscape shellscape commented Aug 10, 2019

Hey folks. This is a saved-form message, but rest assured we mean every word. The Rollup team is attempting to clean up the Issues backlog in the hopes that the active and still-needed, still-relevant issues bubble up to the surface. With that, we're closing issues that have been open for an eon or two, and have gone stale like pirate hard-tack without activity.

We really appreciate the folks have taken the time to open and comment on this issue. Please don't confuse this closure with us not caring or dismissing your issue, feature request, discussion, or report. The issue will still be here, just in a closed state. If the issue pertains to a bug, please re-test for the bug on the latest version of Rollup and if present, please tag @shellscape and request a re-open, and we'll be happy to oblige.

@TheComputerM
Copy link

@TheComputerM TheComputerM commented Oct 6, 2020

@shellscape This is still an issue in rollup v2.28.2. How do I do something like this:

const template = await import(
   /* webpackChunkName: "examples-source" */
  `../examples/${file}.js`
)
@lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Oct 6, 2020

@TheComputerM
Copy link

@TheComputerM TheComputerM commented Oct 6, 2020

It has no support for IIFE bundles, webpack creates different bundles for the files.

@lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Oct 6, 2020

Rollup as a while has no support for code-splitting IIFE bundles. You need a runtime loader like AMD or SystemJS for that. Webpack always bundles their proprietary bundler code, which is likely what you mean. With ESM, CommonJS, SytemsJS or AMD output, this plugin should work as expected.

@TheComputerM
Copy link

@TheComputerM TheComputerM commented Oct 8, 2020

@lukastaegert So is there no way that I can do:

const files = ['t1','t2','t3']
files.forEach((i) => import(`../tests/${t1}.js`));

Which generates files:

- test.1.js
- test.2.js
- test.3.js

With rollup?

@lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Oct 8, 2020

Well. not with IIFE format. As I said, use AMD or SystemJS. If you want the ease-of-use of not having to worry about the loader, I can actually recommend using https://github.com/surma/rollup-plugin-off-main-thread . While this plugin appears to be about workers, one effect is that it takes care of embedding a minimal AMD loader into your entry chunks so you get roughly the same experience as when using Webpack.

@TheComputerM
Copy link

@TheComputerM TheComputerM commented Oct 8, 2020

Thanks, just FYI, I am using rollup for svelte.

@pckhoi
Copy link

@pckhoi pckhoi commented Dec 28, 2020

Hmm, I've tried to use dynamic-import-vars plugin with no success no matter whether I set output to iife or esm or umd.

const module = await import(`../../benchmark/suites/${name}.js`)

It always output this line:

TypeError: Failed to fetch dynamically imported module: file:///.../benchmark/suites/indexing.js

And the file path is correct.

@pckhoi
Copy link

@pckhoi pckhoi commented Dec 28, 2020

Oh is it because browser can't read file? Then this plugin only work for Node.

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

Successfully merging a pull request may close this issue.

None yet
10 participants