UNPKG

@degjs/module-loader

Version:

Asynchronously load JavaScript modules via an HTML attribute.

85 lines (74 loc) 2.76 kB
const moduleLoader = function(options = {}) { const defaults = { moduleDataAttr: 'data-module', elToObserve: document.body, enableObservation: true, loadingMethod: 'auto', basePath: '/js/', filenameSuffix: '-bundle.js', filenameNoModuleSuffix: '-bundle-nomodule.js' }; const mutationConfig = { attributes: false, characterData: false, childList: true }; const settings = {...defaults, ...options}; const dynamicImportsSupported = supportsDynamicImports(); function init() { const elsWithModules = [...document.querySelectorAll(`[${settings.moduleDataAttr}]`)]; loadModules(elsWithModules); if (settings.enableObservation === true) { initMutationObserver(); } } function initMutationObserver() { if (window.MutationObserver) { const observer = new MutationObserver(onMutation); observer.observe(settings.elToObserve, mutationConfig); } } function onMutation(mutationsList) { mutationsList.forEach(mutation => { const moduleEls = getModuleEls(mutation); loadModules(moduleEls); }); } function getModuleEls(mutation) { const addedEls = [...mutation.addedNodes]; return addedEls.filter(el => isElement(el) && el.getAttribute(settings.moduleDataAttr)); } function loadModules(els) { const loadEsm = settings.loadingMethod === 'esm' || (settings.loadingMethod === 'auto' && dynamicImportsSupported === true); els.forEach(el => { const module = el.getAttribute(settings.moduleDataAttr); const resolvedBasepath = el.dataset.basepath || settings.basePath; const resolvedSuffix = el.dataset.suffix || settings.filenameSuffix; const resolvedNoModuleSuffix = el.dataset.noModuleSuffix || settings.filenameNoModuleSuffix; let modulePath; const props = { containerElement: el }; if (loadEsm) { modulePath = `${resolvedBasepath}${module}${resolvedSuffix}`; import(modulePath).then(mod => mod.default(props)); } else { modulePath = `${resolvedBasepath}${module}${resolvedNoModuleSuffix}`; System.import(modulePath).then(mod => mod.default(props)); } }); } function isElement(item) { return item instanceof Element; } function supportsDynamicImports() { try { new Function('import("")'); return true; } catch (err) { return false; } } init(); }; export default moduleLoader;