Search code examples
javascripttypescriptwebpackwebpack-5ecmascript-2016

Import external static file in webpack bundle


How can I import an external file (config.js) from within a webpack bundle?

I have the following file-structure for local dev:

/dist/
    /index.html
    /plugin.js
/src/
    /index.ts
/config.js        # This config is only for local dev, will be different in prod
/ ...

The plugin.js will be moved to and executed by a third-party website. I want to import the config.js (which will in production be provided by the third-party website) inside my plugin.js.

I import the config.js in index.ts like this:

import config from "../config.js";

I have managed to exclude the config.js from plugin.js by specifing it in the externals-field in the webpack.config.js which is working so far:

module.exports = {
    ...
    externals: [
        "../config.js"    
    ]

}

When I open index.html however, the import statement from ../config.js is not getting an error but the config-object file is undefined.

The file structure of the third-party server in prod looks like this:

/ ... /
    /plugins/
        /other-plugin/...  # A bunch of other plugins. Each has its own folder in plugins
        /my-plugin/
            plugin.js      # This is my plugin
        /config.js         # This is the global config file of the server for all plugins

index.ts:

import config from "../config.js";
console.log(config);

config.js:

module.exports = {
   foo: "bar"
}

Solution

  • The externals implies that whatever that was exported by config.js file would be available at runtime. So for browser, that means, you have probably already injected it via script tag or for Node.js, it is already imported via globalThis or some equivalent. Your import line - import config from "../config.js"; is simply gone when the code is bundled. The external doesn't mean that it would re-import config.js when the code is being run Additionally, the general practice is to use external object configuration instead of array configuration like:

    module.exports = {
      // ...
      externals: {
        "../config.js": "AppConfig"
      }
    };
    

    This tells Webpack that whatever was exported by config.js file should be available as AppConfig object at runtime. Using array syntax is meant for code with side-effects.

    Now coming back to possible solution. The recommended option is to use environment variables for passing the environment specific values. Assuming your plugin is a library, when it is being consumed and getting bundled as part of application via Webpack, you can make use of DefinePlugin to inject these values into your code. You can then access those in your code as process.env.foo. You should not import config.js in your code anywhere.

    The second option is to use externals with object configuration as shown above. Change your config.js file to UMD or equivalent:

    // config.js file
    
    const config = {
       foo: "bar"
    };
    
    // Make it available globally
    window.AppConfig = config;
    
    // Since `module` is not available in browser.
    if (module && module.exports) {
      module.exports = config;
    }
    

    And finally, import your config.js in index.html file before your bundled script gets loaded.

    <head>
      <script src="/path/to/config.js>"></script>
      <script src="/path/to/bundle.js>"></script>
    </head>