Webpack with external modules

I know this topic has been brought up before and I’m hoping someone has found a nice way of doing what I need. I need to import a module that is NOT part of the bundle. What is the best way to achieve downloading the module and loading into the application for use? The module’s purpose is to run some custom validation against values. The module will be hosted on the webserver the application runs from. I’d rather not have to use `eval. I’m currently using webpack and the odds of that changing anytime soon are slim.

Hi,

I made a function for dynamic import some years ago: Dynamic compose - #7 by dspublic, but now I use import( /* webpackIgnore: true */ “https://component.js”) with webpack and Aurelia2

2 Likes

Thank you so much for your help. I took what you did and tweaked it to fit my needs.

import { autoinject, LogManager } from "aurelia-framework";
import { Logger } from "aurelia-logging";
import { HttpClient } from "aurelia-fetch-client";
import { v4 as uuid } from 'uuid';
import { SHA256 } from "crypto-js";

const logger: Logger = LogManager.getLogger('ModuleLoader');

@autoinject
export class ModuleLoader {
    private modules = {};

    constructor(private httpClient: HttpClient) {

    }

    async load(moduleName) {     
        const sha256 = SHA256(moduleName).toString();
       
        if (!this.modules.hasOwnProperty(sha256)) {
            logger.debug("Fetching module");          

            let response = await this.httpClient.fetch(`/${moduleName}.js`);
            let text = await response.text();
            let m = await this.loadClass(text);

            this.modules[sha256] = m;
            return m;
           
        } else {           
            logger.debug("Returning loaded module");
            return this.modules[sha256];
        }
    }

    loadClass(code) {
        return new Promise((resolve, reject) => {
            let node = document.createElement('script');
            let classId = `dyncClass${(uuid() as string).replace(/-/g, "")}`; 
            let destroy = () => {
                delete window[classId];
                node.onerror = null;
                node.onload = null;
                URL.revokeObjectURL(node.src);
                node.src = null;
                node.remove();
            };

            node.type = 'module';
            node.async = true;                
        
            node.src = URL.createObjectURL(
                new Blob(
                    [`${code}\n window.${classId}=Validation`],
                    { "type": "text/javascript" }
                )
            );       

            node.onload = (m) => {               
                resolve(window[classId]);
                destroy();
            };
            node.onerror = (m) => {
                reject(new Error("loadClass error"));
                destroy();
            };            

            document.head.appendChild(node);
        });
    }
}