assets-dynamic-import
    TypeScript icon, indicating that this package has built-in type declarations

    2.0.5 • Public • Published

    assets-dynamic-import

    Build Status Coverage Status npm version npm downloads GitHub license

    Simple way to dynamicly import some external assets in the runtime.

    Installation

    npm install --save assets-dynamic-import

    Usage

    import { importScript } from 'assets-dynamic-import';
    
    const processSomeData = async data => {
      const { someFnFromMyLib } = await importScript(
        '//some.path.to/script.js',
        () => global.someGlobalLibraryName
      );
    
      return someFnFromMyLib(data);
    }
    import { importScript, importStyle } from 'assets-dynamic-import';
    
    export interface Recurly {
      token(form: HTMLFormElement, cb: (err: Error | null, token: string) => void): void;
    }
    
    export default () => Promise.all([
        () => importScript('https://js.recurly.com/v4/recurly.js'),
        () => importStyle('https://js.recurly.com/v4/recurly.css'),
    ])
      .then(() => {
        const recurly = (window as any).recurly as Recurly;
        return {
          token: (form: HTMLFormElement): Promise<string> => new Promise(
            (resolve, reject) => recurly.token(form, (err: Error | null, token: string) => (
              err == null ? resolve(token) : reject(err)
            )
          )),
        };
      });

    Motivation

    The main idea of the library to provide users with minimal tool set that allows to work with external (to application bundle) assets.

    The library is developed considering following use cases:

    1. to use extrnal libraries in single-page appliction preventing excess loading of not-used of them for major of pages (aka recurly, stripe, pdf.js etc.)

    2. integrate separetelly deployed SPA's to one large single page application.

    import React from 'react';
    import { importScript } from 'assets-dynamic-import';
    import { BrowserRouter, Switch, Route } from 'react-router-dom';
    import { CDN } from './config';
    
    const ViewerApplication = React.lazy(
      () => importScript(
        `${CDN}/viewer.bundle.js`, 
        () => global.viewerLib.default,
      )
    );
    
    const MyAccountApplication = React.lazy(
      () => importScript(
        `${CDN}/myaccount.bundle.js`,
        () => global.accountLib.default,
      )
    );
    
    const App = () => (
      <BrowserRouter>
        <Switch>
          <Route path="/viewer" component={ViewerApplication} />
          <Route component={MyAccountApplication}> />
        </Switch>
      </BrowserRouter>
    );
    
    export default App;

    Specification

    The library exports the following functions:

    Function Target Child Cache Description
    importScript body script yes Imports javascript-assets and caches such imports
    appendScript body script no Creates a script-node and appends it to the document body that initiats resourse loading
    importStyle head link yes Imports CSS-assets and caches such imports
    appendStyle head link no Creates a link-node and appends it to the document head that initiats resource loading
    createElement n/a n/a no Creates DOMNode and assigns its properties
    appendNodeAsync any any no Assigns onload and onerror event lesteners of the Child and appends it to the Target
    cacheAll n/a n/a n/a Memoization function decorator

    importScript(src[, nodeProps][, resolveCallback]): Promise

    Creates <script> node assigns it with src, type attributes from nodeProps and then appends it to the document <body>.

    Function returns promise.

    importScript could be safely called several times with the same src. Each further call of importScript results with the same promise as the first call.

    Arguments:

    Argument Type Mandatory Description
    src string yes url to load javascript from
    nodeProps WritableAttribures<HTMLScriptElement> no object with attributes of <script> element
    resolveCallback () => T no callback to resolve some js interface after script is loaded

    Return value:

    • Promise<T> - that resolves with result of resolveCallback (if specified otherwise with undefined) or rejects with Error and message: 'Couldn't load script by '.

    Example 1. "Recurly":

    ./recurly.ts

    import { importScript, importStyle } from 'assets-dynamic-import';
    
    export interface Recurly {
      token(form: HTMLFormElement, cb: (err: Error | null, token: string) => void): void;
    }
    
    export default () => Promise.all([
        () => importScript('https://js.recurly.com/v4/recurly.js'),
        () => importStyle('https://js.recurly.com/v4/recurly.css'),
    ])
      .then(() => {
        const recurly = (window as any).recurly as Recurly;
        return {
          token: (form: HTMLFormElement): Promise<string> => new Promise(
            (resolve, reject) => recurly.token(form, (err: Error | null, token: string) => (
              err == null ? resolve(token) : reject(err)
            )
          )),
        };
      });

    And then we can easily import recurly where we need and call promisified token method

    import importRecurly from './recurly.ts';
    
    const getToken = (form: HTMLFormElement) => importRecurly()
      .then(
        recurly => recurly.token(form)
      )
    ;

    Example 2."Integrity and Credential policy"

        importScript(
          'https://some-domain.come/some-script.js',
          {
            integrity: 'sha256-4+XzXVhsDmqanXGHaHvgh1gMQKX40OUvDEBTu8JcmNs=',
            crossOrigin: 'anonymous'
          },
          () => global.jQuery
        ),

    appendScript(src[, nodeProps][, resolveCallback]): Promise

    Creates <script> node assigns it with src, type attributes from nodeProps and then appends it to the document <body>.

    Function returns promise.

    Each call of appendScript results in a new <script> tag appended to the body, so it initiates script loading and running each time.

    The main reason to expose this function is to allows library users to customise memoization for that in their own way tailored to their tasks.

    Actaully importScript is a momoized version of appenScript that caches its calls by src.

    Arguments:

    Argument Type Mandatory Description
    src string yes url to load javascript from
    nodeProps WritableAttribures<HTMLScriptElement> no object with attributes of <script> element. nodeProps.src overrides value of src
    resolveCallback () => T no callback to resolve some js interface after script is loaded

    Return value:

    • Promise<T> - that resolves with result of resolveCallback (if specified otherwise with undefined) or rejects with Error and message: 'Couldn't load script by '.

    Example:

    ./cached-import.ts

    import { appendScript, appendStyle } from 'assets-dynamic-import';
    
    
    global.__moduleCache = (global.__moduleCache  as Map<string, any>)
      || new Map<string, any>();
    
    const memoize = <A extends any[], R>(fn: (key: string, ...args: A) => R) 
      => (key: string, ...args: A): R => {
        if (global.__moduleCache.has(key)) return global.__moduleCache.get(key);
      
      const result = fn(key, ...args);
      global.__moduleCache.set(key, result);
    
      return result;
    }
    
    export const importScript = memoize(appendScript);
    export const importStyle = memoize(appendStyle);

    And then we can use new memoized importing function in any bundle that works in the same browser tab.

    import { importScript, importStyle } from './cached-import.ts';
    
    // ...

    importStyle(href[, nodeProps][, resolveCallback]): Promise

    Creates <link> node assigns it with href, rel, attributes from nodeProps and then appends it to the document <head>.

    Function returns promise.

    importStyle could be safely called several times with the same href. Each further call of importStyle results with the same promise as the first call.

    Arguments:

    Argument Type Mandatory Description
    href string yes url to load styles from
    nodeProps WritableAttribures<HTMLLinkElement> no object with attributes of <link> element. nodeProp.href overrides href
    resolveCallback () => T no callback to resolve some js interface after styles are loaded

    Return value:

    • Promise<T> - that resolves with result of resolveCallback (if specified otherwise with undefined) or rejects with Error and message: 'Couldn't load stylesheet by '

    How to import:

    import { importStyle } from 'assets-dynamic-import';

    appendStyle(href[, nodeProps][, resolveCallback]): Promise

    Creates <link> node assigns it with href, rel, attributes from nodeProps and then appends it to the document <head>.

    Function returns promise.

    Each call of appendStyle results in a new <link> tag appended to the head, so it initiates style loading each time.

    The main reason to expose this function is to allows library users to customise memoization for it in their own way tailored to their tasks.

    Actaully importStyle is a momoized version of appenStyle that caches its calls by href.

    Arguments:

    Argument Type Mandatory Description
    href string yes url to load styles from
    nodeProps WritableAttribures<HTMLLinkElement> no object with attributes of <link> element
    resolveCallback () => T no callback to resolve some js interface after styles are loaded

    Return value:

    • Promise<T> - that resolves with result of resolveCallback (if specified otherwise with undefined) or rejects with Error and message: 'Couldn't load stylesheet by '

    How to import:

    import { appendStyle } from 'assets-dynamic-import';

    createElement(tag, nodeProps): HTMLElement

    Creates a DOM node with tag and assigns its props, specified as nodeProps:

    Arguments:

    Argument Type Mandatory Description
    tag string yes the html tag to create DOM node with
    nodeProps WritableAttribures<HTMLElement[tag]> no object with attributes of element

    Returns

    • HTNLElement[tag] - created DOM node.

    How to import:

    import { createElement } from 'assets-dynamic-import';

    appendNode(target, node[, resolveCallback]): Promise

    Assigns loading hooks of node (onload and onerror) and appends it to the target. If node has some content to be loaded, the function returns Promise that will be resolved after the content will be loaded.

    Arguments:

    Argument Type Mandatory Description
    target HTMLElement yes the DOM node to append node as its child
    node HTMLElement no the DOM node to be appended to the target node
    resolveCallback () => T no callback to resolve some js interface after node will be appended and its content will be loaded

    Returns

    • Promise<T> - that resolves with result of resolveCallback (if specified otherwise with undefined) or rejects with Error.

    How to import:

    import { appendNodeAsync } from 'assets-dynamic-import';

    cacheAll(fn [, getKey]): Function

    type Fn<A extends any[], R> = (...args: A) => R;
    
    export function cacheAll<A extends any[], R, K>(
    	fn: Fn<A, R>, 
    	getCacheKey?: Fn<A, K>
    ): Fn<A, R> & { force: Fn<A, R>}

    How to import:

    import { cacheAll } from 'assets-dynamic-import';

    Install

    npm i assets-dynamic-import

    DownloadsWeekly Downloads

    60

    Version

    2.0.5

    License

    MIT

    Unpacked Size

    28.8 kB

    Total Files

    15

    Last publish

    Collaborators

    • dmitry.scheglov