├── .github
└── workflows
│ └── publish.yml
├── .gitignore
├── README.MD
├── index.js
└── package.json
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: publish
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 | tags:
8 | - v*
9 |
10 | jobs:
11 | publish-npm:
12 | if: startsWith(github.ref, 'refs/tags/v')
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | with:
17 | ref: master
18 |
19 | - uses: volta-cli/action@v1
20 |
21 | - name: Authorize NPM
22 | run: npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
23 |
24 | - name: Cache pnpm modules
25 | uses: actions/cache@v2
26 | with:
27 | path: ~/.pnpm-store
28 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
29 | restore-keys: |
30 | ${{ runner.os }}-
31 |
32 | - uses: pnpm/action-setup@v2.1.0
33 | with:
34 | version: 6.0.2
35 | run_install: true
36 |
37 | - run: pnpm publish
38 | env:
39 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | ## Async Script Loader
10 |
11 | [](http://standardjs.com)
12 |
13 | Allows asynchronous loading of scripts and styles in a Single Page Application (or anything else, in fact):
14 |
15 | * Using a test to ensure that the code is only loaded once
16 | * Running a callback once the script is loaded
17 | * Running a callback if the script is already loaded
18 | * Not blocking the main thread
19 |
20 | ### Reasoning
21 |
22 | Having integrated a multitude of third-party SDKs from large, well known providers, I've come to the conclusion that not having a standard interface turns the whole thing into a minefield of callbacks, timers, random library-specific loader modules, and global objects on the window, resulting in XSS risks and all sort of other undesirable behaviour. This module aims to provide a standard way of loading third-party dependencies.
23 |
24 | ### Usage
25 |
26 | You pass a list of urls to the loader, along with a method for checking that your page is ready, and a callback to call when it is.
27 |
28 | Urls can be scripts or stylesheets.
29 |
30 | ### Script Tags
31 |
32 | You can use the module like so, for a library loaded from example.com, which, when loaded, adds an attribute called PROVIDER to the global window object.
33 |
34 | ```js
35 |
52 | ```
53 |
54 | You can pass options for script tags.
55 |
56 | ```js
57 |
62 | ```
63 |
64 | #### Style Tags
65 |
66 | You can include any number of tags, including style tags.
67 |
68 | When the last one has loaded, the callback will be called.
69 |
70 | ```js
71 |
84 | ```
85 |
86 | No more tears!
87 |
88 | #### Inline scripts / Inline css
89 |
90 | You can use inline content for either type of tag by passing the configuration attribute `content` *instead* of `url`. This will write the content passed into the tag's body rather than setting it as an `href` or `src` attribute `url` will always take prescidence, so leave it out for `content` to work.
91 |
92 |
93 | ```js
94 |
105 | ```
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | function load (urls, test, callback) {
2 | let remaining = urls.length
3 |
4 | function maybeCallback () {
5 | remaining = --remaining
6 | if (remaining < 1) {
7 | callback()
8 | }
9 | }
10 |
11 | if (test()) {
12 | return callback()
13 | }
14 |
15 | for (const { type, url, content, options = { async: true, defer: true }} of urls) {
16 | const isScript = type === 'script'
17 | const tag = document.createElement(isScript ? 'script': 'link')
18 | const attribute = isScript ? 'src' : 'href'
19 | const hasUrl = Boolean(url).valueOf()
20 |
21 | if (isScript) {
22 | tag.async = options.async
23 | tag.defer = options.defer
24 | } else {
25 | tag.rel = 'stylesheet'
26 | }
27 |
28 | if (hasUrl) {
29 | tag[attribute] = url
30 | } else {
31 | tag.appendChild(
32 | document.createTextNode(content)
33 | )
34 | }
35 |
36 | tag.onload = maybeCallback
37 | document.body.appendChild(tag)
38 | }
39 | }
40 |
41 | export default load
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@beyonk/async-script-loader",
3 | "version": "2.3.0",
4 | "description": "Loads scripts cleanly and asynchronously in SPAs",
5 | "keywords": [
6 | "async",
7 | "script",
8 | "loader",
9 | "js",
10 | "client"
11 | ],
12 | "author": "Antony Jones",
13 | "license": "MIT",
14 | "type": "module",
15 | "exports": {
16 | "./package.json": "./package.json",
17 | ".": "./index.js"
18 | },
19 | "volta": {
20 | "node": "16.14.2"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------