stealjs / steal-tools

Build easy. Load fast.
https://stealjs.com/docs/steal-tools.html
MIT License
67 stars 23 forks source link

Automatically bundle dynamic import() #1093

Open frank-dspeed opened 5 years ago

frank-dspeed commented 5 years ago

Steal should search for import() in ES6 code and bundle that stuff also

green3g commented 5 years ago

Yeah, It would be super slick if steal would handle import similar to how webpack does. I think what webpack does is it scans files for import(...) and creates a "side-bundle". Its pretty capable too, like you can configure webpack to scan and bundle files matching a wildcard pattern in a directory. So for instance:

If I use

const config = route.data.configName;
import('config/${config}').then(config => {

});

webpack will automatically side bundle everything in config/....js as an individual bundle.

justinbmeyer commented 5 years ago

Just an FYI, wildcards are supported in the bundle config now.

justinbmeyer commented 5 years ago

To do this, you could probably search the ast for this and then update the bundle config.

frank-dspeed commented 5 years ago

i am also not sure how to do this justin's suggestion looks valid for me so this could get added to steal tools maybe to scan for imports and modify bundle config.

matthewp commented 5 years ago

We're going to turn this into a proposal.

tldr; Make it so that steal-tools will automatically detect uses of import() and steal.import() within JavaScript code and add those as bundles.

This was discussed on a recent live stream (19:28).

The Problem

If you are creating a progressively loaded app you will likely split it into separate "pages", where each page is a separate bundle. So code like this:

function switchPage(page) {
  if(page === "cart") {
    steal.import("~/cart/").then(mod => {
      document.body.appendChild(new mod.Cart());
    });
  }

  if(page === "home") {
    steal.import("~/home/").then(mod => {
      document.body.appendChild(new mod.Home());
    });
  }
}

You will need to remember to update the bundle configuration in package.json:

{
  "steal": {
    "bundle": [
      "~/cart/cart",
      "~/home/home"
    ]
  }
}

Let's say you later add a new page, product. If you forget to add this to bundle your build will succeed but when you go to production the app won't work correctly.

If you're using continuous deployment this becomes quite a big problem.

The Solution

The proposal is to add to steal-tools the ability to detect these steal.import() and likely import() calls through AST parsing and add these modules as bundles. This will prevent the need from manually setting the bundle configuration.

green3g commented 5 years ago

Also consider the following:

route.data.on('config', newConfig => {
    import(`~/config/${newConfig}`).then(configModule => {
        app.assign(configModule.default); 
    });
});

And the following directory structure:

app 
  /config
     /config1.js
     /config2.js
    /....

Steal might detect each of these config files and side bundle them.

Webpack does this sort of thing and it is surprisingly nice. For instance, webpack provides configuration in how to locate these modules by using special comments:

function loadConfig(path) {
    import(
        /*
            webpackMode: "lazy",
            webpackInclude: /[A-z]+\.js(on)?/
        */
        `./config/apps/${path}`
    ).then((config) => {
        config = config.default;

        const app = document.querySelector('dl-app');
        if (app) {
            document.body.removeChild(app);
        }
        document.body.appendChild(stache('<dl-app props="config" />')({config}));
    });
}