2.0.5 • Published 3 years ago

assets-dynamic-import v2.0.5

Weekly downloads
44
License
MIT
Repository
github
Last release
3 years ago

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:

FunctionTargetChildCacheDescription
importScriptbodyscriptyesImports javascript-assets and caches such imports
appendScriptbodyscriptnoCreates a script-node and appends it to the document body that initiats resourse loading
importStyleheadlinkyesImports CSS-assets and caches such imports
appendStyleheadlinknoCreates a link-node and appends it to the document head that initiats resource loading
createElementn/an/anoCreates DOMNode and assigns its properties
appendNodeAsyncanyanynoAssigns onload and onerror event lesteners of the Child and appends it to the Target
cacheAlln/an/an/aMemoization 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:

ArgumentTypeMandatoryDescription
srcstringyesurl to load javascript from
nodePropsWritableAttribures<HTMLScriptElement>noobject with attributes of <script> element
resolveCallback() => Tnocallback 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:

ArgumentTypeMandatoryDescription
srcstringyesurl to load javascript from
nodePropsWritableAttribures<HTMLScriptElement>noobject with attributes of <script> element. nodeProps.src overrides value of src
resolveCallback() => Tnocallback 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:

ArgumentTypeMandatoryDescription
hrefstringyesurl to load styles from
nodePropsWritableAttribures<HTMLLinkElement>noobject with attributes of <link> element. nodeProp.href overrides href
resolveCallback() => Tnocallback 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:

ArgumentTypeMandatoryDescription
hrefstringyesurl to load styles from
nodePropsWritableAttribures<HTMLLinkElement>noobject with attributes of <link> element
resolveCallback() => Tnocallback 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:

ArgumentTypeMandatoryDescription
tagstringyesthe html tag to create DOM node with
nodePropsWritableAttribures<HTMLElement[tag]>noobject 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:

ArgumentTypeMandatoryDescription
targetHTMLElementyesthe DOM node to append node as its child
nodeHTMLElementnothe DOM node to be appended to the target node
resolveCallback() => Tnocallback 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';
2.0.5

3 years ago

2.0.3

4 years ago

2.0.2

4 years ago

2.0.4

4 years ago

2.0.1

4 years ago

2.0.0

4 years ago

1.0.5

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.2

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago