1.1.0 • Published 9 months ago

vite-plugin-cdn-import-async v1.1.0

Weekly downloads
-
License
MIT
Repository
github
Last release
9 months ago

Vite plugin which can import modules asynchronously from CDN.

This plugin is forked from vite-plugin-cdn-import and allows you to specify modules that should be loaded in defer/async mode in addition.

Installation

npm:

npm install vite-plugin-cdn-import-async --save-dev

yarn:

yarn add  vite-plugin-cdn-import-async -D

Usage

Plugin config

Specify async or defer to mode param whthin configs of the module you want to import asynchronously from CDN:

// vite.config.js
import cdnImport from 'vite-plugin-cdn-import-async'

export default {
    plugins: [
        cdnImport({
            modules: [
                {
                    name: 'react',
                    var: 'React',
                    mode: 'async', // 'async' atrribute will be added to its <script> tag.
                    path: `https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js`,
                },
                {
                    name: 'lottie-web',
                    var: 'lottie',
                    mode: 'defer', // 'defer' atrribute will be added to its <script> tag.
                    path: `https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js`,
                },
                {
                    name: 'axios',  // Module without 'mode' param will be loaded synchronously.
                    var: 'axios',
                    path: 'https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js',
                }
            ],
        }),
    ],
}

This demo will generate codes below into the output file:

<script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
<script async onload="__cdnImportAsyncHandler('React')" onerror="__cdnImportAsyncHandler('React', true)" src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
<script defer onload="__cdnImportAsyncHandler('lottie')" onerror="__cdnImportAsyncHandler('lottie', true)" src="https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js"></script>

Input file

By default, every module that defined in plugin config will generate its <script> tag into the output file, no matter this module was used in the project or not.

But now you can set attribute data-cdn-import in <meta> tag of the input file, to determind which module should generate <script> into the output file.

For example (example/react/index.html):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" data-cdn-import="React,lottie" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Example</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

According to data-cdn-import="React,lottie", plugin will only handle React and lottie modules and generate their <script> into the output file (example/react/dist/index.html):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Example</title>
    <script>window.__cdnImportAsync_varToNameMap={"React":"react","lottie":"lottie-web"};</script>
    <script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
    <script async onload="__cdnImportAsyncHandler('React')" onerror="__cdnImportAsyncHandler('React', true)" src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
    <script defer onload="__cdnImportAsyncHandler('lottie')" onerror="__cdnImportAsyncHandler('lottie', true)" src="https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js"></script>
    <script type="module" crossorigin src="/assets/index.c9473e27.js"></script>
    <link rel="stylesheet" href="/assets/index.cd9c0392.css">
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

Mode suffix

Using suffix @async or @defer within data-cdn-import can also generate asynchronous <script> tag of marked module.

Example

Notice that here's no mode: 'async' in the configs of React module:

// vite.config.js
import cdnImport from 'vite-plugin-cdn-import-async'

export default {
    plugins: [
        cdnImport({
            modules: [
                {
                    name: 'react',
                    var: 'React',
                    path: `https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js`,
                },
                {
                    name: 'lottie-web',
                    var: 'lottie',
                    mode: 'defer', 
                    path: `https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js`,
                },
                {
                    name: 'axios',
                    var: 'axios',
                    path: 'https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js',
                }
            ],
        }),
    ],
}

And the input file (example/react/index.html) likes:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" data-cdn-import="React@async,lottie" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Example</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

According to React@async within data-cdn-import, the React module will generate <script> tag with async attribute into the output file (example/react/dist/index.html):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Example</title>
    <script>window.__cdnImportAsync_nameToVar={"react":"React","lottie-web":"lottie"};</script>
    <script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
    <script async onload="__cdnImportAsyncHandler('React')" onerror="__cdnImportAsyncHandler('React', true)" src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
    <script defer onload="__cdnImportAsyncHandler('lottie')" onerror="__cdnImportAsyncHandler('lottie', true)" src="https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js"></script>
    <script type="module" crossorigin src="/assets/index.c9473e27.js"></script>
    <link rel="stylesheet" href="/assets/index.cd9c0392.css">
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

Lazy loading

In addition to async or defer as the value of mode, here's other avalable values for lazy-loading:

Value of modeDescription
DOMContentLoadedModule will start being loaded within DOMContentLoaded event of window.
loadModule will start being loaded within load event of window.
millisecondsModule will start being loaded in specified milliseconds after load event emits.

Example

In vite.config.js:

export default defineConfig({
    plugins: [
        importToCDN({
            modules: [
                {
                    name: 'react',
                    var: 'React',
                    mode: 'DOMContentLoaded',
                    path: `https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js`,
                },
                {
                    name: 'lottie-web',
                    var: 'lottie',
                    mode: '3000',
                    path: `https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js`,
                },
                {
                    name: 'axios',
                    var: 'axios',
                    mode: 'load',
                    path: 'https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js',
                }
            ],
        }),
        reactRefresh(),
    ],
})

Or the entry file:

<meta data-cdn-import="React@DOMContentLoaded,lottie@3000,axios@load" />

The output file would be like:

    <script>window.__cdnImportAsync_nameToVar={"react":"React","lottie-web":"lottie"};</script>
    <script>function __cdnImportAsyncHandler(o,n){n&&window.cdnImportAsync_loadingErrorModules.push(o);var d=new CustomEvent("asyncmoduleloaded",{detail:{module:o,isError:!!n}});window.dispatchEvent(d)}window.cdnImportAsync_loadingErrorModules=window.cdnImportAsync_loadingErrorModules||[];</script>
    <script>function __cdnImportAsync_deferredLoader(n,r){var c=document.createElement("script");c.onload=function(){__cdnImportAsyncHandler(n)},c.onerror=function(){__cdnImportAsyncHandler(n,!0)},c.src=r,document.body.appendChild(c)}</script>
    <script>!function(){window.addEventListener("DOMContentLoaded",function e(){__cdnImportAsync_deferredLoader("React","https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"),window.removeEventListener("DOMContentLoaded",e)},!1)}();</script>
    <script>!function(){window.addEventListener("load",function e(){setTimeout(function(){__cdnImportAsync_deferredLoader("lottie","https://cdn.jsdelivr.net/npm/lottie-web@5.10.0/build/player/lottie.min.js")},3000),window.removeEventListener("load",e)},!1)}();</script>
    <script>!function(){window.addEventListener("load",function e(){__cdnImportAsync_deferredLoader("axios","https://cdn.jsdelivr.net/npm/axios@1.2.1/dist/axios.min.js"),window.removeEventListener("load",e)},!1)}();</script>
  

Import async module

Once a module is loaded asynchronously by mode config, you should create an function to handle it while using it in pages:

export function cdnAsyncImport(moduleName: string, isDev: boolean): Promise<any> {
  if (isDev) {
    return Promise.reject()
  }

  return new Promise((resolve, rejects) => {
    const errorModules = window.cdnImportAsync_loadingErrorModules || []
    const moduleVar = window.__cdnImportAsync_nameToVar[moduleName]
    if (errorModules.includes(moduleVar)) {
      rejects()
    } else if (window[moduleVar]) {
      resolve(window[moduleVar])
    } else {
      window.addEventListener(
        'asyncmoduleloaded',
        (e: any) => {
          const { detail } = e || {}
          if (detail && !detail.isError && window[moduleVar]) {
            resolve(window[moduleVar])
          } else {
            rejects()
          }
        },
        false
      )
    }
  })
}

cdnAsyncImport('lottie-web', import.meta.env.DEV).then(lottie => {
  // Async module has been successfully loaded.
  console.log(lottie)
}).catch(() => {
  // Handling DEV or Error case
  import('lottie-web').then(...)
})

Other ussages

Other basic ussages see vite-plugin-cdn-import.

1.1.0

9 months ago

1.0.12

9 months ago

1.0.11

1 year ago

1.0.10

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago