@wezom/dynamic-modules-import

A library for defining the modules used on the page and loading them asynchronously on demand

Usage no npm install needed!

<script type="module">
  import wezomDynamicModulesImport from 'https://cdn.skypack.dev/@wezom/dynamic-modules-import';
</script>

README

@wezom/dynamic-modules-import

Typescript first BSD-3-Clause License badge NPM package badge Test and Build

A library for defining the modules used on the page and loading them asynchronously on demand

Coverage

Statements Branches Functions Lines
Statements Branches Functions Lines

Table of Content:

  1. Usage
    1. Install npm package
    2. Import to your codebase
    3. Create
    4. Modules that are imported
  2. Create options
    1. selector
    2. modules
    3. debug
    4. pendingCssClass
    5. loadedCssClass
    6. errorCssClass
  3. API
    1. Properties
    2. Methods

Usage

Install npm package

npm i @wezom/dynamic-modules-import

Import to your codebase

By default, we distribute our lib as is - original TypeScript files, without transpiling to ES5 or ES6.

// Import original ts code
// but requires to be not exclude in `node_modules`.
// Check your `tsconfig.json`
import { create } from '@wezom/dynamic-modules-import';

You can import compiled files from special folders.

// ES6: const, let, spread, rest and other modern JavaScript features
// but requires to be not exclude in `node_modules`.
// Check your `babebl-loader` (if your use webpack as bandler)
import { create } from '@wezom/dynamic-modules-import/dist/es-6';
// or ES5: no ES6 features but ready for use as is, without transpiling
import { create } from '@wezom/dynamic-modules-import/dist/es-5';

Create

We recommend, that create and setup DMI object in a single module and then import it to your other modules for usage

// modules/dmi.ts
import { create } from '@wezom/dynamic-modules-import';

export default create({
    selector: '.my-js-selector',
    modules: {
        handleFormModule: {
            filter: 'form',
            importFn: () => import('modules/form-module')
        },
        handleSliderModule: {
            filter: '.js-slider',
            importFn: () => import('modules/slider-module')
        }
    }
});
// app.ts
import $ from 'jquery';
import DMI from 'modules/dmi';

$(() => {
    const $root = $('#root');
    DMI.importAll($root);
});

Also, you can import each module directly with your custom behavior

// modules/some-module.ts
import $ from 'jquery';
import DMI from 'modules/dmi';

export default () => {
    const $someModuleContainer = $('#some-module-container');
    const $button = $someModuleContainer.find('button');
    $button.on('click', () => {
        DMI.importModule('handleSomeModule', $someModuleContainer);
    });
};

Modules that are imported

Your dynamic modules must export default method!
Method will receive jQuery elements as first argument.
That will be elements for the current module filtered by filter prop (see "create" section)

// modules/slider-module.ts
import 'heavy-slider-from-node_modules';
export default ($elements: JQuery) => {
    $elements.slider({
        /* options */
    });
};

Create options

selector

required
type: JQuery.Selector

modules

required
type: Object<DMIModule>

Each module provided by DMIModule interface

interface DMIModule {
    filter:
        | JQuery.Selector
        | JQuery.TypeOrArray<Element>
        | JQuery
        | ((this: Element, index: number, element: Element) => boolean);
    importFn(stats: DMIModuleStats): Promise<any>;
    importCondition?(
        $elements: JQuery,
        $container: JQuery,
        stats: DMIModuleStats
    ): boolean;
}

modules[moduleName].filter

Method that has signature like jQuery.fn.filter and works in same way;

// example
const modules = {
    moduleA: {
        filter: 'form',
        // ...
    },
    moduleB: {
        filter(index) {
            return $("strong", this).length === 1;
        },
        // ...
    }
}

modules[moduleName].importFn

You own method for importing module

// example
const modules = {
    moduleA: {
        importFn: () => import('my-module'),
        // ...
    },
    moduleB: {
        importFn: async () => {
            await someGlobals();
            return import('my-dependent-module'); 
        },
        // ...
    }
}

modules[moduleName].importCondition

You own way to determinate for allowed importing

Note! DMI will not observe by any changes that can be happen in your page or app. So you need yourself re-invoke DMI if something changed and you need to react that with your importCondion

// example
const modules = {
    moduleA: {
        importCondition: () => {
            // I want to load module only if there more than 20 HTML <p> elements on current invoke
            return $('p').length > 20; 
        },
        // ...
    }
}

debug

optional
type: boolean
default: false

pendingCssClass

optional
type: string
default: '_dmi-is-pending'

loadedCssClass

optional
type: string
default: '_dmi-is-loaded'

errorCssClass

optional
type: string
default: '_dmi-has-error'


API

Properties

All props are readonly. You cannot change them after creation.

debug

type: boolean
value: depends on create option debug

selector

type: JQuery.Selector
value: depends on create option selector

pendingCssClass

type: string
value: depends on create option pendingCssClass

pendingEvent

type: string
value: "dmi:pending"

loadedCssClass

type: string
value: depends on create option loadedCssClass

loadedEvent

type: string
value: "dmi:loaded"

errorCssClass

type: string
value: depends on create option errorCssClass

errorEvent

type: string
value: "dmi:error"

Methods

importAll()

// signature
importAll(
    $container?: JQuery,
    awaitAll?: boolean,
    ignoreImportCondition?: boolean
): Promise<any>;

importModule()

// signature
importModule(
    moduleName: string,
    $container?: JQuery,
    ignoreImportCondition?: boolean
): Promise<any>;