How to include manual import() in Webpack BundleHow do JavaScript closures work?How do I check if an element is hidden in jQuery?How do I remove a property from a JavaScript object?How do I check if an array includes an object in JavaScript?How do I redirect to another webpage?How do I include a JavaScript file in another JavaScript file?How to check whether a string contains a substring in JavaScript?How do I remove a particular element from an array in JavaScript?Managing jQuery plugin dependency in webpackNPM vs. Bower vs. Browserify vs. Gulp vs. Grunt vs. Webpack

Why is it a bad idea to hire a hitman to eliminate most corrupt politicians?

How to travel to Japan while expressing milk?

Does Dispel Magic work on Tiny Hut?

Do Iron Man suits sport waste management systems?

What's the meaning of "Sollensaussagen"?

Finding the reason behind the value of the integral.

How can I deal with my CEO asking me to hire someone with a higher salary than me, a co-founder?

Why didn't Boeing produce its own regional jet?

How to enclose theorems and definition in rectangles?

How do conventional missiles fly?

Can a virus destroy the BIOS of a modern computer?

Avoiding the "not like other girls" trope?

Does int main() need a declaration on C++?

Knowledge-based authentication using Domain-driven Design in C#

Is this draw by repetition?

Why was Sir Cadogan fired?

How to compactly explain secondary and tertiary characters without resorting to stereotypes?

How obscure is the use of 令 in 令和?

files created then deleted at every second in tmp directory

Bullying boss launched a smear campaign and made me unemployable

Different meanings of こわい

Is it a bad idea to plug the other end of ESD strap to wall ground?

How to install cross-compiler on Ubuntu 18.04?

How do I exit BASH while loop using modulus operator?



How to include manual import() in Webpack Bundle


How do JavaScript closures work?How do I check if an element is hidden in jQuery?How do I remove a property from a JavaScript object?How do I check if an array includes an object in JavaScript?How do I redirect to another webpage?How do I include a JavaScript file in another JavaScript file?How to check whether a string contains a substring in JavaScript?How do I remove a particular element from an array in JavaScript?Managing jQuery plugin dependency in webpackNPM vs. Bower vs. Browserify vs. Gulp vs. Grunt vs. Webpack













2















I am quite new to Webpack, so bear with me if thats a stupid question.



My goal is to transform my old, AMD based codebase to a ES6 Module based solution. What I am struggling with is handling dynamic import()s. So my app router works on a module basis, i.e. each route is mapped to a module path and then required. Since I know what modules will be included, I just add those dynamically imported modules to my r.js configuration and am able to build everything in a single file, with all require calls still working.



Now, I am trying to do the same with ES6 modules and Webpack. With my devmode this is no problem as I can just replace require() with import(). However I cannot get this to work with bundling. Either Webpack splits my code (and still fails to load the dynamic module anyways), or - if I use the Array format for the entry config, the dynamic module is included in the bundle but loading still fails: Error: Cannot find module '/src/app/DynClass.js'



This is how my Webpack config looks like:



const webpack = require('webpack');
const path = require('path');

module.exports =
mode: "development",
entry: ['./main.js', './app/DynClass.js'],
output:
filename: 'main.js',
path: path.resolve(__dirname, "../client/")
,
resolve:
alias:
"/src": path.resolve(__dirname, '')

,
module:
rules: [

test: /.tpl$/i,
use: 'raw-loader',
,
]

;


So basically I want to tell Webpack: "hey, there is another module (or more) that is to be loaded dynamically and I want it to be included in the bundle"



How can I do this?










share|improve this question
























  • Have you looked at webpack's explanation of the subject?

    – flup
    Mar 13 at 22:27







  • 2





    there are many unclear things here, how do you use import() in your code? also where is /src/app/DynClass.js used?

    – d7my
    Mar 14 at 12:01











  • @flup: yes I have. @d7my import() is used like this: const module = await import(modulePath);, so I cannot use a template here. DynClass is just a test-dummy being imported by the code I just mentioned. So there will be a kind of configuration object so my app-router knows what routes to match and what module to import.

    – frontend_dev
    Mar 14 at 12:40















2















I am quite new to Webpack, so bear with me if thats a stupid question.



My goal is to transform my old, AMD based codebase to a ES6 Module based solution. What I am struggling with is handling dynamic import()s. So my app router works on a module basis, i.e. each route is mapped to a module path and then required. Since I know what modules will be included, I just add those dynamically imported modules to my r.js configuration and am able to build everything in a single file, with all require calls still working.



Now, I am trying to do the same with ES6 modules and Webpack. With my devmode this is no problem as I can just replace require() with import(). However I cannot get this to work with bundling. Either Webpack splits my code (and still fails to load the dynamic module anyways), or - if I use the Array format for the entry config, the dynamic module is included in the bundle but loading still fails: Error: Cannot find module '/src/app/DynClass.js'



This is how my Webpack config looks like:



const webpack = require('webpack');
const path = require('path');

module.exports =
mode: "development",
entry: ['./main.js', './app/DynClass.js'],
output:
filename: 'main.js',
path: path.resolve(__dirname, "../client/")
,
resolve:
alias:
"/src": path.resolve(__dirname, '')

,
module:
rules: [

test: /.tpl$/i,
use: 'raw-loader',
,
]

;


So basically I want to tell Webpack: "hey, there is another module (or more) that is to be loaded dynamically and I want it to be included in the bundle"



How can I do this?










share|improve this question
























  • Have you looked at webpack's explanation of the subject?

    – flup
    Mar 13 at 22:27







  • 2





    there are many unclear things here, how do you use import() in your code? also where is /src/app/DynClass.js used?

    – d7my
    Mar 14 at 12:01











  • @flup: yes I have. @d7my import() is used like this: const module = await import(modulePath);, so I cannot use a template here. DynClass is just a test-dummy being imported by the code I just mentioned. So there will be a kind of configuration object so my app-router knows what routes to match and what module to import.

    – frontend_dev
    Mar 14 at 12:40













2












2








2








I am quite new to Webpack, so bear with me if thats a stupid question.



My goal is to transform my old, AMD based codebase to a ES6 Module based solution. What I am struggling with is handling dynamic import()s. So my app router works on a module basis, i.e. each route is mapped to a module path and then required. Since I know what modules will be included, I just add those dynamically imported modules to my r.js configuration and am able to build everything in a single file, with all require calls still working.



Now, I am trying to do the same with ES6 modules and Webpack. With my devmode this is no problem as I can just replace require() with import(). However I cannot get this to work with bundling. Either Webpack splits my code (and still fails to load the dynamic module anyways), or - if I use the Array format for the entry config, the dynamic module is included in the bundle but loading still fails: Error: Cannot find module '/src/app/DynClass.js'



This is how my Webpack config looks like:



const webpack = require('webpack');
const path = require('path');

module.exports =
mode: "development",
entry: ['./main.js', './app/DynClass.js'],
output:
filename: 'main.js',
path: path.resolve(__dirname, "../client/")
,
resolve:
alias:
"/src": path.resolve(__dirname, '')

,
module:
rules: [

test: /.tpl$/i,
use: 'raw-loader',
,
]

;


So basically I want to tell Webpack: "hey, there is another module (or more) that is to be loaded dynamically and I want it to be included in the bundle"



How can I do this?










share|improve this question
















I am quite new to Webpack, so bear with me if thats a stupid question.



My goal is to transform my old, AMD based codebase to a ES6 Module based solution. What I am struggling with is handling dynamic import()s. So my app router works on a module basis, i.e. each route is mapped to a module path and then required. Since I know what modules will be included, I just add those dynamically imported modules to my r.js configuration and am able to build everything in a single file, with all require calls still working.



Now, I am trying to do the same with ES6 modules and Webpack. With my devmode this is no problem as I can just replace require() with import(). However I cannot get this to work with bundling. Either Webpack splits my code (and still fails to load the dynamic module anyways), or - if I use the Array format for the entry config, the dynamic module is included in the bundle but loading still fails: Error: Cannot find module '/src/app/DynClass.js'



This is how my Webpack config looks like:



const webpack = require('webpack');
const path = require('path');

module.exports =
mode: "development",
entry: ['./main.js', './app/DynClass.js'],
output:
filename: 'main.js',
path: path.resolve(__dirname, "../client/")
,
resolve:
alias:
"/src": path.resolve(__dirname, '')

,
module:
rules: [

test: /.tpl$/i,
use: 'raw-loader',
,
]

;


So basically I want to tell Webpack: "hey, there is another module (or more) that is to be loaded dynamically and I want it to be included in the bundle"



How can I do this?







javascript webpack es6-modules






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 16 at 16:30







frontend_dev

















asked Mar 7 at 20:54









frontend_devfrontend_dev

1,1551121




1,1551121












  • Have you looked at webpack's explanation of the subject?

    – flup
    Mar 13 at 22:27







  • 2





    there are many unclear things here, how do you use import() in your code? also where is /src/app/DynClass.js used?

    – d7my
    Mar 14 at 12:01











  • @flup: yes I have. @d7my import() is used like this: const module = await import(modulePath);, so I cannot use a template here. DynClass is just a test-dummy being imported by the code I just mentioned. So there will be a kind of configuration object so my app-router knows what routes to match and what module to import.

    – frontend_dev
    Mar 14 at 12:40

















  • Have you looked at webpack's explanation of the subject?

    – flup
    Mar 13 at 22:27







  • 2





    there are many unclear things here, how do you use import() in your code? also where is /src/app/DynClass.js used?

    – d7my
    Mar 14 at 12:01











  • @flup: yes I have. @d7my import() is used like this: const module = await import(modulePath);, so I cannot use a template here. DynClass is just a test-dummy being imported by the code I just mentioned. So there will be a kind of configuration object so my app-router knows what routes to match and what module to import.

    – frontend_dev
    Mar 14 at 12:40
















Have you looked at webpack's explanation of the subject?

– flup
Mar 13 at 22:27






Have you looked at webpack's explanation of the subject?

– flup
Mar 13 at 22:27





2




2





there are many unclear things here, how do you use import() in your code? also where is /src/app/DynClass.js used?

– d7my
Mar 14 at 12:01





there are many unclear things here, how do you use import() in your code? also where is /src/app/DynClass.js used?

– d7my
Mar 14 at 12:01













@flup: yes I have. @d7my import() is used like this: const module = await import(modulePath);, so I cannot use a template here. DynClass is just a test-dummy being imported by the code I just mentioned. So there will be a kind of configuration object so my app-router knows what routes to match and what module to import.

– frontend_dev
Mar 14 at 12:40





@flup: yes I have. @d7my import() is used like this: const module = await import(modulePath);, so I cannot use a template here. DynClass is just a test-dummy being imported by the code I just mentioned. So there will be a kind of configuration object so my app-router knows what routes to match and what module to import.

– frontend_dev
Mar 14 at 12:40












1 Answer
1






active

oldest

votes


















0














So yeah, after much fiddling there seems to be a light at the end of the tunnel. Still, this is not a 100% solution and it is surely not for the faint of heart, as it is quite ugly and fragile. But still I want to share my approach with you:



1) manual parsing of my routes config



My router uses a config file looking like this:



import StaticClass from "/src/app/StaticClass.js";

export default
StaticClass:
match: /^//,
module: StaticClass
,
DynClass:
match: /^//,
module: "/src/app/DynClass.js"

;


So as you can see the export is an object, with keys acting as the route id, and an object that contains the matches (regex based) and the module which should be executed by the router if the route matches. I can feed my router with both a Constructor function (or an object) for modules which are available immediatly (i.e. contained in the main chunk) or if the module value is a string, this means that the router has to load this module dynamically by using the path specified in the string.



So as I know what modules could be potentially loaded (but not if and when) I can now parse this file within my build process and transform the route config to something webpack can understand:



const path = require("path");
const fs = require("fs");

let routesSource = fs.readFileSync(path.resolve(__dirname, "app/routes.js"), "utf8");

routesSource = routesSource.substr(routesSource.indexOf("export default"));
routesSource = routesSource.replace(/module:s*((?!".*").)*$/gm, "module: undefined,");
routesSource = routesSource.replace(/r?n|r/g, "").replace("export default", "var routes = ");

eval(routesSource);

let dummySource = Object.entries(routes).reduce((acc, [routeName, routeConfig]) =>

if (typeof routeConfig.module === "string")
return acc + `import(/* webpackChunkName: "$routeName" */"$routeConfig.module");`;


return acc;

, "") + "export default ''";


(Yeah I know this is quite ugly and also a bit brittle so this surely could be done better)



Essentially I create a new, virtual module where every route entry which demands a dynamic import is translated, so:



DynClass: 
match: /^//,
module: "/src/app/DynClass.js"



becomes:



import(/* webpackChunkName: "DynClass" */"/src/app/DynClass.js");


So the route id simply becomes the name of the chunk!



2) including the virtual module in the build



For this I use the virtual-module-webpack-plugin:



plugins: [
new VirtualModulePlugin(
moduleName: "./app/dummy.js",
contents: dummySource
)
],


Where dummySource is just a string containing the sourcecode of my virtual module I just have generated. Now, this module is pulled in and the "virtual imports" can be processed by webpack. But wait, I still need to import the dummy module, but I do not have any in my development mode (where I use everything natively, so no loaders).



So in my main code I do the following:



let isDev = false;
/** @remove */
isDev = true;
/** @endremove */

if (isDev) import('./app/dummy.js');


Where "dummy.js" is just an empty stub module while I am in development mode. The parts between that special comments are removed while building (using the webpack-loader-clean-pragma loader), so while webpack "sees" the import for dummy.js, this code will not be executed in the build itself since then isDev evaluates to false. And since we already defined a virtual module with the same path, the virtual module is included while building just like I want, and of course all dependencies are resolved as well.



3) Handling the actual loading



For development, this is quite easy:



import routes from './app/routes.js';

Object.entries(routes).forEach(async ([routeId, route]) =>
if (typeof route.module === "function")
new route.module;
else
const result = await import(route.module);
new result.default;

);


(Note that this is not the actual router code, just enough to help me with my PoC)



Well, but for the build I need something else, so I added some code specific to the build environment:



/** @remove */
const result = await import(route.module);
new result.default;
/** @endremove */

if (!isDev)
if (typeof route.module === "string") await __webpack_require__.e(routeId);
const result = __webpack_require__(route.module.replace("/src", "."));
new result.default;



Now, the loading code for the dev environment is just stripped out, and there is another loading code that uses webpack internally. I also check if the module value is a function or string, and if it is the latter I invoke the internal require.ensure function to load the correct chunk: await __webpack_require__.e(routeId);. Remember that I named my chunks when generating the virtual module? Now thats why I still can find them now!



4) more needs to be done



Another thing I encountered is when several dynamically loaded modules have the same dependencies, webpack tries to generate more chunks with names like module1~module2.bundle.js, breaking my build. To counter this, I needed to make sure that all those shared modules go into a specific named bundle I called "shared":



optimization: 
splitChunks:
chunks: "all",
name: "shared"




And when in production mode, I simply load this chunk manually before any dynamic modules depending on it are requested:



if (!isDev) 
await __webpack_require__.e("shared");



Again, this code only runs in production mode!



Finally, I have to prevent webpack renaming my modules (and chunks) to something like "1", "2" etc, but rather keep the names I just have defined:



optimization: 
namedChunks: true,
namedModules: true



Se yeah, there you have it! As I said this wasn't pretty but seems to work, at least with my simplified test setup. I really hope there aren't any blockers ahead of me when I do all the rest (like ESLint, SCSS etc)!






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55052636%2fhow-to-include-manual-import-in-webpack-bundle%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    So yeah, after much fiddling there seems to be a light at the end of the tunnel. Still, this is not a 100% solution and it is surely not for the faint of heart, as it is quite ugly and fragile. But still I want to share my approach with you:



    1) manual parsing of my routes config



    My router uses a config file looking like this:



    import StaticClass from "/src/app/StaticClass.js";

    export default
    StaticClass:
    match: /^//,
    module: StaticClass
    ,
    DynClass:
    match: /^//,
    module: "/src/app/DynClass.js"

    ;


    So as you can see the export is an object, with keys acting as the route id, and an object that contains the matches (regex based) and the module which should be executed by the router if the route matches. I can feed my router with both a Constructor function (or an object) for modules which are available immediatly (i.e. contained in the main chunk) or if the module value is a string, this means that the router has to load this module dynamically by using the path specified in the string.



    So as I know what modules could be potentially loaded (but not if and when) I can now parse this file within my build process and transform the route config to something webpack can understand:



    const path = require("path");
    const fs = require("fs");

    let routesSource = fs.readFileSync(path.resolve(__dirname, "app/routes.js"), "utf8");

    routesSource = routesSource.substr(routesSource.indexOf("export default"));
    routesSource = routesSource.replace(/module:s*((?!".*").)*$/gm, "module: undefined,");
    routesSource = routesSource.replace(/r?n|r/g, "").replace("export default", "var routes = ");

    eval(routesSource);

    let dummySource = Object.entries(routes).reduce((acc, [routeName, routeConfig]) =>

    if (typeof routeConfig.module === "string")
    return acc + `import(/* webpackChunkName: "$routeName" */"$routeConfig.module");`;


    return acc;

    , "") + "export default ''";


    (Yeah I know this is quite ugly and also a bit brittle so this surely could be done better)



    Essentially I create a new, virtual module where every route entry which demands a dynamic import is translated, so:



    DynClass: 
    match: /^//,
    module: "/src/app/DynClass.js"



    becomes:



    import(/* webpackChunkName: "DynClass" */"/src/app/DynClass.js");


    So the route id simply becomes the name of the chunk!



    2) including the virtual module in the build



    For this I use the virtual-module-webpack-plugin:



    plugins: [
    new VirtualModulePlugin(
    moduleName: "./app/dummy.js",
    contents: dummySource
    )
    ],


    Where dummySource is just a string containing the sourcecode of my virtual module I just have generated. Now, this module is pulled in and the "virtual imports" can be processed by webpack. But wait, I still need to import the dummy module, but I do not have any in my development mode (where I use everything natively, so no loaders).



    So in my main code I do the following:



    let isDev = false;
    /** @remove */
    isDev = true;
    /** @endremove */

    if (isDev) import('./app/dummy.js');


    Where "dummy.js" is just an empty stub module while I am in development mode. The parts between that special comments are removed while building (using the webpack-loader-clean-pragma loader), so while webpack "sees" the import for dummy.js, this code will not be executed in the build itself since then isDev evaluates to false. And since we already defined a virtual module with the same path, the virtual module is included while building just like I want, and of course all dependencies are resolved as well.



    3) Handling the actual loading



    For development, this is quite easy:



    import routes from './app/routes.js';

    Object.entries(routes).forEach(async ([routeId, route]) =>
    if (typeof route.module === "function")
    new route.module;
    else
    const result = await import(route.module);
    new result.default;

    );


    (Note that this is not the actual router code, just enough to help me with my PoC)



    Well, but for the build I need something else, so I added some code specific to the build environment:



    /** @remove */
    const result = await import(route.module);
    new result.default;
    /** @endremove */

    if (!isDev)
    if (typeof route.module === "string") await __webpack_require__.e(routeId);
    const result = __webpack_require__(route.module.replace("/src", "."));
    new result.default;



    Now, the loading code for the dev environment is just stripped out, and there is another loading code that uses webpack internally. I also check if the module value is a function or string, and if it is the latter I invoke the internal require.ensure function to load the correct chunk: await __webpack_require__.e(routeId);. Remember that I named my chunks when generating the virtual module? Now thats why I still can find them now!



    4) more needs to be done



    Another thing I encountered is when several dynamically loaded modules have the same dependencies, webpack tries to generate more chunks with names like module1~module2.bundle.js, breaking my build. To counter this, I needed to make sure that all those shared modules go into a specific named bundle I called "shared":



    optimization: 
    splitChunks:
    chunks: "all",
    name: "shared"




    And when in production mode, I simply load this chunk manually before any dynamic modules depending on it are requested:



    if (!isDev) 
    await __webpack_require__.e("shared");



    Again, this code only runs in production mode!



    Finally, I have to prevent webpack renaming my modules (and chunks) to something like "1", "2" etc, but rather keep the names I just have defined:



    optimization: 
    namedChunks: true,
    namedModules: true



    Se yeah, there you have it! As I said this wasn't pretty but seems to work, at least with my simplified test setup. I really hope there aren't any blockers ahead of me when I do all the rest (like ESLint, SCSS etc)!






    share|improve this answer



























      0














      So yeah, after much fiddling there seems to be a light at the end of the tunnel. Still, this is not a 100% solution and it is surely not for the faint of heart, as it is quite ugly and fragile. But still I want to share my approach with you:



      1) manual parsing of my routes config



      My router uses a config file looking like this:



      import StaticClass from "/src/app/StaticClass.js";

      export default
      StaticClass:
      match: /^//,
      module: StaticClass
      ,
      DynClass:
      match: /^//,
      module: "/src/app/DynClass.js"

      ;


      So as you can see the export is an object, with keys acting as the route id, and an object that contains the matches (regex based) and the module which should be executed by the router if the route matches. I can feed my router with both a Constructor function (or an object) for modules which are available immediatly (i.e. contained in the main chunk) or if the module value is a string, this means that the router has to load this module dynamically by using the path specified in the string.



      So as I know what modules could be potentially loaded (but not if and when) I can now parse this file within my build process and transform the route config to something webpack can understand:



      const path = require("path");
      const fs = require("fs");

      let routesSource = fs.readFileSync(path.resolve(__dirname, "app/routes.js"), "utf8");

      routesSource = routesSource.substr(routesSource.indexOf("export default"));
      routesSource = routesSource.replace(/module:s*((?!".*").)*$/gm, "module: undefined,");
      routesSource = routesSource.replace(/r?n|r/g, "").replace("export default", "var routes = ");

      eval(routesSource);

      let dummySource = Object.entries(routes).reduce((acc, [routeName, routeConfig]) =>

      if (typeof routeConfig.module === "string")
      return acc + `import(/* webpackChunkName: "$routeName" */"$routeConfig.module");`;


      return acc;

      , "") + "export default ''";


      (Yeah I know this is quite ugly and also a bit brittle so this surely could be done better)



      Essentially I create a new, virtual module where every route entry which demands a dynamic import is translated, so:



      DynClass: 
      match: /^//,
      module: "/src/app/DynClass.js"



      becomes:



      import(/* webpackChunkName: "DynClass" */"/src/app/DynClass.js");


      So the route id simply becomes the name of the chunk!



      2) including the virtual module in the build



      For this I use the virtual-module-webpack-plugin:



      plugins: [
      new VirtualModulePlugin(
      moduleName: "./app/dummy.js",
      contents: dummySource
      )
      ],


      Where dummySource is just a string containing the sourcecode of my virtual module I just have generated. Now, this module is pulled in and the "virtual imports" can be processed by webpack. But wait, I still need to import the dummy module, but I do not have any in my development mode (where I use everything natively, so no loaders).



      So in my main code I do the following:



      let isDev = false;
      /** @remove */
      isDev = true;
      /** @endremove */

      if (isDev) import('./app/dummy.js');


      Where "dummy.js" is just an empty stub module while I am in development mode. The parts between that special comments are removed while building (using the webpack-loader-clean-pragma loader), so while webpack "sees" the import for dummy.js, this code will not be executed in the build itself since then isDev evaluates to false. And since we already defined a virtual module with the same path, the virtual module is included while building just like I want, and of course all dependencies are resolved as well.



      3) Handling the actual loading



      For development, this is quite easy:



      import routes from './app/routes.js';

      Object.entries(routes).forEach(async ([routeId, route]) =>
      if (typeof route.module === "function")
      new route.module;
      else
      const result = await import(route.module);
      new result.default;

      );


      (Note that this is not the actual router code, just enough to help me with my PoC)



      Well, but for the build I need something else, so I added some code specific to the build environment:



      /** @remove */
      const result = await import(route.module);
      new result.default;
      /** @endremove */

      if (!isDev)
      if (typeof route.module === "string") await __webpack_require__.e(routeId);
      const result = __webpack_require__(route.module.replace("/src", "."));
      new result.default;



      Now, the loading code for the dev environment is just stripped out, and there is another loading code that uses webpack internally. I also check if the module value is a function or string, and if it is the latter I invoke the internal require.ensure function to load the correct chunk: await __webpack_require__.e(routeId);. Remember that I named my chunks when generating the virtual module? Now thats why I still can find them now!



      4) more needs to be done



      Another thing I encountered is when several dynamically loaded modules have the same dependencies, webpack tries to generate more chunks with names like module1~module2.bundle.js, breaking my build. To counter this, I needed to make sure that all those shared modules go into a specific named bundle I called "shared":



      optimization: 
      splitChunks:
      chunks: "all",
      name: "shared"




      And when in production mode, I simply load this chunk manually before any dynamic modules depending on it are requested:



      if (!isDev) 
      await __webpack_require__.e("shared");



      Again, this code only runs in production mode!



      Finally, I have to prevent webpack renaming my modules (and chunks) to something like "1", "2" etc, but rather keep the names I just have defined:



      optimization: 
      namedChunks: true,
      namedModules: true



      Se yeah, there you have it! As I said this wasn't pretty but seems to work, at least with my simplified test setup. I really hope there aren't any blockers ahead of me when I do all the rest (like ESLint, SCSS etc)!






      share|improve this answer

























        0












        0








        0







        So yeah, after much fiddling there seems to be a light at the end of the tunnel. Still, this is not a 100% solution and it is surely not for the faint of heart, as it is quite ugly and fragile. But still I want to share my approach with you:



        1) manual parsing of my routes config



        My router uses a config file looking like this:



        import StaticClass from "/src/app/StaticClass.js";

        export default
        StaticClass:
        match: /^//,
        module: StaticClass
        ,
        DynClass:
        match: /^//,
        module: "/src/app/DynClass.js"

        ;


        So as you can see the export is an object, with keys acting as the route id, and an object that contains the matches (regex based) and the module which should be executed by the router if the route matches. I can feed my router with both a Constructor function (or an object) for modules which are available immediatly (i.e. contained in the main chunk) or if the module value is a string, this means that the router has to load this module dynamically by using the path specified in the string.



        So as I know what modules could be potentially loaded (but not if and when) I can now parse this file within my build process and transform the route config to something webpack can understand:



        const path = require("path");
        const fs = require("fs");

        let routesSource = fs.readFileSync(path.resolve(__dirname, "app/routes.js"), "utf8");

        routesSource = routesSource.substr(routesSource.indexOf("export default"));
        routesSource = routesSource.replace(/module:s*((?!".*").)*$/gm, "module: undefined,");
        routesSource = routesSource.replace(/r?n|r/g, "").replace("export default", "var routes = ");

        eval(routesSource);

        let dummySource = Object.entries(routes).reduce((acc, [routeName, routeConfig]) =>

        if (typeof routeConfig.module === "string")
        return acc + `import(/* webpackChunkName: "$routeName" */"$routeConfig.module");`;


        return acc;

        , "") + "export default ''";


        (Yeah I know this is quite ugly and also a bit brittle so this surely could be done better)



        Essentially I create a new, virtual module where every route entry which demands a dynamic import is translated, so:



        DynClass: 
        match: /^//,
        module: "/src/app/DynClass.js"



        becomes:



        import(/* webpackChunkName: "DynClass" */"/src/app/DynClass.js");


        So the route id simply becomes the name of the chunk!



        2) including the virtual module in the build



        For this I use the virtual-module-webpack-plugin:



        plugins: [
        new VirtualModulePlugin(
        moduleName: "./app/dummy.js",
        contents: dummySource
        )
        ],


        Where dummySource is just a string containing the sourcecode of my virtual module I just have generated. Now, this module is pulled in and the "virtual imports" can be processed by webpack. But wait, I still need to import the dummy module, but I do not have any in my development mode (where I use everything natively, so no loaders).



        So in my main code I do the following:



        let isDev = false;
        /** @remove */
        isDev = true;
        /** @endremove */

        if (isDev) import('./app/dummy.js');


        Where "dummy.js" is just an empty stub module while I am in development mode. The parts between that special comments are removed while building (using the webpack-loader-clean-pragma loader), so while webpack "sees" the import for dummy.js, this code will not be executed in the build itself since then isDev evaluates to false. And since we already defined a virtual module with the same path, the virtual module is included while building just like I want, and of course all dependencies are resolved as well.



        3) Handling the actual loading



        For development, this is quite easy:



        import routes from './app/routes.js';

        Object.entries(routes).forEach(async ([routeId, route]) =>
        if (typeof route.module === "function")
        new route.module;
        else
        const result = await import(route.module);
        new result.default;

        );


        (Note that this is not the actual router code, just enough to help me with my PoC)



        Well, but for the build I need something else, so I added some code specific to the build environment:



        /** @remove */
        const result = await import(route.module);
        new result.default;
        /** @endremove */

        if (!isDev)
        if (typeof route.module === "string") await __webpack_require__.e(routeId);
        const result = __webpack_require__(route.module.replace("/src", "."));
        new result.default;



        Now, the loading code for the dev environment is just stripped out, and there is another loading code that uses webpack internally. I also check if the module value is a function or string, and if it is the latter I invoke the internal require.ensure function to load the correct chunk: await __webpack_require__.e(routeId);. Remember that I named my chunks when generating the virtual module? Now thats why I still can find them now!



        4) more needs to be done



        Another thing I encountered is when several dynamically loaded modules have the same dependencies, webpack tries to generate more chunks with names like module1~module2.bundle.js, breaking my build. To counter this, I needed to make sure that all those shared modules go into a specific named bundle I called "shared":



        optimization: 
        splitChunks:
        chunks: "all",
        name: "shared"




        And when in production mode, I simply load this chunk manually before any dynamic modules depending on it are requested:



        if (!isDev) 
        await __webpack_require__.e("shared");



        Again, this code only runs in production mode!



        Finally, I have to prevent webpack renaming my modules (and chunks) to something like "1", "2" etc, but rather keep the names I just have defined:



        optimization: 
        namedChunks: true,
        namedModules: true



        Se yeah, there you have it! As I said this wasn't pretty but seems to work, at least with my simplified test setup. I really hope there aren't any blockers ahead of me when I do all the rest (like ESLint, SCSS etc)!






        share|improve this answer













        So yeah, after much fiddling there seems to be a light at the end of the tunnel. Still, this is not a 100% solution and it is surely not for the faint of heart, as it is quite ugly and fragile. But still I want to share my approach with you:



        1) manual parsing of my routes config



        My router uses a config file looking like this:



        import StaticClass from "/src/app/StaticClass.js";

        export default
        StaticClass:
        match: /^//,
        module: StaticClass
        ,
        DynClass:
        match: /^//,
        module: "/src/app/DynClass.js"

        ;


        So as you can see the export is an object, with keys acting as the route id, and an object that contains the matches (regex based) and the module which should be executed by the router if the route matches. I can feed my router with both a Constructor function (or an object) for modules which are available immediatly (i.e. contained in the main chunk) or if the module value is a string, this means that the router has to load this module dynamically by using the path specified in the string.



        So as I know what modules could be potentially loaded (but not if and when) I can now parse this file within my build process and transform the route config to something webpack can understand:



        const path = require("path");
        const fs = require("fs");

        let routesSource = fs.readFileSync(path.resolve(__dirname, "app/routes.js"), "utf8");

        routesSource = routesSource.substr(routesSource.indexOf("export default"));
        routesSource = routesSource.replace(/module:s*((?!".*").)*$/gm, "module: undefined,");
        routesSource = routesSource.replace(/r?n|r/g, "").replace("export default", "var routes = ");

        eval(routesSource);

        let dummySource = Object.entries(routes).reduce((acc, [routeName, routeConfig]) =>

        if (typeof routeConfig.module === "string")
        return acc + `import(/* webpackChunkName: "$routeName" */"$routeConfig.module");`;


        return acc;

        , "") + "export default ''";


        (Yeah I know this is quite ugly and also a bit brittle so this surely could be done better)



        Essentially I create a new, virtual module where every route entry which demands a dynamic import is translated, so:



        DynClass: 
        match: /^//,
        module: "/src/app/DynClass.js"



        becomes:



        import(/* webpackChunkName: "DynClass" */"/src/app/DynClass.js");


        So the route id simply becomes the name of the chunk!



        2) including the virtual module in the build



        For this I use the virtual-module-webpack-plugin:



        plugins: [
        new VirtualModulePlugin(
        moduleName: "./app/dummy.js",
        contents: dummySource
        )
        ],


        Where dummySource is just a string containing the sourcecode of my virtual module I just have generated. Now, this module is pulled in and the "virtual imports" can be processed by webpack. But wait, I still need to import the dummy module, but I do not have any in my development mode (where I use everything natively, so no loaders).



        So in my main code I do the following:



        let isDev = false;
        /** @remove */
        isDev = true;
        /** @endremove */

        if (isDev) import('./app/dummy.js');


        Where "dummy.js" is just an empty stub module while I am in development mode. The parts between that special comments are removed while building (using the webpack-loader-clean-pragma loader), so while webpack "sees" the import for dummy.js, this code will not be executed in the build itself since then isDev evaluates to false. And since we already defined a virtual module with the same path, the virtual module is included while building just like I want, and of course all dependencies are resolved as well.



        3) Handling the actual loading



        For development, this is quite easy:



        import routes from './app/routes.js';

        Object.entries(routes).forEach(async ([routeId, route]) =>
        if (typeof route.module === "function")
        new route.module;
        else
        const result = await import(route.module);
        new result.default;

        );


        (Note that this is not the actual router code, just enough to help me with my PoC)



        Well, but for the build I need something else, so I added some code specific to the build environment:



        /** @remove */
        const result = await import(route.module);
        new result.default;
        /** @endremove */

        if (!isDev)
        if (typeof route.module === "string") await __webpack_require__.e(routeId);
        const result = __webpack_require__(route.module.replace("/src", "."));
        new result.default;



        Now, the loading code for the dev environment is just stripped out, and there is another loading code that uses webpack internally. I also check if the module value is a function or string, and if it is the latter I invoke the internal require.ensure function to load the correct chunk: await __webpack_require__.e(routeId);. Remember that I named my chunks when generating the virtual module? Now thats why I still can find them now!



        4) more needs to be done



        Another thing I encountered is when several dynamically loaded modules have the same dependencies, webpack tries to generate more chunks with names like module1~module2.bundle.js, breaking my build. To counter this, I needed to make sure that all those shared modules go into a specific named bundle I called "shared":



        optimization: 
        splitChunks:
        chunks: "all",
        name: "shared"




        And when in production mode, I simply load this chunk manually before any dynamic modules depending on it are requested:



        if (!isDev) 
        await __webpack_require__.e("shared");



        Again, this code only runs in production mode!



        Finally, I have to prevent webpack renaming my modules (and chunks) to something like "1", "2" etc, but rather keep the names I just have defined:



        optimization: 
        namedChunks: true,
        namedModules: true



        Se yeah, there you have it! As I said this wasn't pretty but seems to work, at least with my simplified test setup. I really hope there aren't any blockers ahead of me when I do all the rest (like ESLint, SCSS etc)!







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Mar 16 at 16:47









        frontend_devfrontend_dev

        1,1551121




        1,1551121





























            draft saved

            draft discarded
















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55052636%2fhow-to-include-manual-import-in-webpack-bundle%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            1928 у кіно

            Захаров Федір Захарович

            Ель Греко